summaryrefslogtreecommitdiffstats
path: root/tools/valgrind/drmemory_analyze.py
blob: 63dd87cce7f39314d25d79e57675f641d2c38ff3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python
# Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# drmemory_analyze.py

''' Given a ThreadSanitizer output file, parses errors and uniques them.'''

import logging
import optparse
import os
import re
import subprocess
import sys
import time

class _StackTraceLine(object):
  def __init__(self, line, address, binary):
    self.raw_line_ = line
    self.address = address
    self.binary = binary
  def __str__(self):
    return self.raw_line_

class DrMemoryAnalyze:
  ''' Given a set of Dr.Memory output files, parse all the errors out of
  them, unique them and output the results.'''

  def __init__(self, source_dir, files):
    '''Reads in a set of files.

    Args:
      source_dir: Path to top of source tree for this build
      files: A list of filenames.
    '''

    self.reports = []
    self.used_suppressions = {}
    for file in files:
      self.ParseReportFile(file)

  def ReadLine(self):
    self.line_ = self.cur_fd_.readline()
    self.stack_trace_line_ = None

  def ReadSection(self):
    FILE_PREFIXES_TO_CUT = [
        "build\\src\\",
        "chromium\\src\\",
        "crt_bld\\self_x86\\",
    ]
    BORING_CALLERS = [
        "BaseThreadInitThunk",
        "testing.*Test.*Run.*",
        "testing::internal.*",
        "MessageLoop::Run",
        "RunnableMethod.*"
    ]
    CUT_STACK_BELOW = (r"\.(exe|dll|DLL)!(" + "|".join(BORING_CALLERS) +
                       r")(\s+\[.*\]$|\s*$)")
    # TODO(timurrrr): whitespaces needed in "\s*$" due to
    # http://code.google.com/p/drmemory/issues/detail?id=584

    result = [self.line_]
    self.ReadLine()
    skip_following_frames = False
    while len(self.line_.strip()) > 0:
      cur_line = self.line_
      self.ReadLine()
      if skip_following_frames or re.search(CUT_STACK_BELOW, cur_line):
        skip_following_frames = True
        continue
      for prefix in FILE_PREFIXES_TO_CUT:
        regexp = "([^\[\]]+" + re.escape(prefix) + ").*"
        m = re.search(regexp, cur_line)
        if m:
          assert len(m.groups()) == 1
          cur_line = cur_line.replace(m.groups()[0], "")
      result.append(cur_line)
    return result

  def ParseReportFile(self, filename):
    self.cur_fd_ = open(filename, 'r')

    while True:
      self.ReadLine()
      if (self.line_ == ''):
        break
      if re.search("Grouping errors that", self.line_):
        # DrMemory has finished working.
        break
      tmp = []
      match = re.search("^Error #[0-9]+: (.*)", self.line_)
      if match:
        self.line_ = match.groups()[0].strip() + "\n"
        tmp.extend(self.ReadSection())
        self.reports.append(tmp)
      elif self.line_.startswith("ASSERT FAILURE"):
        self.reports.append(self.line_.strip())

    self.cur_fd_.close()

  def Report(self, check_sanity):
    sys.stdout.flush()
    #TODO(timurrrr): support positive tests / check_sanity==True

    if len(self.reports) > 0:
      logging.error("Found %i error reports" % len(self.reports))
      for report_list in self.reports:
        report = ''
        for line in report_list:
          report += str(line)
        logging.error('\n' + report)
      logging.error("Total: %i error reports" % len(self.reports))
      return -1
    logging.info("PASS: No error reports found")
    return 0

if __name__ == '__main__':
  '''For testing only. The DrMemoryAnalyze class should be imported instead.'''
  retcode = 0
  parser = optparse.OptionParser("usage: %prog [options] <files to analyze>")
  parser.add_option("", "--source_dir",
                    help="path to top of source tree for this build"
                    "(used to normalize source paths in baseline)")

  (options, args) = parser.parse_args()
  if len(args) == 0:
    parser.error("no filename specified")
  filenames = args

  analyzer = DrMemoryAnalyze(options.source_dir, filenames)
  retcode = analyzer.Report(False)

  sys.exit(retcode)