/[chrome]/trunk/src/tools/valgrind/tsan_analyze.py
Chromium logo

Contents of /trunk/src/tools/valgrind/tsan_analyze.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 57519 - (show annotations)
Thu Aug 26 14:30:01 2010 UTC (13 years ago) by glider@chromium.org
File MIME type: text/x-python
File size: 6204 byte(s)
tsan_analyze.py now assumes that all ThreadSanitizer reports contain VIM folds.

Review URL: http://codereview.chromium.org/3156050
1 #!/usr/bin/python
2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 # tsan_analyze.py
7
8 ''' Given a ThreadSanitizer output file, parses errors and uniques them.'''
9
10 import gdb_helper
11
12 import common
13 import logging
14 import optparse
15 import os
16 import re
17 import subprocess
18 import sys
19 import time
20
21 # Global symbol table (ugh)
22 TheAddressTable = None
23
24 class _StackTraceLine(object):
25 def __init__(self, line, address, binary):
26 self.raw_line_ = line
27 self.address = address
28 self.binary = binary
29 def __str__(self):
30 global TheAddressTable
31 file, line = TheAddressTable.GetFileLine(self.binary, self.address)
32 if (file is None) or (line is None):
33 return self.raw_line_
34 else:
35 return self.raw_line_.replace(self.binary, '%s:%s' % (file, line))
36
37 class TsanAnalyzer:
38 ''' Given a set of ThreadSanitizer output files, parse all the errors out of
39 them, unique them and output the results.'''
40
41 LOAD_LIB_RE = re.compile('--[0-9]+-- ([^(:]*) \((0x[0-9a-f]+)\)')
42 TSAN_LINE_RE = re.compile('==[0-9]+==\s*[#0-9]+\s*'
43 '([0-9A-Fa-fx]+):'
44 '(?:[^ ]* )*'
45 '([^ :\n]+)'
46 '')
47 TSAN_WARNING_LINE_RE = re.compile('==[0-9]+==\s*[#0-9]+\s*'
48 '(?:[^ ]* )*'
49 '([^ :\n]+)')
50
51 THREAD_CREATION_STR = ("INFO: T.* "
52 "(has been created by T.* at this point|is program's main thread)")
53
54 SANITY_TEST_SUPPRESSION = "ThreadSanitizer sanity test"
55 TSAN_RACE_DESCRIPTION = "Possible data race"
56 TSAN_WARNING_DESCRIPTION = ("Unlocking a non-locked lock"
57 "|accessing an invalid lock"
58 "|which did not acquire this lock")
59 def __init__(self, source_dir, use_gdb=False):
60 '''Reads in a set of files.
61
62 Args:
63 source_dir: Path to top of source tree for this build
64 '''
65
66 self._use_gdb = use_gdb
67
68 def ReadLine(self):
69 self.line_ = self.cur_fd_.readline()
70 self.stack_trace_line_ = None
71 if not self._use_gdb:
72 return
73 global TheAddressTable
74 match = TsanAnalyzer.LOAD_LIB_RE.match(self.line_)
75 if match:
76 binary, ip = match.groups()
77 TheAddressTable.AddBinaryAt(binary, ip)
78 return
79 match = TsanAnalyzer.TSAN_LINE_RE.match(self.line_)
80 if match:
81 address, binary_name = match.groups()
82 stack_trace_line = _StackTraceLine(self.line_, address, binary_name)
83 TheAddressTable.Add(stack_trace_line.binary, stack_trace_line.address)
84 self.stack_trace_line_ = stack_trace_line
85
86 def ReadSection(self):
87 result = [self.line_]
88 if re.search("{{{", self.line_):
89 while not re.search('}}}', self.line_):
90 self.ReadLine()
91 if self.stack_trace_line_ is None:
92 result.append(self.line_)
93 else:
94 result.append(self.stack_trace_line_)
95 return result
96
97 def ParseReportFile(self, filename):
98 self.cur_fd_ = open(filename, 'r')
99
100 while True:
101 # Read ThreadSanitizer reports.
102 self.ReadLine()
103 if (self.line_ == ''):
104 break
105
106 tmp = []
107 while re.search(TsanAnalyzer.THREAD_CREATION_STR, self.line_):
108 tmp.extend(self.ReadSection())
109 self.ReadLine()
110 if re.search(TsanAnalyzer.TSAN_RACE_DESCRIPTION, self.line_):
111 tmp.extend(self.ReadSection())
112 self.reports.append(tmp)
113 if (re.search(TsanAnalyzer.TSAN_WARNING_DESCRIPTION, self.line_) and
114 not common.IsWindows()): # workaround for http://crbug.com/53198
115 tmp.extend(self.ReadSection())
116 self.reports.append(tmp)
117
118 match = re.search(" used_suppression:\s+([0-9]+)\s(.*)", self.line_)
119 if match:
120 count, supp_name = match.groups()
121 count = int(count)
122 if supp_name in self.used_suppressions:
123 self.used_suppressions[supp_name] += count
124 else:
125 self.used_suppressions[supp_name] = count
126 self.cur_fd_.close()
127
128 def Report(self, files, check_sanity=False):
129 '''Reads in a set of files and prints ThreadSanitizer report.
130
131 Args:
132 files: A list of filenames.
133 check_sanity: if true, search for SANITY_TEST_SUPPRESSIONS
134 '''
135
136 global TheAddressTable
137 if self._use_gdb:
138 TheAddressTable = gdb_helper.AddressTable()
139 else:
140 TheAddressTable = None
141 self.reports = []
142 self.used_suppressions = {}
143 for file in files:
144 self.ParseReportFile(file)
145 if self._use_gdb:
146 TheAddressTable.ResolveAll()
147
148 is_sane = False
149 print "-----------------------------------------------------"
150 print "Suppressions used:"
151 print " count name"
152 for item in sorted(self.used_suppressions.items(), key=lambda (k,v): (v,k)):
153 print "%7s %s" % (item[1], item[0])
154 if item[0].startswith(TsanAnalyzer.SANITY_TEST_SUPPRESSION):
155 is_sane = True
156 print "-----------------------------------------------------"
157 sys.stdout.flush()
158
159 retcode = 0
160 if len(self.reports) > 0:
161 logging.error("FAIL! Found %i reports" % len(self.reports))
162 for report_list in self.reports:
163 report = ''
164 for line in report_list:
165 report += str(line)
166 logging.error('\n' + report)
167 retcode = -1
168
169 # Report tool's insanity even if there were errors.
170 if check_sanity and not is_sane:
171 logging.error("FAIL! Sanity check failed!")
172 retcode = -3
173
174 if retcode != 0:
175 return retcode
176 logging.info("PASS: No reports found")
177 return 0
178
179 if __name__ == '__main__':
180 '''For testing only. The TsanAnalyzer class should be imported instead.'''
181 retcode = 0
182 parser = optparse.OptionParser("usage: %prog [options] <files to analyze>")
183 parser.add_option("", "--source_dir",
184 help="path to top of source tree for this build"
185 "(used to normalize source paths in baseline)")
186
187 (options, args) = parser.parse_args()
188 if len(args) == 0:
189 parser.error("no filename specified")
190 filenames = args
191
192 analyzer = TsanAnalyzer(options.source_dir, use_gdb=True)
193 retcode = analyzer.Report(filenames)
194
195 sys.exit(retcode)

Properties

Name Value
svn:eol-style LF

Powered by ViewVC 1.1.26 ViewVC Help