Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Side by Side Diff: tools/valgrind/suppressions.py

Issue 8951011: Add presubmit checks for TSan suppressions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/valgrind/tsan/PRESUBMIT.py » ('j') | tools/valgrind/tsan/PRESUBMIT.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # suppressions.py 6 # suppressions.py
7 7
8 """Post-process Valgrind suppression matcher. 8 """Post-process Valgrind suppression matcher.
9 9
10 Suppressions are defined as follows: 10 Suppressions are defined as follows:
(...skipping 21 matching lines...) Expand all
32 32
33 class Suppression(object): 33 class Suppression(object):
34 """This class represents a single stack trace suppression. 34 """This class represents a single stack trace suppression.
35 35
36 Attributes: 36 Attributes:
37 description: A string representing the error description. 37 description: A string representing the error description.
38 type: A string representing the error type, e.g. Memcheck:Leak. 38 type: A string representing the error type, e.g. Memcheck:Leak.
39 stack: a list of "fun:" or "obj:" or ellipsis lines. 39 stack: a list of "fun:" or "obj:" or ellipsis lines.
40 """ 40 """
41 41
42 def __init__(self, description, type, stack): 42 def __init__(self, description, type, stack, defined_at):
43 """Inits Suppression. 43 """Inits Suppression.
44 44
45 Args: Same as class attributes. 45 description, type, stack: same as class attributes
46 defined_at: file:line identifying where the suppression was defined
46 """ 47 """
47 self.description = description 48 self.description = description
48 self.type = type 49 self.type = type
49 self._stack = stack 50 self._stack = stack
51 self.defined_at = defined_at
50 re_line = '{\n.*\n%s\n' % self.type 52 re_line = '{\n.*\n%s\n' % self.type
51 re_bucket = '' 53 re_bucket = ''
52 for line in stack: 54 for line in stack:
53 if line == ELLIPSIS: 55 if line == ELLIPSIS:
54 re_line += re.escape(re_bucket) 56 re_line += re.escape(re_bucket)
55 re_bucket = '' 57 re_bucket = ''
56 re_line += '(.*\n)*' 58 re_line += '(.*\n)*'
57 else: 59 else:
58 for char in line: 60 for char in line:
59 if char == '*': 61 if char == '*':
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 if not self._stack: 122 if not self._stack:
121 return False 123 return False
122 lines = [f.strip() for f in suppression_from_report] 124 lines = [f.strip() for f in suppression_from_report]
123 if self._re.match('\n'.join(lines) + '\n'): 125 if self._re.match('\n'.join(lines) + '\n'):
124 return True 126 return True
125 else: 127 else:
126 return False 128 return False
127 129
128 130
129 class SuppressionError(Exception): 131 class SuppressionError(Exception):
130 def __init__(self, filename, line, message=''): 132 def __init__(self, message, happened_at):
131 Exception.__init__(self, filename, line, message)
132 self._file = filename
133 self._line = line
134 self._message = message 133 self._message = message
134 self._happened_at = happened_at
135 135
136 def __str__(self): 136 def __str__(self):
137 return 'Error reading suppressions from "%s" (line %d): %s.' % ( 137 return 'Error reading suppressions at %s - %s' % (
138 self._file, self._line, self._message) 138 self._happened_at, self._message)
139 139
140 def ReadSuppressionsFromFile(filename): 140 def ReadSuppressionsFromFile(filename):
141 """Read suppressions from the given file and return them as a list""" 141 """Read suppressions from the given file and return them as a list"""
142 input_file = file(filename, 'r') 142 input_file = file(filename, 'r')
143 try: 143 try:
144 return ReadSuppressions(input_file, filename) 144 return ReadSuppressions(input_file, filename)
145 except SuppressionError: 145 except SuppressionError:
146 input_file.close() 146 input_file.close()
147 raise 147 raise
148 148
149 def ReadSuppressions(lines, supp_descriptor): 149 def ReadSuppressions(lines, supp_descriptor):
150 """Given a list of lines, returns a list of suppressions. 150 """Given a list of lines, returns a list of suppressions.
151 151
152 Args: 152 Args:
153 lines: a list of lines containing suppressions. 153 lines: a list of lines containing suppressions.
154 supp_descriptor: should typically be a filename. 154 supp_descriptor: should typically be a filename.
155 Used only when parsing errors happen. 155 Used only when printing errors.
156 """ 156 """
157 result = [] 157 result = []
158 cur_descr = '' 158 cur_descr = ''
159 cur_type = '' 159 cur_type = ''
160 cur_stack = [] 160 cur_stack = []
161 in_suppression = False 161 in_suppression = False
162 nline = 0 162 nline = 0
163 for line in lines: 163 for line in lines:
164 nline += 1 164 nline += 1
165 line = line.strip() 165 line = line.strip()
166 if line.startswith('#'): 166 if line.startswith('#'):
167 continue 167 continue
168 if not in_suppression: 168 if not in_suppression:
169 if not line: 169 if not line:
170 # empty lines between suppressions 170 # empty lines between suppressions
171 pass 171 pass
172 elif line.startswith('{'): 172 elif line.startswith('{'):
173 in_suppression = True 173 in_suppression = True
174 pass 174 pass
175 else: 175 else:
176 raise SuppressionError(supp_descriptor, nline, 176 raise SuppressionError('Expected: "{"',
177 'Expected: "{"') 177 "%s:%d" % (supp_descriptor, nline))
178 elif line.startswith('}'): 178 elif line.startswith('}'):
179 result.append(Suppression(cur_descr, cur_type, cur_stack)) 179 result.append(
180 Suppression(cur_descr, cur_type, cur_stack,
181 "%s:%d" % (supp_descriptor, nline)))
180 cur_descr = '' 182 cur_descr = ''
181 cur_type = '' 183 cur_type = ''
182 cur_stack = [] 184 cur_stack = []
183 in_suppression = False 185 in_suppression = False
184 elif not cur_descr: 186 elif not cur_descr:
185 cur_descr = line 187 cur_descr = line
186 continue 188 continue
187 elif not cur_type: 189 elif not cur_type:
188 if (not line.startswith("Memcheck:")) and \ 190 if (not line.startswith("Memcheck:")) and \
189 (not line.startswith("ThreadSanitizer:")) and \ 191 (not line.startswith("ThreadSanitizer:")) and \
190 (line != "Heapcheck:Leak"): 192 (line != "Heapcheck:Leak"):
191 raise SuppressionError(supp_descriptor, nline, 193 raise SuppressionError(
192 '"Memcheck:TYPE" , "ThreadSanitizer:TYPE" or "Heapcheck:Leak ' 194 'Expected "Memcheck:TYPE", "ThreadSanitizer:TYPE" '
193 'is expected, got "%s"' % line) 195 'or "Heapcheck:Leak", got "%s"' % line,
196 "%s:%d" % (supp_descriptor, nline))
194 supp_type = line.split(':')[1] 197 supp_type = line.split(':')[1]
195 if not supp_type in ["Addr1", "Addr2", "Addr4", "Addr8", 198 if not supp_type in ["Addr1", "Addr2", "Addr4", "Addr8",
196 "Cond", "Free", "Jump", "Leak", "Overlap", "Param", 199 "Cond", "Free", "Jump", "Leak", "Overlap", "Param",
197 "Value1", "Value2", "Value4", "Value8", 200 "Value1", "Value2", "Value4", "Value8",
198 "Race", "UnlockNonLocked", "InvalidLock", 201 "Race", "UnlockNonLocked", "InvalidLock",
199 "Unaddressable", "Uninitialized"]: 202 "Unaddressable", "Uninitialized"]:
200 raise SuppressionError(supp_descriptor, nline, 203 raise SuppressionError('Unknown suppression type "%s"' % supp_type,
201 'Unknown suppression type "%s"' % supp_type) 204 "%s:%d" % (supp_descriptor, nline))
202 cur_type = line 205 cur_type = line
203 continue 206 continue
204 elif re.match("^fun:.*|^obj:.*|^\.\.\.$", line): 207 elif re.match("^fun:.*|^obj:.*|^\.\.\.$", line):
205 cur_stack.append(line.strip()) 208 cur_stack.append(line.strip())
206 elif len(cur_stack) == 0 and cur_type == "Memcheck:Param": 209 elif len(cur_stack) == 0 and cur_type == "Memcheck:Param":
207 cur_stack.append(line.strip()) 210 cur_stack.append(line.strip())
208 else: 211 else:
209 raise SuppressionError(supp_descriptor, nline, 212 raise SuppressionError(
210 '"fun:function_name" or "obj:object_file" ' \ 213 '"fun:function_name" or "obj:object_file" or "..." expected',
211 'or "..." expected') 214 "%s:%d" % (supp_descriptor, nline))
212 return result 215 return result
213 216
214 217
218 def PresubmitCheck(filenames):
219 """A helper function useful in PRESUBMIT.py
220 Returns a list of errors or [].
221 """
222 ret = []
223
224 # TODO(timurrrr): warn on putting suppressions into a wrong file,
225 # e.g. TSan suppression in a memcheck file.
226
227 for f in filenames:
228 try:
229 known_supp_names = {} # Key: name, Value: suppression.
230 supps = ReadSuppressionsFromFile(f)
231 for s in supps:
232 if re.search("<.*suppression.name.here>", s.description):
233 # Suppression name line is
234 # <insert_a_suppression_name_here> for Memcheck,
235 # <Put your suppression name here> for TSan,
236 # name=<insert_a_suppression_name_here> for DrMemory
237 ret.append(
238 SuppressionError(
239 "You've forgotten to put a suppression name like bug_XXX",
240 s.defined_at))
241 continue
242
243 if s.description in known_supp_names:
244 ret.append(
245 SuppressionError(
246 'Suppression named "%s" is defined more than once, '
247 'see %s' % (s.description,
248 known_supp_names[s.description].defined_at),
Alexander Potapenko 2011/12/15 13:07:23 pls fix the indentation
Timur Iskhodzhanov 2011/12/15 13:17:27 Done.
249 s.defined_at))
250 else:
251 known_supp_names[s.description] = s
252
253 except SuppressionError as e:
254 ret.append(e)
255 return ret
256
257
215 def TestStack(stack, positive, negative): 258 def TestStack(stack, positive, negative):
216 """A helper function for SelfTest() that checks a single stack. 259 """A helper function for SelfTest() that checks a single stack.
217 260
218 Args: 261 Args:
219 stack: the stack to match the suppressions. 262 stack: the stack to match the suppressions.
220 positive: the list of suppressions that must match the given stack. 263 positive: the list of suppressions that must match the given stack.
221 negative: the list of suppressions that should not match. 264 negative: the list of suppressions that should not match.
222 """ 265 """
223 for supp in positive: 266 for supp in positive:
224 assert ReadSuppressions(supp.split("\n"), "")[0].Match(stack), \ 267 parsed = ReadSuppressions(supp.split("\n"), "positive_suppression")
268 assert parsed[0].Match(stack), \
225 "Suppression:\n%s\ndidn't match stack:\n%s" % (supp, stack) 269 "Suppression:\n%s\ndidn't match stack:\n%s" % (supp, stack)
226 for supp in negative: 270 for supp in negative:
227 assert not ReadSuppressions(supp.split("\n"), "")[0].Match(stack), \ 271 parsed = ReadSuppressions(supp.split("\n"), "negative_suppression")
228 "Suppression:\n%s\ndid match stack:\n%s" % (supp, stack) 272 assert not parsed[0].Match(stack), \
273 "Suppression:\n%s\ndid match stack:\n%s" % (supp, stack)
Alexander Potapenko 2011/12/15 13:07:23 This should be under "not"
Timur Iskhodzhanov 2011/12/15 13:17:27 Done.
229 274
230 def SelfTest(): 275 def SelfTest():
231 """Tests the Suppression.Match() capabilities.""" 276 """Tests the Suppression.Match() capabilities."""
232 277
233 test_memcheck_stack_1 = """{ 278 test_memcheck_stack_1 = """{
234 test 279 test
235 Memcheck:Leak 280 Memcheck:Leak
236 fun:absolutly 281 fun:absolutly
237 fun:brilliant 282 fun:brilliant
238 obj:condition 283 obj:condition
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 positive_memcheck_suppressions_4, 447 positive_memcheck_suppressions_4,
403 negative_memcheck_suppressions_4) 448 negative_memcheck_suppressions_4)
404 TestStack(test_heapcheck_stack, positive_heapcheck_suppressions, 449 TestStack(test_heapcheck_stack, positive_heapcheck_suppressions,
405 negative_heapcheck_suppressions) 450 negative_heapcheck_suppressions)
406 TestStack(test_tsan_stack, positive_tsan_suppressions, 451 TestStack(test_tsan_stack, positive_tsan_suppressions,
407 negative_tsan_suppressions) 452 negative_tsan_suppressions)
408 453
409 if __name__ == '__main__': 454 if __name__ == '__main__':
410 SelfTest() 455 SelfTest()
411 print 'PASS' 456 print 'PASS'
OLDNEW
« no previous file with comments | « no previous file | tools/valgrind/tsan/PRESUBMIT.py » ('j') | tools/valgrind/tsan/PRESUBMIT.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698