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.
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>
18 fun:wildcarded_fun*_name
19 # an ellipsis wildcards zero or more functions in a stack.
21 fun:some_other_function_name
24 If ran from the command line, suppressions.py does a self-test
25 of the Suppression class.
32 sys
.path
.insert(0, os
.path
.join(os
.path
.dirname(__file__
),
33 '..', 'python', 'google'))
40 def GetSuppressions():
41 suppressions_root
= path_utils
.ScriptDir()
46 supp_filename
= JOIN(suppressions_root
, "memcheck", "suppressions.txt")
47 vg_common
= ReadSuppressionsFromFile(supp_filename
)
48 result
['common_suppressions'] = vg_common
50 supp_filename
= JOIN(suppressions_root
, "memcheck", "suppressions_linux.txt")
51 vg_linux
= ReadSuppressionsFromFile(supp_filename
)
52 result
['linux_suppressions'] = vg_linux
54 supp_filename
= JOIN(suppressions_root
, "memcheck", "suppressions_mac.txt")
55 vg_mac
= ReadSuppressionsFromFile(supp_filename
)
56 result
['mac_suppressions'] = vg_mac
58 supp_filename
= JOIN(suppressions_root
, "drmemory", "suppressions.txt")
59 result
['drmem_suppressions'] = ReadSuppressionsFromFile(supp_filename
)
60 supp_filename
= JOIN(suppressions_root
, "drmemory", "suppressions_full.txt")
61 result
['drmem_full_suppressions'] = ReadSuppressionsFromFile(supp_filename
)
66 def GlobToRegex(glob_pattern
, ignore_case
=False):
67 """Translate glob wildcards (*?) into regex syntax. Escape the rest."""
69 for char
in glob_pattern
:
74 elif ignore_case
and char
.isalpha():
75 regex
+= '[%s%s]' % (char
.lower(), char
.upper())
77 regex
+= re
.escape(char
)
81 def StripAndSkipCommentsIterator(lines
):
82 """Generator of (line_no, line) pairs that strips comments and whitespace."""
83 for (line_no
, line
) in enumerate(lines
):
84 line
= line
.strip() # Drop \n
85 if line
.startswith('#'):
87 # Skip comment lines, but not empty lines, they indicate the end of a
88 # suppression. Add one to the line number as well, since most editors use
89 # 1-based numberings, and enumerate is 0-based.
90 yield (line_no
+ 1, line
)
93 class Suppression(object):
94 """This class represents a single stack trace suppression.
97 description: A string representing the error description.
98 type: A string representing the error type, e.g. Memcheck:Leak.
99 stack: The lines comprising the stack trace for the suppression.
100 regex: The actual regex used to match against scraped reports.
103 def __init__(self
, description
, type, stack
, defined_at
, regex
):
104 """Inits Suppression.
106 description, type, stack, regex: same as class attributes
107 defined_at: file:line identifying where the suppression was defined
109 self
.description
= description
112 self
.defined_at
= defined_at
113 self
.regex
= re
.compile(regex
, re
.MULTILINE
)
115 def Match(self
, suppression_from_report
):
116 """Returns bool indicating whether this suppression matches
117 the suppression generated from Valgrind error report.
119 We match our suppressions against generated suppressions
120 (not against reports) since they have the same format
121 while the reports are taken from XML, contain filenames,
122 they are demangled, and are generally more difficult to
126 suppression_from_report: list of strings (function names).
128 True if the suppression is not empty and matches the report.
132 lines
= [f
.strip() for f
in suppression_from_report
]
133 return self
.regex
.match('\n'.join(lines
) + '\n') is not None
136 def FilenameToTool(filename
):
137 """Return the name of the tool that a file is related to, or None.
140 tools/valgrind/drmemory/suppressions.txt -> drmemory
141 tools/valgrind/drmemory/suppressions_full.txt -> drmemory
142 tools/valgrind/memcheck/suppressions.txt -> memcheck
143 tools/valgrind/memcheck/suppressions_mac.txt -> memcheck
145 filename
= os
.path
.abspath(filename
)
146 parts
= filename
.split(os
.sep
)
148 if tool
in ('drmemory', 'memcheck'):
153 def ReadSuppressionsFromFile(filename
):
154 """Read suppressions from the given file and return them as a list"""
156 "drmemory": ReadDrMemorySuppressions
,
157 "memcheck": ReadValgrindStyleSuppressions
,
159 tool
= FilenameToTool(filename
)
160 assert tool
in tool_to_parser
, (
161 "unknown tool %s for filename %s" % (tool
, filename
))
162 parse_func
= tool_to_parser
[tool
]
164 # Consider non-existent files to be empty.
165 if not os
.path
.exists(filename
):
168 input_file
= file(filename
, 'r')
170 return parse_func(input_file
, filename
)
171 except SuppressionError
:
176 class ValgrindStyleSuppression(Suppression
):
177 """A suppression using the Valgrind syntax.
179 Most tools, even ones that are not Valgrind-based, use this syntax.
185 def __init__(self
, description
, type, stack
, defined_at
):
186 """Creates a suppression using the Memcheck syntax."""
187 regex
= '{\n.*\n%s\n' % type
192 regex
+= GlobToRegex(line
)
197 # In the recent version of valgrind-variant we've switched
198 # from memcheck's default Addr[1248]/Value[1248]/Cond suppression types
199 # to simply Unaddressable/Uninitialized.
200 # The suppression generator no longer gives us "old" types thus
201 # for the "new-type" suppressions:
202 # * Memcheck:Unaddressable should also match Addr* reports,
203 # * Memcheck:Uninitialized should also match Cond and Value reports,
205 # We also want to support legacy suppressions (e.g. copied from
206 # upstream bugs etc), so:
207 # * Memcheck:Addr[1248] suppressions should match Unaddressable reports,
208 # * Memcheck:Cond and Memcheck:Value[1248] should match Uninitialized.
209 # Please note the latest two rules only apply to the
210 # tools/valgrind/waterfall.sh suppression matcher and the real
211 # valgrind-variant Memcheck will not suppress
212 # e.g. Addr1 printed as Unaddressable with Addr4 suppression.
213 # Be careful to check the access size while copying legacy suppressions!
214 for sz
in [1, 2, 4, 8]:
215 regex
= regex
.replace("\nMemcheck:Addr%d\n" % sz
,
216 "\nMemcheck:(Addr%d|Unaddressable)\n" % sz
)
217 regex
= regex
.replace("\nMemcheck:Value%d\n" % sz
,
218 "\nMemcheck:(Value%d|Uninitialized)\n" % sz
)
219 regex
= regex
.replace("\nMemcheck:Cond\n",
220 "\nMemcheck:(Cond|Uninitialized)\n")
221 regex
= regex
.replace("\nMemcheck:Unaddressable\n",
222 "\nMemcheck:(Addr.|Unaddressable)\n")
223 regex
= regex
.replace("\nMemcheck:Uninitialized\n",
224 "\nMemcheck:(Cond|Value.|Uninitialized)\n")
226 return super(ValgrindStyleSuppression
, self
).__init
__(
227 description
, type, stack
, defined_at
, regex
)
231 lines
= [self
.description
, self
.type] + self
.stack
232 return "{\n %s\n}\n" % "\n ".join(lines
)
235 class SuppressionError(Exception):
236 def __init__(self
, message
, happened_at
):
237 self
._message
= message
238 self
._happened
_at
= happened_at
241 return 'Error reading suppressions at %s!\n%s' % (
242 self
._happened
_at
, self
._message
)
245 def ReadValgrindStyleSuppressions(lines
, supp_descriptor
):
246 """Given a list of lines, returns a list of suppressions.
249 lines: a list of lines containing suppressions.
250 supp_descriptor: should typically be a filename.
251 Used only when printing errors.
257 in_suppression
= False
262 if line
.startswith('#'):
264 if not in_suppression
:
266 # empty lines between suppressions
268 elif line
.startswith('{'):
269 in_suppression
= True
272 raise SuppressionError('Expected: "{"',
273 "%s:%d" % (supp_descriptor
, nline
))
274 elif line
.startswith('}'):
276 ValgrindStyleSuppression(cur_descr
, cur_type
, cur_stack
,
277 "%s:%d" % (supp_descriptor
, nline
)))
281 in_suppression
= False
286 if not line
.startswith("Memcheck:"):
287 raise SuppressionError(
288 'Expected "Memcheck:TYPE", got "%s"' % line
,
289 "%s:%d" % (supp_descriptor
, nline
))
290 supp_type
= line
.split(':')[1]
291 if not supp_type
in ["Addr1", "Addr2", "Addr4", "Addr8",
292 "Cond", "Free", "Jump", "Leak", "Overlap", "Param",
293 "Value1", "Value2", "Value4", "Value8",
294 "Unaddressable", "Uninitialized"]:
295 raise SuppressionError('Unknown suppression type "%s"' % supp_type
,
296 "%s:%d" % (supp_descriptor
, nline
))
299 elif re
.match("^fun:.*|^obj:.*|^\.\.\.$", line
):
300 cur_stack
.append(line
.strip())
301 elif len(cur_stack
) == 0 and cur_type
== "Memcheck:Param":
302 cur_stack
.append(line
.strip())
304 raise SuppressionError(
305 '"fun:function_name" or "obj:object_file" or "..." expected',
306 "%s:%d" % (supp_descriptor
, nline
))
310 def PresubmitCheckSuppressions(supps
):
311 """Check a list of suppressions and return a list of SuppressionErrors.
313 Mostly useful for separating the checking logic from the Presubmit API for
316 known_supp_names
= {} # Key: name, Value: suppression.
319 if re
.search("<.*suppression.name.here>", s
.description
):
320 # Suppression name line is
321 # <insert_a_suppression_name_here> for Memcheck,
322 # name=<insert_a_suppression_name_here> for DrMemory
325 "You've forgotten to put a suppression name like bug_XXX",
329 if s
.description
in known_supp_names
:
332 'Suppression named "%s" is defined more than once, '
333 'see %s' % (s
.description
,
334 known_supp_names
[s
.description
].defined_at
),
337 known_supp_names
[s
.description
] = s
341 def PresubmitCheck(input_api
, output_api
):
342 """A helper function useful in PRESUBMIT.py
343 Returns a list of errors or [].
345 sup_regex
= re
.compile('suppressions.*\.txt$')
346 filenames
= [f
.AbsoluteLocalPath() for f
in input_api
.AffectedFiles()
347 if sup_regex
.search(f
.LocalPath())]
353 supps
= ReadSuppressionsFromFile(f
)
354 errors
.extend(PresubmitCheckSuppressions(supps
))
355 except SuppressionError
as e
:
358 return [output_api
.PresubmitError(str(e
)) for e
in errors
]
361 class DrMemorySuppression(Suppression
):
362 """A suppression using the DrMemory syntax.
365 instr: The instruction to match.
366 Rest inherited from Suppression.
369 def __init__(self
, name
, report_type
, instr
, stack
, defined_at
):
373 # Construct the regex.
375 if report_type
== 'LEAK':
376 regex
+= '(POSSIBLE )?LEAK'
379 regex
+= '\nname=.*\n'
381 # TODO(rnk): Implement http://crbug.com/107416#c5 .
382 # drmemory_analyze.py doesn't generate suppressions with an instruction in
383 # them, so these suppressions will always fail to match. We should override
384 # Match to fetch the instruction from the report and try to match against
387 regex
+= 'instruction=%s\n' % GlobToRegex(instr
)
393 (mod
, func
) = line
.split('!')
394 if func
== ELLIPSIS
: # mod!ellipsis frame
395 regex
+= '(%s\!.*\n)+' % GlobToRegex(mod
, ignore_case
=True)
396 else: # mod!func frame
397 # Ignore case for the module match, but not the function match.
398 regex
+= '%s\!%s\n' % (GlobToRegex(mod
, ignore_case
=True),
399 GlobToRegex(func
, ignore_case
=False))
401 regex
+= GlobToRegex(line
)
403 regex
+= '(.*\n)*' # Match anything left in the stack.
405 return super(DrMemorySuppression
, self
).__init
__(name
, report_type
, stack
,
410 text
= self
.type + "\n"
412 text
+= "name=%s\n" % self
.description
414 text
+= "instruction=%s\n" % self
.instr
415 text
+= "\n".join(self
.stack
)
420 # Possible DrMemory error report types. Keep consistent with suppress_name
421 # array in drmemory/drmemory/report.c.
422 DRMEMORY_ERROR_TYPES
= [
423 'UNADDRESSABLE ACCESS',
424 'UNINITIALIZED READ',
425 'INVALID HEAP ARGUMENT',
434 # Regexes to match valid drmemory frames.
435 DRMEMORY_FRAME_PATTERNS
= [
436 re
.compile(r
"^.*\!.*$"), # mod!func
437 re
.compile(r
"^.*!\.\.\.$"), # mod!ellipsis
438 re
.compile(r
"^\<.*\+0x.*\>$"), # <mod+0xoffs>
439 re
.compile(r
"^\<not in a module\>$"),
440 re
.compile(r
"^system call .*$"),
441 re
.compile(r
"^\*$"), # wildcard
442 re
.compile(r
"^\.\.\.$"), # ellipsis
446 def ReadDrMemorySuppressions(lines
, supp_descriptor
):
447 """Given a list of lines, returns a list of DrMemory suppressions.
450 lines: a list of lines containing suppressions.
451 supp_descriptor: should typically be a filename.
452 Used only when parsing errors happen.
454 lines
= StripAndSkipCommentsIterator(lines
)
456 for (line_no
, line
) in lines
:
459 if line
not in DRMEMORY_ERROR_TYPES
:
460 raise SuppressionError('Expected a DrMemory error type, '
461 'found %r instead\n Valid error types: %s' %
462 (line
, ' '.join(DRMEMORY_ERROR_TYPES
)),
463 "%s:%d" % (supp_descriptor
, line_no
))
465 # Suppression starts here.
470 defined_at
= "%s:%d" % (supp_descriptor
, line_no
)
472 for (line_no
, line
) in lines
:
473 if not found_stack
and line
.startswith('name='):
474 name
= line
.replace('name=', '')
475 elif not found_stack
and line
.startswith('instruction='):
476 instr
= line
.replace('instruction=', '')
478 # Unrecognized prefix indicates start of stack trace.
481 # Blank line means end of suppression.
483 if not any([regex
.match(line
) for regex
in DRMEMORY_FRAME_PATTERNS
]):
484 raise SuppressionError(
485 ('Unexpected stack frame pattern at line %d\n' +
486 'Frames should be one of the following:\n' +
487 ' module!function\n' +
489 ' <module+0xhexoffset>\n' +
490 ' <not in a module>\n' +
491 ' system call Name\n' +
493 ' ...\n') % line_no
, defined_at
)
496 if len(stack
) == 0: # In case we hit EOF or blank without any stack frames.
497 raise SuppressionError('Suppression "%s" has no stack frames, ends at %d'
498 % (name
, line_no
), defined_at
)
499 if stack
[-1] == ELLIPSIS
:
500 raise SuppressionError('Suppression "%s" ends in an ellipsis on line %d' %
501 (name
, line_no
), defined_at
)
504 DrMemorySuppression(name
, report_type
, instr
, stack
, defined_at
))
509 def ParseSuppressionOfType(lines
, supp_descriptor
, def_line_no
, report_type
):
510 """Parse the suppression starting on this line.
512 Suppressions start with a type, have an optional name and instruction, and a
513 stack trace that ends in a blank line.
518 def TestStack(stack
, positive
, negative
, suppression_parser
=None):
519 """A helper function for SelfTest() that checks a single stack.
522 stack: the stack to match the suppressions.
523 positive: the list of suppressions that must match the given stack.
524 negative: the list of suppressions that should not match.
525 suppression_parser: optional arg for the suppression parser, default is
526 ReadValgrindStyleSuppressions.
528 if not suppression_parser
:
529 suppression_parser
= ReadValgrindStyleSuppressions
530 for supp
in positive
:
531 parsed
= suppression_parser(supp
.split("\n"), "positive_suppression")
532 assert parsed
[0].Match(stack
.split("\n")), (
533 "Suppression:\n%s\ndidn't match stack:\n%s" % (supp
, stack
))
534 for supp
in negative
:
535 parsed
= suppression_parser(supp
.split("\n"), "negative_suppression")
536 assert not parsed
[0].Match(stack
.split("\n")), (
537 "Suppression:\n%s\ndid match stack:\n%s" % (supp
, stack
))
540 def TestFailPresubmit(supp_text
, error_text
, suppression_parser
=None):
541 """A helper function for SelfTest() that verifies a presubmit check fires.
544 supp_text: suppression text to parse.
545 error_text: text of the presubmit error we expect to find.
546 suppression_parser: optional arg for the suppression parser, default is
547 ReadValgrindStyleSuppressions.
549 if not suppression_parser
:
550 suppression_parser
= ReadValgrindStyleSuppressions
552 supps
= suppression_parser(supp_text
.split("\n"), "<presubmit suppression>")
553 except SuppressionError
, e
:
554 # If parsing raised an exception, match the error text here.
555 assert error_text
in str(e
), (
556 "presubmit text %r not in SuppressionError:\n%r" %
557 (error_text
, str(e
)))
559 # Otherwise, run the presubmit checks over the supps. We expect a single
560 # error that has text matching error_text.
561 errors
= PresubmitCheckSuppressions(supps
)
562 assert len(errors
) == 1, (
563 "expected exactly one presubmit error, got:\n%s" % errors
)
564 assert error_text
in str(errors
[0]), (
565 "presubmit text %r not in SuppressionError:\n%r" %
566 (error_text
, str(errors
[0])))
570 """Tests the Suppression.Match() capabilities."""
572 test_memcheck_stack_1
= """{
582 test_memcheck_stack_2
= """{
584 Memcheck:Uninitialized
592 test_memcheck_stack_3
= """{
594 Memcheck:Unaddressable
602 test_memcheck_stack_4
= """{
612 positive_memcheck_suppressions_1
= [
613 "{\nzzz\nMemcheck:Leak\nfun:absolutly\n}",
614 "{\nzzz\nMemcheck:Leak\nfun:ab*ly\n}",
615 "{\nzzz\nMemcheck:Leak\nfun:absolutly\nfun:brilliant\n}",
616 "{\nzzz\nMemcheck:Leak\n...\nfun:brilliant\n}",
617 "{\nzzz\nMemcheck:Leak\n...\nfun:detection\n}",
618 "{\nzzz\nMemcheck:Leak\nfun:absolutly\n...\nfun:detection\n}",
619 "{\nzzz\nMemcheck:Leak\nfun:ab*ly\n...\nfun:detection\n}",
620 "{\nzzz\nMemcheck:Leak\n...\nobj:condition\n}",
621 "{\nzzz\nMemcheck:Leak\n...\nobj:condition\nfun:detection\n}",
622 "{\nzzz\nMemcheck:Leak\n...\nfun:brilliant\nobj:condition\n}",
625 positive_memcheck_suppressions_2
= [
626 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\n}",
627 "{\nzzz\nMemcheck:Uninitialized\nfun:ab*ly\n}",
628 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\nfun:brilliant\n}",
629 # Legacy suppression types
630 "{\nzzz\nMemcheck:Value1\n...\nfun:brilliant\n}",
631 "{\nzzz\nMemcheck:Cond\n...\nfun:detection\n}",
632 "{\nzzz\nMemcheck:Value8\nfun:absolutly\nfun:brilliant\n}",
635 positive_memcheck_suppressions_3
= [
636 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\n}",
637 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\nfun:brilliant\n}",
638 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\nfun:brilliant\n}",
639 # Legacy suppression types
640 "{\nzzz\nMemcheck:Addr1\n...\nfun:brilliant\n}",
641 "{\nzzz\nMemcheck:Addr8\n...\nfun:detection\n}",
644 positive_memcheck_suppressions_4
= [
645 "{\nzzz\nMemcheck:Addr4\nfun:absolutly\n}",
646 "{\nzzz\nMemcheck:Unaddressable\nfun:absolutly\n}",
647 "{\nzzz\nMemcheck:Addr4\nfun:absolutly\nfun:brilliant\n}",
648 "{\nzzz\nMemcheck:Unaddressable\n...\nfun:brilliant\n}",
649 "{\nzzz\nMemcheck:Addr4\n...\nfun:detection\n}",
652 negative_memcheck_suppressions_1
= [
653 "{\nzzz\nMemcheck:Leak\nfun:abnormal\n}",
654 "{\nzzz\nMemcheck:Leak\nfun:ab*liant\n}",
655 "{\nzzz\nMemcheck:Leak\nfun:brilliant\n}",
656 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
657 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
660 negative_memcheck_suppressions_2
= [
661 "{\nzzz\nMemcheck:Cond\nfun:abnormal\n}",
662 "{\nzzz\nMemcheck:Value2\nfun:abnormal\n}",
663 "{\nzzz\nMemcheck:Uninitialized\nfun:ab*liant\n}",
664 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
665 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
666 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
667 "{\nzzz\nMemcheck:Unaddressable\nfun:brilliant\n}",
670 negative_memcheck_suppressions_3
= [
671 "{\nzzz\nMemcheck:Addr1\nfun:abnormal\n}",
672 "{\nzzz\nMemcheck:Uninitialized\nfun:absolutly\n}",
673 "{\nzzz\nMemcheck:Addr2\nfun:ab*liant\n}",
674 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
675 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
676 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
679 negative_memcheck_suppressions_4
= [
680 "{\nzzz\nMemcheck:Addr1\nfun:abnormal\n}",
681 "{\nzzz\nMemcheck:Addr4\nfun:abnormal\n}",
682 "{\nzzz\nMemcheck:Unaddressable\nfun:abnormal\n}",
683 "{\nzzz\nMemcheck:Addr1\nfun:absolutly\n}",
684 "{\nzzz\nMemcheck:Addr2\nfun:ab*liant\n}",
685 "{\nzzz\nMemcheck:Value4\nfun:brilliant\n}",
686 "{\nzzz\nMemcheck:Leak\nobj:condition\n}",
687 "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}",
690 TestStack(test_memcheck_stack_1
,
691 positive_memcheck_suppressions_1
,
692 negative_memcheck_suppressions_1
)
693 TestStack(test_memcheck_stack_2
,
694 positive_memcheck_suppressions_2
,
695 negative_memcheck_suppressions_2
)
696 TestStack(test_memcheck_stack_3
,
697 positive_memcheck_suppressions_3
,
698 negative_memcheck_suppressions_3
)
699 TestStack(test_memcheck_stack_4
,
700 positive_memcheck_suppressions_4
,
701 negative_memcheck_suppressions_4
)
703 # TODO(timurrrr): add TestFailPresubmit tests.
705 ### DrMemory self tests.
707 # http://crbug.com/96010 suppression.
710 name=<insert_a_suppression_name_here>
711 *!TestingProfile::FinishInit
712 *!TestingProfile::TestingProfile
713 *!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody
718 "UNADDRESSABLE ACCESS\nname=zzz\n...\n*!testing::Test::Run\n",
719 ("UNADDRESSABLE ACCESS\nname=zzz\n...\n" +
720 "*!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody\n"),
721 "UNADDRESSABLE ACCESS\nname=zzz\n...\n*!BrowserAboutHandlerTest*\n",
722 "UNADDRESSABLE ACCESS\nname=zzz\n*!TestingProfile::FinishInit\n",
723 # No name should be needed
724 "UNADDRESSABLE ACCESS\n*!TestingProfile::FinishInit\n",
726 ("UNADDRESSABLE ACCESS\n" +
727 "*!TestingProfile::FinishInit\n" +
728 "*!TestingProfile::TestingProfile\n" +
729 "*!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody\n" +
730 "*!testing::Test::Run\n"),
735 "UNINITIALIZED READ\nname=zzz\n*!TestingProfile::FinishInit\n",
737 "UNADDRESSABLE ACCESS\nname=zzz\n*!BrowserAboutHandlerTest*\n",
740 TestStack(stack_96010
, suppress_96010
, negative_96010
,
741 suppression_parser
=ReadDrMemorySuppressions
)
745 INVALID HEAP ARGUMENT
750 "INVALID HEAP ARGUMENT\n*!foo\n",
753 "UNADDRESSABLE ACCESS\n*!foo\n",
756 TestStack(stack_invalid
, suppress_invalid
, negative_invalid
,
757 suppression_parser
=ReadDrMemorySuppressions
)
759 # Suppress only ntdll
760 stack_in_ntdll
= """{
762 name=<insert_a_suppression_name_here>
763 ntdll.dll!RtlTryEnterCriticalSection
765 stack_not_ntdll
= """{
767 name=<insert_a_suppression_name_here>
768 notntdll.dll!RtlTryEnterCriticalSection
771 suppress_in_ntdll
= [
772 "UNADDRESSABLE ACCESS\nntdll.dll!RtlTryEnterCriticalSection\n",
775 "UNADDRESSABLE ACCESS\n*!RtlTryEnterCriticalSection\n",
778 TestStack(stack_in_ntdll
, suppress_in_ntdll
+ suppress_in_any
, [],
779 suppression_parser
=ReadDrMemorySuppressions
)
780 # Make sure we don't wildcard away the "not" part and match ntdll.dll by
782 TestStack(stack_not_ntdll
, suppress_in_any
, suppress_in_ntdll
,
783 suppression_parser
=ReadDrMemorySuppressions
)
785 # Suppress a POSSIBLE LEAK with LEAK.
786 stack_foo_possible
= """{
791 suppress_foo_possible
= [ "POSSIBLE LEAK\n*!foo\n" ]
792 suppress_foo_leak
= [ "LEAK\n*!foo\n" ]
793 TestStack(stack_foo_possible
, suppress_foo_possible
+ suppress_foo_leak
, [],
794 suppression_parser
=ReadDrMemorySuppressions
)
796 # Don't suppress LEAK with POSSIBLE LEAK.
797 stack_foo_leak
= """{
802 TestStack(stack_foo_leak
, suppress_foo_leak
, suppress_foo_possible
,
803 suppression_parser
=ReadDrMemorySuppressions
)
805 # Test case insensitivity of module names.
806 stack_user32_mixed_case
= """{
813 suppress_user32
= [ # Module name case doesn't matter.
814 "LEAK\nuser32.dll!foo\nuser32.dll!bar\nuser32.dll!baz\n",
815 "LEAK\nUSER32.DLL!foo\nUSER32.DLL!bar\nUSER32.DLL!baz\n",
817 no_suppress_user32
= [ # Function name case matters.
818 "LEAK\nuser32.dll!FOO\nuser32.dll!BAR\nuser32.dll!BAZ\n",
819 "LEAK\nUSER32.DLL!FOO\nUSER32.DLL!BAR\nUSER32.DLL!BAZ\n",
821 TestStack(stack_user32_mixed_case
, suppress_user32
, no_suppress_user32
,
822 suppression_parser
=ReadDrMemorySuppressions
)
824 # Test mod!... frames.
825 stack_kernel32_through_ntdll
= """{
833 suppress_mod_ellipsis
= [
834 "LEAK\nkernel32.dll!...\nntdll.dll!quux\n",
835 "LEAK\nKERNEL32.DLL!...\nntdll.dll!quux\n",
837 no_suppress_mod_ellipsis
= [
838 # Need one or more matching frames, not zero, unlike regular ellipsis.
839 "LEAK\nuser32.dll!...\nkernel32.dll!...\nntdll.dll!quux\n",
841 TestStack(stack_kernel32_through_ntdll
, suppress_mod_ellipsis
,
842 no_suppress_mod_ellipsis
,
843 suppression_parser
=ReadDrMemorySuppressions
)
845 # Test that the presubmit checks work.
848 name=<insert_a_suppression_name_here>
849 ntdll.dll!RtlTryEnterCriticalSection
851 TestFailPresubmit(forgot_to_name
, 'forgotten to put a suppression',
852 suppression_parser
=ReadDrMemorySuppressions
)
856 name=http://crbug.com/1234
860 name=http://crbug.com/1234
863 TestFailPresubmit(named_twice
, 'defined more than once',
864 suppression_parser
=ReadDrMemorySuppressions
)
868 name=http://crbug.com/1234
870 TestFailPresubmit(forgot_stack
, 'has no stack frames',
871 suppression_parser
=ReadDrMemorySuppressions
)
873 ends_in_ellipsis
= """
875 name=http://crbug.com/1234
876 ntdll.dll!RtlTryEnterCriticalSection
879 TestFailPresubmit(ends_in_ellipsis
, 'ends in an ellipsis',
880 suppression_parser
=ReadDrMemorySuppressions
)
882 bad_stack_frame
= """
884 name=http://crbug.com/1234
885 fun:memcheck_style_frame
887 TestFailPresubmit(bad_stack_frame
, 'Unexpected stack frame pattern',
888 suppression_parser
=ReadDrMemorySuppressions
)
890 # Test FilenameToTool.
891 filenames_to_tools
= {
892 "tools/valgrind/drmemory/suppressions.txt": "drmemory",
893 "tools/valgrind/drmemory/suppressions_full.txt": "drmemory",
894 "tools/valgrind/memcheck/suppressions.txt": "memcheck",
895 "tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
896 "asdf/tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
897 "foo/bar/baz/tools/valgrind/memcheck/suppressions_mac.txt": "memcheck",
898 "foo/bar/baz/tools/valgrind/suppressions.txt": None,
899 "tools/valgrind/suppressions.txt": None,
901 for (filename
, expected_tool
) in filenames_to_tools
.items():
902 filename
.replace('/', os
.sep
) # Make the path look native.
903 tool
= FilenameToTool(filename
)
904 assert tool
== expected_tool
, (
905 "failed to get expected tool for filename %r, expected %s, got %s" %
906 (filename
, expected_tool
, tool
))
908 # Test ValgrindStyleSuppression.__str__.
909 supp
= ValgrindStyleSuppression("http://crbug.com/1234", "Memcheck:Leak",
910 ["...", "fun:foo"], "supp.txt:1")
911 # Intentional 3-space indent. =/
913 " http://crbug.com/1234\n"
918 assert str(supp
) == supp_str
, (
919 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp
), supp_str
))
921 # Test DrMemorySuppression.__str__.
922 supp
= DrMemorySuppression(
923 "http://crbug.com/1234", "LEAK", None, ["...", "*!foo"], "supp.txt:1")
925 "name=http://crbug.com/1234\n"
928 assert str(supp
) == supp_str
, (
929 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp
), supp_str
))
931 supp
= DrMemorySuppression(
932 "http://crbug.com/1234", "UNINITIALIZED READ", "test 0x08(%eax) $0x01",
933 ["ntdll.dll!*", "*!foo"], "supp.txt:1")
934 supp_str
= ("UNINITIALIZED READ\n"
935 "name=http://crbug.com/1234\n"
936 "instruction=test 0x08(%eax) $0x01\n"
939 assert str(supp
) == supp_str
, (
940 "str(supp) != supp_str:\nleft: %s\nright: %s" % (str(supp
), supp_str
))
943 if __name__
== '__main__':