Fix bug in load time stats.
[chromium-blink-merge.git] / tools / valgrind / suppressions.py
blobead499be509fcdde7022989b734cb4d1313d6934
1 #!/usr/bin/env python
2 # Copyright (c) 2012 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.
6 # suppressions.py
8 """Post-process Valgrind suppression matcher.
10 Suppressions are defined as follows:
12 # optional one-line comments anywhere in the suppressions file.
14 <Short description of the error>
15 Toolname:Errortype
16 fun:function_name
17 obj:object_filename
18 fun:wildcarded_fun*_name
19 # an ellipsis wildcards zero or more functions in a stack.
20 ...
21 fun:some_other_function_name
24 If ran from the command line, suppressions.py does a self-test
25 of the Suppression class.
26 """
28 import os
29 import re
30 import sys
32 ELLIPSIS = '...'
35 def GlobToRegex(glob_pattern, ignore_case=False):
36 """Translate glob wildcards (*?) into regex syntax. Escape the rest."""
37 regex = ''
38 for char in glob_pattern:
39 if char == '*':
40 regex += '.*'
41 elif char == '?':
42 regex += '.'
43 elif ignore_case and char.isalpha():
44 regex += '[%s%s]' % (char.lower(), char.upper())
45 else:
46 regex += re.escape(char)
47 return ''.join(regex)
50 def StripAndSkipCommentsIterator(lines):
51 """Generator of (line_no, line) pairs that strips comments and whitespace."""
52 for (line_no, line) in enumerate(lines):
53 line = line.strip() # Drop \n
54 if line.startswith('#'):
55 continue # Comments
56 # Skip comment lines, but not empty lines, they indicate the end of a
57 # suppression. Add one to the line number as well, since most editors use
58 # 1-based numberings, and enumerate is 0-based.
59 yield (line_no + 1, line)
62 class Suppression(object):
63 """This class represents a single stack trace suppression.
65 Attributes:
66 description: A string representing the error description.
67 type: A string representing the error type, e.g. Memcheck:Leak.
68 stack: The lines comprising the stack trace for the suppression.
69 regex: The actual regex used to match against scraped reports.
70 """
72 def __init__(self, description, type, stack, defined_at, regex):
73 """Inits Suppression.
75 description, type, stack, regex: same as class attributes
76 defined_at: file:line identifying where the suppression was defined
77 """
78 self.description = description
79 self.type = type
80 self.stack = stack
81 self.defined_at = defined_at
82 self.regex = re.compile(regex, re.MULTILINE)
84 def Match(self, suppression_from_report):
85 """Returns bool indicating whether this suppression matches
86 the suppression generated from Valgrind error report.
88 We match our suppressions against generated suppressions
89 (not against reports) since they have the same format
90 while the reports are taken from XML, contain filenames,
91 they are demangled, and are generally more difficult to
92 parse.
94 Args:
95 suppression_from_report: list of strings (function names).
96 Returns:
97 True if the suppression is not empty and matches the report.
98 """
99 if not self.stack:
100 return False
101 lines = [f.strip() for f in suppression_from_report]
102 return self.regex.match('\n'.join(lines) + '\n') is not None
105 def FilenameToTool(filename):
106 """Return the name of the tool that a file is related to, or None.
108 Example mappings:
109 tools/heapcheck/suppressions.txt -> heapcheck
110 tools/valgrind/tsan/suppressions.txt -> tsan
111 tools/valgrind/drmemory/suppressions.txt -> drmemory
112 tools/valgrind/drmemory/suppressions_full.txt -> drmemory
113 tools/valgrind/memcheck/suppressions.txt -> memcheck
114 tools/valgrind/memcheck/suppressions_mac.txt -> memcheck
116 filename = os.path.abspath(filename)
117 parts = filename.split(os.sep)
118 tool = parts[-2]
119 if tool in ('heapcheck', 'drmemory', 'memcheck', 'tsan'):
120 return tool
121 return None
124 def ReadSuppressionsFromFile(filename):
125 """Read suppressions from the given file and return them as a list"""
126 tool_to_parser = {
127 "drmemory": ReadDrMemorySuppressions,
128 "memcheck": ReadValgrindStyleSuppressions,
129 "tsan": ReadValgrindStyleSuppressions,
130 "heapcheck": ReadValgrindStyleSuppressions,
132 tool = FilenameToTool(filename)
133 assert tool in tool_to_parser, (
134 "unknown tool %s for filename %s" % (tool, filename))
135 parse_func = tool_to_parser[tool]
137 input_file = file(filename, 'r')
138 try:
139 return parse_func(input_file, filename)
140 except SuppressionError:
141 input_file.close()
142 raise
145 class ValgrindStyleSuppression(Suppression):
146 """A suppression using the Valgrind syntax.
148 Most tools, even ones that are not Valgrind-based, use this syntax, ie
149 Heapcheck, TSan, etc.
151 Attributes:
152 Same as Suppression.
155 def __init__(self, description, type, stack, defined_at):
156 """Creates a suppression using the Memcheck, TSan, and Heapcheck syntax."""
157 regex = '{\n.*\n%s\n' % type
158 for line in stack:
159 if line == ELLIPSIS:
160 regex += '(.*\n)*'
161 else:
162 regex += GlobToRegex(line)
163 regex += '\n'
164 regex += '(.*\n)*'
165 regex += '}'
167 # In the recent version of valgrind-variant we've switched
168 # from memcheck's default Addr[1248]/Value[1248]/Cond suppression types
169 # to simply Unaddressable/Uninitialized.
170 # The suppression generator no longer gives us "old" types thus
171 # for the "new-type" suppressions:
172 # * Memcheck:Unaddressable should also match Addr* reports,
173 # * Memcheck:Uninitialized should also match Cond and Value reports,
175 # We also want to support legacy suppressions (e.g. copied from
176 # upstream bugs etc), so:
177 # * Memcheck:Addr[1248] suppressions should match Unaddressable reports,
178 # * Memcheck:Cond and Memcheck:Value[1248] should match Uninitialized.
179 # Please note the latest two rules only apply to the
180 # tools/valgrind/waterfall.sh suppression matcher and the real
181 # valgrind-variant Memcheck will not suppress
182 # e.g. Addr1 printed as Unaddressable with Addr4 suppression.
183 # Be careful to check the access size while copying legacy suppressions!
184 for sz in [1, 2, 4, 8]:
185 regex = regex.replace("\nMemcheck:Addr%d\n" % sz,
186 "\nMemcheck:(Addr%d|Unaddressable)\n" % sz)
187 regex = regex.replace("\nMemcheck:Value%d\n" % sz,
188 "\nMemcheck:(Value%d|Uninitialized)\n" % sz)
189 regex = regex.replace("\nMemcheck:Cond\n",
190 "\nMemcheck:(Cond|Uninitialized)\n")
191 regex = regex.replace("\nMemcheck:Unaddressable\n",
192 "\nMemcheck:(Addr.|Unaddressable)\n")
193 regex = regex.replace("\nMemcheck:Uninitialized\n",
194 "\nMemcheck:(Cond|Value.|Uninitialized)\n")
196 return super(ValgrindStyleSuppression, self).__init__(
197 description, type, stack, defined_at, regex)
199 def __str__(self):
200 """Stringify."""
201 lines = [self.description, self.type] + self.stack
202 return "{\n %s\n}\n" % "\n ".join(lines)
205 class SuppressionError(Exception):
206 def __init__(self, message, happened_at):
207 self._message = message
208 self._happened_at = happened_at
210 def __str__(self):
211 return 'Error reading suppressions at %s!\n%s' % (
212 self._happened_at, self._message)
215 def ReadValgrindStyleSuppressions(lines, supp_descriptor):
216 """Given a list of lines, returns a list of suppressions.
218 Args:
219 lines: a list of lines containing suppressions.
220 supp_descriptor: should typically be a filename.
221 Used only when printing errors.
223 result = []
224 cur_descr = ''
225 cur_type = ''
226 cur_stack = []
227 in_suppression = False
228 nline = 0
229 for line in lines:
230 nline += 1
231 line = line.strip()
232 if line.startswith('#'):
233 continue
234 if not in_suppression:
235 if not line:
236 # empty lines between suppressions
237 pass
238 elif line.startswith('{'):
239 in_suppression = True
240 pass
241 else:
242 raise SuppressionError('Expected: "{"',
243 "%s:%d" % (supp_descriptor, nline))
244 elif line.startswith('}'):
245 result.append(
246 ValgrindStyleSuppression(cur_descr, cur_type, cur_stack,
247 "%s:%d" % (supp_descriptor, nline)))
248 cur_descr = ''
249 cur_type = ''
250 cur_stack = []
251 in_suppression = False
252 elif not cur_descr:
253 cur_descr = line
254 continue
255 elif not cur_type:
256 if (not line.startswith("Memcheck:") and
257 not line.startswith("ThreadSanitizer:") and
258 (line != "Heapcheck:Leak")):
259 raise SuppressionError(
260 'Expected "Memcheck:TYPE", "ThreadSanitizer:TYPE" '
261 'or "Heapcheck:Leak", got "%s"' % line,
262 "%s:%d" % (supp_descriptor, nline))
263 supp_type = line.split(':')[1]
264 if not supp_type in ["Addr1", "Addr2", "Addr4", "Addr8",
265 "Cond", "Free", "Jump", "Leak", "Overlap", "Param",
266 "Value1", "Value2", "Value4", "Value8",
267 "Race", "UnlockNonLocked", "InvalidLock",
268 "Unaddressable", "Uninitialized"]:
269 raise SuppressionError('Unknown suppression type "%s"' % supp_type,
270 "%s:%d" % (supp_descriptor, nline))
271 cur_type = line
272 continue
273 elif re.match("^fun:.*|^obj:.*|^\.\.\.$", line):
274 cur_stack.append(line.strip())
275 elif len(cur_stack) == 0 and cur_type == "Memcheck:Param":
276 cur_stack.append(line.strip())
277 else:
278 raise SuppressionError(
279 '"fun:function_name" or "obj:object_file" or "..." expected',
280 "%s:%d" % (supp_descriptor, nline))
281 return result
284 def PresubmitCheckSuppressions(supps):
285 """Check a list of suppressions and return a list of SuppressionErrors.
287 Mostly useful for separating the checking logic from the Presubmit API for
288 testing.
290 known_supp_names = {} # Key: name, Value: suppression.
291 errors = []
292 for s in supps:
293 if re.search("<.*suppression.name.here>", s.description):
294 # Suppression name line is
295 # <insert_a_suppression_name_here> for Memcheck,
296 # <Put your suppression name here> for TSan,
297 # name=<insert_a_suppression_name_here> for DrMemory
298 errors.append(
299 SuppressionError(
300 "You've forgotten to put a suppression name like bug_XXX",
301 s.defined_at))
302 continue
304 if s.description in known_supp_names:
305 errors.append(
306 SuppressionError(
307 'Suppression named "%s" is defined more than once, '
308 'see %s' % (s.description,
309 known_supp_names[s.description].defined_at),
310 s.defined_at))
311 else:
312 known_supp_names[s.description] = s
313 return errors
316 def PresubmitCheck(input_api, output_api):
317 """A helper function useful in PRESUBMIT.py
318 Returns a list of errors or [].
320 sup_regex = re.compile('suppressions.*\.txt$')
321 filenames = [f.AbsoluteLocalPath() for f in input_api.AffectedFiles()
322 if sup_regex.search(f.LocalPath())]
324 errors = []
326 # TODO(timurrrr): warn on putting suppressions into a wrong file,
327 # e.g. TSan suppression in a memcheck file.
329 for f in filenames:
330 try:
331 supps = ReadSuppressionsFromFile(f)
332 errors.extend(PresubmitCheckSuppressions(supps))
333 except SuppressionError as e:
334 errors.append(e)
336 return [output_api.PresubmitError(str(e)) for e in errors]
339 class DrMemorySuppression(Suppression):
340 """A suppression using the DrMemory syntax.
342 Attributes:
343 instr: The instruction to match.
344 Rest inherited from Suppression.
347 def __init__(self, name, report_type, instr, stack, defined_at):
348 """Constructor."""
349 self.instr = instr
351 # Construct the regex.
352 regex = '{\n'
353 if report_type == 'LEAK':
354 regex += '(POSSIBLE )?LEAK'
355 else:
356 regex += report_type
357 regex += '\nname=.*\n'
359 # TODO(rnk): Implement http://crbug.com/107416#c5 .
360 # drmemory_analyze.py doesn't generate suppressions with an instruction in
361 # them, so these suppressions will always fail to match. We should override
362 # Match to fetch the instruction from the report and try to match against
363 # that.
364 if instr:
365 regex += 'instruction=%s\n' % GlobToRegex(instr)
367 for line in stack:
368 if line == ELLIPSIS:
369 regex += '(.*\n)*'
370 elif '!' in line:
371 (mod, func) = line.split('!')
372 if func == ELLIPSIS: # mod!ellipsis frame
373 regex += '(%s\!.*\n)+' % GlobToRegex(mod, ignore_case=True)
374 else: # mod!func frame
375 # Ignore case for the module match, but not the function match.
376 regex += '%s\!%s\n' % (GlobToRegex(mod, ignore_case=True),
377 GlobToRegex(func, ignore_case=False))
378 else:
379 regex += GlobToRegex(line)
380 regex += '\n'
381 regex += '(.*\n)*' # Match anything left in the stack.
382 regex += '}'
383 return super(DrMemorySuppression, self).__init__(name, report_type, stack,
384 defined_at, regex)
386 def __str__(self):
387 """Stringify."""
388 text = self.type + "\n"
389 if self.description:
390 text += "name=%s\n" % self.description
391 if self.instr:
392 text += "instruction=%s\n" % self.instr
393 text += "\n".join(self.stack)
394 text += "\n"
395 return text
398 # Possible DrMemory error report types. Keep consistent with suppress_name
399 # array in drmemory/drmemory/report.c.
400 DRMEMORY_ERROR_TYPES = [
401 'UNADDRESSABLE ACCESS',
402 'UNINITIALIZED READ',
403 'INVALID HEAP ARGUMENT',
404 'LEAK',
405 'POSSIBLE LEAK',
406 'WARNING',
410 # Regexes to match valid drmemory frames.
411 DRMEMORY_FRAME_PATTERNS = [
412 re.compile(r"^.*\!.*$"), # mod!func
413 re.compile(r"^.*!\.\.\.$"), # mod!ellipsis
414 re.compile(r"^\<.*\+0x.*\>$"), # <mod+0xoffs>
415 re.compile(r"^\<not in a module\>$"),
416 re.compile(r"^system call .*$"),
417 re.compile(r"^\*$"), # wildcard
418 re.compile(r"^\.\.\.$"), # ellipsis
422 def ReadDrMemorySuppressions(lines, supp_descriptor):
423 """Given a list of lines, returns a list of DrMemory suppressions.
425 Args:
426 lines: a list of lines containing suppressions.
427 supp_descriptor: should typically be a filename.
428 Used only when parsing errors happen.
430 lines = StripAndSkipCommentsIterator(lines)
431 suppressions = []
432 for (line_no, line) in lines:
433 if not line:
434 continue
435 if line not in DRMEMORY_ERROR_TYPES:
436 raise SuppressionError('Expected a DrMemory error type, '
437 'found %r instead\n Valid error types: %s' %
438 (line, ' '.join(DRMEMORY_ERROR_TYPES)),
439 "%s:%d" % (supp_descriptor, line_no))
441 # Suppression starts here.
442 report_type = line
443 name = ''
444 instr = None
445 stack = []
446 defined_at = "%s:%d" % (supp_descriptor, line_no)
447 found_stack = False
448 for (line_no, line) in lines:
449 if not found_stack and line.startswith('name='):
450 name = line.replace('name=', '')
451 elif not found_stack and line.startswith('instruction='):
452 instr = line.replace('instruction=', '')
453 else:
454 # Unrecognized prefix indicates start of stack trace.
455 found_stack = True
456 if not line:
457 # Blank line means end of suppression.
458 break
459 if not any([regex.match(line) for regex in DRMEMORY_FRAME_PATTERNS]):
460 raise SuppressionError(
461 ('Unexpected stack frame pattern at line %d\n' +
462 'Frames should be one of the following:\n' +
463 ' module!function\n' +
464 ' module!...\n' +
465 ' <module+0xhexoffset>\n' +
466 ' <not in a module>\n' +
467 ' system call Name\n' +
468 ' *\n' +
469 ' ...\n') % line_no, defined_at)
470 stack.append(line)
472 if len(stack) == 0: # In case we hit EOF or blank without any stack frames.
473 raise SuppressionError('Suppression "%s" has no stack frames, ends at %d'
474 % (name, line_no), defined_at)
475 if stack[-1] == ELLIPSIS:
476 raise SuppressionError('Suppression "%s" ends in an ellipsis on line %d' %
477 (name, line_no), defined_at)
479 suppressions.append(
480 DrMemorySuppression(name, report_type, instr, stack, defined_at))
482 return suppressions
485 def ParseSuppressionOfType(lines, supp_descriptor, def_line_no, report_type):
486 """Parse the suppression starting on this line.
488 Suppressions start with a type, have an optional name and instruction, and a
489 stack trace that ends in a blank line.
494 def TestStack(stack, positive, negative, suppression_parser=None):
495 """A helper function for SelfTest() that checks a single stack.
497 Args:
498 stack: the stack to match the suppressions.
499 positive: the list of suppressions that must match the given stack.
500 negative: the list of suppressions that should not match.
501 suppression_parser: optional arg for the suppression parser, default is
502 ReadValgrindStyleSuppressions.
504 if not suppression_parser:
505 suppression_parser = ReadValgrindStyleSuppressions
506 for supp in positive:
507 parsed = suppression_parser(supp.split("\n"), "positive_suppression")
508 assert parsed[0].Match(stack.split("\n")), (
509 "Suppression:\n%s\ndidn't match stack:\n%s" % (supp, stack))
510 for supp in negative:
511 parsed = suppression_parser(supp.split("\n"), "negative_suppression")
512 assert not parsed[0].Match(stack.split("\n")), (
513 "Suppression:\n%s\ndid match stack:\n%s" % (supp, stack))
516 def TestFailPresubmit(supp_text, error_text, suppression_parser=None):
517 """A helper function for SelfTest() that verifies a presubmit check fires.
519 Args:
520 supp_text: suppression text to parse.
521 error_text: text of the presubmit error we expect to find.
522 suppression_parser: optional arg for the suppression parser, default is
523 ReadValgrindStyleSuppressions.
525 if not suppression_parser:
526 suppression_parser = ReadValgrindStyleSuppressions
527 try:
528 supps = suppression_parser(supp_text.split("\n"), "<presubmit suppression>")
529 except SuppressionError, e:
530 # If parsing raised an exception, match the error text here.
531 assert error_text in str(e), (
532 "presubmit text %r not in SuppressionError:\n%r" %
533 (error_text, str(e)))
534 else:
535 # Otherwise, run the presubmit checks over the supps. We expect a single
536 # error that has text matching error_text.
537 errors = PresubmitCheckSuppressions(supps)
538 assert len(errors) == 1, (
539 "expected exactly one presubmit error, got:\n%s" % errors)
540 assert error_text in str(errors[0]), (
541 "presubmit text %r not in SuppressionError:\n%r" %
542 (error_text, str(errors[0])))
545 def SelfTest():
546 """Tests the Suppression.Match() capabilities."""
548 test_memcheck_stack_1 = """{
549 test
550 Memcheck:Leak
551 fun:absolutly
552 fun:brilliant
553 obj:condition
554 fun:detection
555 fun:expression
556 }"""
558 test_memcheck_stack_2 = """{
559 test
560 Memcheck:Uninitialized
561 fun:absolutly
562 fun:brilliant
563 obj:condition
564 fun:detection
565 fun:expression
566 }"""
568 test_memcheck_stack_3 = """{
569 test
570 Memcheck:Unaddressable
571 fun:absolutly
572 fun:brilliant
573 obj:condition
574 fun:detection
575 fun:expression
576 }"""
578 test_memcheck_stack_4 = """{
579 test
580 Memcheck:Addr4
581 fun:absolutly
582 fun:brilliant
583 obj:condition
584 fun:detection
585 fun:expression
586 }"""
588 test_heapcheck_stack = """{
589 test
590 Heapcheck:Leak
591 fun:absolutly
592 fun:brilliant
593 obj:condition
594 fun:detection
595 fun:expression
596 }"""
598 test_tsan_stack = """{
599 test
600 ThreadSanitizer:Race
601 fun:absolutly
602 fun:brilliant
603 obj:condition
604 fun:detection
605 fun:expression
606 }"""
609 positive_memcheck_suppressions_1 = [
610 "{\nzzz\nMemcheck:Leak\nfun:absolutly\n}",
611 "{\nzzz\nMemcheck:Leak\nfun:ab*ly\n}",
612 "{\nzzz\nMemcheck:Leak\nfun:absolutly\nfun:brilliant\n}",
613 "{\nzzz\nMemcheck:Leak\n...\nfun:brilliant\n}",
614 "{\nzzz\nMemcheck:Leak\n...\nfun:detection\n}",
615 "{\nzzz\nMemcheck:Leak\nfun:absolutly\n...\nfun:detection\n}",
616 "{\nzzz\nMemcheck:Leak\nfun:ab*ly\n...\nfun:detection\n}",
617 "{\nzzz\nMemcheck:Leak\n...\nobj:condition\n}",
618 "{\nzzz\nMemcheck:Leak\n...\nobj:condition\nfun:detection\n}",
619 "{\nzzz\nMemcheck:Leak\n...\nfun:brilliant\nobj:condition\n}",
622 positive_memcheck_suppressions_2 = [
623 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\n}",
624 "{\nzzz\nMemcheck:Uninitialized\nfun:ab*ly\n}",
625 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\nfun:brilliant\n}",
626 # Legacy suppression types
627 "{\nzzz\nMemcheck:Value1\n...\nfun:brilliant\n}",
628 "{\nzzz\nMemcheck:Cond\n...\nfun:detection\n}",
629 "{\nzzz\nMemcheck:Value8\nfun:absolutly\nfun:brilliant\n}",
632 positive_memcheck_suppressions_3 = [
633 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\n}",
634 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\nfun:brilliant\n}",
635 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\nfun:brilliant\n}",
636 # Legacy suppression types
637 "{\nzzz\nMemcheck:Addr1\n...\nfun:brilliant\n}",
638 "{\nzzz\nMemcheck:Addr8\n...\nfun:detection\n}",
641 positive_memcheck_suppressions_4 = [
642 "{\nzzz\nMemcheck:Addr4\nfun:absolutly\n}",
643 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\n}",
644 "{\nzzz\nMemcheck:Addr4\nfun:absolutly\nfun:brilliant\n}",
645 "{\nzzz\nMemcheck:Unaddressable\n...\nfun:brilliant\n}",
646 "{\nzzz\nMemcheck:Addr4\n...\nfun:detection\n}",
649 positive_heapcheck_suppressions = [
650 "{\nzzz\nHeapcheck:Leak\n...\nobj:condition\n}",
651 "{\nzzz\nHeapcheck:Leak\nfun:absolutly\n}",
654 positive_tsan_suppressions = [
655 "{\nzzz\nThreadSanitizer:Race\n...\nobj:condition\n}",
656 "{\nzzz\nThreadSanitizer:Race\nfun:absolutly\n}",
659 negative_memcheck_suppressions_1 = [
660 "{\nzzz\nMemcheck:Leak\nfun:abnormal\n}",
661 "{\nzzz\nMemcheck:Leak\nfun:ab*liant\n}",
662 "{\nzzz\nMemcheck:Leak\nfun:brilliant\n}",
663 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
664 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
667 negative_memcheck_suppressions_2 = [
668 "{\nzzz\nMemcheck:Cond\nfun:abnormal\n}",
669 "{\nzzz\nMemcheck:Value2\nfun:abnormal\n}",
670 "{\nzzz\nMemcheck:Uninitialized\nfun:ab*liant\n}",
671 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
672 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
673 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
674 "{\nzzz\nMemcheck:Unaddressable\nfun:brilliant\n}",
677 negative_memcheck_suppressions_3 = [
678 "{\nzzz\nMemcheck:Addr1\nfun:abnormal\n}",
679 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\n}",
680 "{\nzzz\nMemcheck:Addr2\nfun:ab*liant\n}",
681 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
682 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
683 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
686 negative_memcheck_suppressions_4 = [
687 "{\nzzz\nMemcheck:Addr1\nfun:abnormal\n}",
688 "{\nzzz\nMemcheck:Addr4\nfun:abnormal\n}",
689 "{\nzzz\nMemcheck:Unaddressable\nfun:abnormal\n}",
690 "{\nzzz\nMemcheck:Addr1\nfun:absolutly\n}",
691 "{\nzzz\nMemcheck:Addr2\nfun:ab*liant\n}",
692 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
693 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
694 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
697 negative_heapcheck_suppressions = [
698 "{\nzzz\nMemcheck:Leak\nfun:absolutly\n}",
699 "{\nzzz\nHeapcheck:Leak\nfun:brilliant\n}",
702 negative_tsan_suppressions = [
703 "{\nzzz\nThreadSanitizer:Leak\nfun:absolutly\n}",
704 "{\nzzz\nThreadSanitizer:Race\nfun:brilliant\n}",
707 TestStack(test_memcheck_stack_1,
708 positive_memcheck_suppressions_1,
709 negative_memcheck_suppressions_1)
710 TestStack(test_memcheck_stack_2,
711 positive_memcheck_suppressions_2,
712 negative_memcheck_suppressions_2)
713 TestStack(test_memcheck_stack_3,
714 positive_memcheck_suppressions_3,
715 negative_memcheck_suppressions_3)
716 TestStack(test_memcheck_stack_4,
717 positive_memcheck_suppressions_4,
718 negative_memcheck_suppressions_4)
719 TestStack(test_heapcheck_stack, positive_heapcheck_suppressions,
720 negative_heapcheck_suppressions)
721 TestStack(test_tsan_stack, positive_tsan_suppressions,
722 negative_tsan_suppressions)
724 # TODO(timurrrr): add TestFailPresubmit tests.
726 ### DrMemory self tests.
728 # http://crbug.com/96010 suppression.
729 stack_96010 = """{
730 UNADDRESSABLE ACCESS
731 name=<insert_a_suppression_name_here>
732 *!TestingProfile::FinishInit
733 *!TestingProfile::TestingProfile
734 *!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody
735 *!testing::Test::Run
736 }"""
738 suppress_96010 = [
739 "UNADDRESSABLE ACCESS\nname=zzz\n...\n*!testing::Test::Run\n",
740 ("UNADDRESSABLE ACCESS\nname=zzz\n...\n" +
741 "*!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody\n"),
742 "UNADDRESSABLE ACCESS\nname=zzz\n...\n*!BrowserAboutHandlerTest*\n",
743 "UNADDRESSABLE ACCESS\nname=zzz\n*!TestingProfile::FinishInit\n",
744 # No name should be needed
745 "UNADDRESSABLE ACCESS\n*!TestingProfile::FinishInit\n",
746 # Whole trace
747 ("UNADDRESSABLE ACCESS\n" +
748 "*!TestingProfile::FinishInit\n" +
749 "*!TestingProfile::TestingProfile\n" +
750 "*!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody\n" +
751 "*!testing::Test::Run\n"),
754 negative_96010 = [
755 # Wrong type
756 "UNINITIALIZED READ\nname=zzz\n*!TestingProfile::FinishInit\n",
757 # No ellipsis
758 "UNADDRESSABLE ACCESS\nname=zzz\n*!BrowserAboutHandlerTest*\n",
761 TestStack(stack_96010, suppress_96010, negative_96010,
762 suppression_parser=ReadDrMemorySuppressions)
764 # Invalid heap arg
765 stack_invalid = """{
766 INVALID HEAP ARGUMENT
767 name=asdf
768 *!foo
769 }"""
770 suppress_invalid = [
771 "INVALID HEAP ARGUMENT\n*!foo\n",
773 negative_invalid = [
774 "UNADDRESSABLE ACCESS\n*!foo\n",
777 TestStack(stack_invalid, suppress_invalid, negative_invalid,
778 suppression_parser=ReadDrMemorySuppressions)
780 # Suppress only ntdll
781 stack_in_ntdll = """{
782 UNADDRESSABLE ACCESS
783 name=<insert_a_suppression_name_here>
784 ntdll.dll!RtlTryEnterCriticalSection
785 }"""
786 stack_not_ntdll = """{
787 UNADDRESSABLE ACCESS
788 name=<insert_a_suppression_name_here>
789 notntdll.dll!RtlTryEnterCriticalSection
790 }"""
792 suppress_in_ntdll = [
793 "UNADDRESSABLE ACCESS\nntdll.dll!RtlTryEnterCriticalSection\n",
795 suppress_in_any = [
796 "UNADDRESSABLE ACCESS\n*!RtlTryEnterCriticalSection\n",
799 TestStack(stack_in_ntdll, suppress_in_ntdll + suppress_in_any, [],
800 suppression_parser=ReadDrMemorySuppressions)
801 # Make sure we don't wildcard away the "not" part and match ntdll.dll by
802 # accident.
803 TestStack(stack_not_ntdll, suppress_in_any, suppress_in_ntdll,
804 suppression_parser=ReadDrMemorySuppressions)
806 # Suppress a POSSIBLE LEAK with LEAK.
807 stack_foo_possible = """{
808 POSSIBLE LEAK
809 name=foo possible
810 *!foo
811 }"""
812 suppress_foo_possible = [ "POSSIBLE LEAK\n*!foo\n" ]
813 suppress_foo_leak = [ "LEAK\n*!foo\n" ]
814 TestStack(stack_foo_possible, suppress_foo_possible + suppress_foo_leak, [],
815 suppression_parser=ReadDrMemorySuppressions)
817 # Don't suppress LEAK with POSSIBLE LEAK.
818 stack_foo_leak = """{
819 LEAK
820 name=foo leak
821 *!foo
822 }"""
823 TestStack(stack_foo_leak, suppress_foo_leak, suppress_foo_possible,
824 suppression_parser=ReadDrMemorySuppressions)
826 # Test case insensitivity of module names.
827 stack_user32_mixed_case = """{
828 LEAK
829 name=<insert>
830 USER32.dll!foo
831 user32.DLL!bar
832 user32.dll!baz
833 }"""
834 suppress_user32 = [ # Module name case doesn't matter.
835 "LEAK\nuser32.dll!foo\nuser32.dll!bar\nuser32.dll!baz\n",
836 "LEAK\nUSER32.DLL!foo\nUSER32.DLL!bar\nUSER32.DLL!baz\n",
838 no_suppress_user32 = [ # Function name case matters.
839 "LEAK\nuser32.dll!FOO\nuser32.dll!BAR\nuser32.dll!BAZ\n",
840 "LEAK\nUSER32.DLL!FOO\nUSER32.DLL!BAR\nUSER32.DLL!BAZ\n",
842 TestStack(stack_user32_mixed_case, suppress_user32, no_suppress_user32,
843 suppression_parser=ReadDrMemorySuppressions)
845 # Test mod!... frames.
846 stack_kernel32_through_ntdll = """{
847 LEAK
848 name=<insert>
849 kernel32.dll!foo
850 KERNEL32.dll!bar
851 kernel32.DLL!baz
852 ntdll.dll!quux
853 }"""
854 suppress_mod_ellipsis = [
855 "LEAK\nkernel32.dll!...\nntdll.dll!quux\n",
856 "LEAK\nKERNEL32.DLL!...\nntdll.dll!quux\n",
858 no_suppress_mod_ellipsis = [
859 # Need one or more matching frames, not zero, unlike regular ellipsis.
860 "LEAK\nuser32.dll!...\nkernel32.dll!...\nntdll.dll!quux\n",
862 TestStack(stack_kernel32_through_ntdll, suppress_mod_ellipsis,
863 no_suppress_mod_ellipsis,
864 suppression_parser=ReadDrMemorySuppressions)
866 # Test that the presubmit checks work.
867 forgot_to_name = """
868 UNADDRESSABLE ACCESS
869 name=<insert_a_suppression_name_here>
870 ntdll.dll!RtlTryEnterCriticalSection
872 TestFailPresubmit(forgot_to_name, 'forgotten to put a suppression',
873 suppression_parser=ReadDrMemorySuppressions)
875 named_twice = """
876 UNADDRESSABLE ACCESS
877 name=http://crbug.com/1234
878 *!foo
880 UNADDRESSABLE ACCESS
881 name=http://crbug.com/1234
882 *!bar
884 TestFailPresubmit(named_twice, 'defined more than once',
885 suppression_parser=ReadDrMemorySuppressions)
887 forgot_stack = """
888 UNADDRESSABLE ACCESS
889 name=http://crbug.com/1234
891 TestFailPresubmit(forgot_stack, 'has no stack frames',
892 suppression_parser=ReadDrMemorySuppressions)
894 ends_in_ellipsis = """
895 UNADDRESSABLE ACCESS
896 name=http://crbug.com/1234
897 ntdll.dll!RtlTryEnterCriticalSection
900 TestFailPresubmit(ends_in_ellipsis, 'ends in an ellipsis',
901 suppression_parser=ReadDrMemorySuppressions)
903 bad_stack_frame = """
904 UNADDRESSABLE ACCESS
905 name=http://crbug.com/1234
906 fun:memcheck_style_frame
908 TestFailPresubmit(bad_stack_frame, 'Unexpected stack frame pattern',
909 suppression_parser=ReadDrMemorySuppressions)
911 # Test FilenameToTool.
912 filenames_to_tools = {
913 "tools/heapcheck/suppressions.txt": "heapcheck",
914 "tools/valgrind/tsan/suppressions.txt": "tsan",
915 "tools/valgrind/drmemory/suppressions.txt": "drmemory",
916 "tools/valgrind/drmemory/suppressions_full.txt": "drmemory",
917 "tools/valgrind/memcheck/suppressions.txt": "memcheck",
918 "tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
919 "asdf/tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
920 "foo/bar/baz/tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
921 "foo/bar/baz/tools/valgrind/suppressions.txt": None,
922 "tools/valgrind/suppressions.txt": None,
924 for (filename, expected_tool) in filenames_to_tools.items():
925 filename.replace('/', os.sep) # Make the path look native.
926 tool = FilenameToTool(filename)
927 assert tool == expected_tool, (
928 "failed to get expected tool for filename %r, expected %s, got %s" %
929 (filename, expected_tool, tool))
931 # Test ValgrindStyleSuppression.__str__.
932 supp = ValgrindStyleSuppression("http://crbug.com/1234", "Memcheck:Leak",
933 ["...", "fun:foo"], "supp.txt:1")
934 # Intentional 3-space indent. =/
935 supp_str = ("{\n"
936 " http://crbug.com/1234\n"
937 " Memcheck:Leak\n"
938 " ...\n"
939 " fun:foo\n"
940 "}\n")
941 assert str(supp) == supp_str, (
942 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp), supp_str))
944 # Test DrMemorySuppression.__str__.
945 supp = DrMemorySuppression(
946 "http://crbug.com/1234", "LEAK", None, ["...", "*!foo"], "supp.txt:1")
947 supp_str = ("LEAK\n"
948 "name=http://crbug.com/1234\n"
949 "...\n"
950 "*!foo\n")
951 assert str(supp) == supp_str, (
952 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp), supp_str))
954 supp = DrMemorySuppression(
955 "http://crbug.com/1234", "UNINITIALIZED READ", "test 0x08(%eax) $0x01",
956 ["ntdll.dll!*", "*!foo"], "supp.txt:1")
957 supp_str = ("UNINITIALIZED READ\n"
958 "name=http://crbug.com/1234\n"
959 "instruction=test 0x08(%eax) $0x01\n"
960 "ntdll.dll!*\n"
961 "*!foo\n")
962 assert str(supp) == supp_str, (
963 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp), supp_str))
966 if __name__ == '__main__':
967 SelfTest()
968 print 'PASS'