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
4 # found in the LICENSE file.
6 """Valgrind-style suppressions for heapchecker reports.
8 Suppressions are defined as follows:
10 # optional one-line comments anywhere in the suppressions file.
13 Short description of the error.
15 fun:wildcarded_fun*_name
16 # an ellipsis wildcards zero or more functions in a stack.
18 fun:some_other_function_name
21 Note that only a 'fun:' prefix is allowed, i.e. we can't suppress objects and
24 If ran from the command line, suppressions.py does a self-test of the
33 class Suppression(object):
34 """This class represents a single stack trace suppression.
37 type: A string representing the error type, e.g. Heapcheck:Leak.
38 description: A string representing the error description.
41 def __init__(self
, kind
, description
, stack
):
44 stack is a list of function names and/or wildcards.
48 description: Same as class attributes.
49 stack: A list of strings.
52 self
.description
= description
58 re_line
+= re
.escape(re_bucket
)
64 re_line
+= re
.escape(re_bucket
)
67 else: # there can't be any '\*'s in a stack trace
69 re_line
+= re
.escape(re_bucket
)
72 self
._re
= re
.compile(re_line
, re
.MULTILINE
)
74 def Match(self
, report
):
75 """Returns bool indicating whether the suppression matches the given report.
78 report: list of strings (function names).
80 True if the suppression is not empty and matches the report.
84 if self
._re
.match('\n'.join(report
) + '\n'):
90 class SuppressionError(Exception):
91 def __init__(self
, filename
, line
, report
=''):
92 Exception.__init
__(self
, filename
, line
, report
)
98 return 'Error reading suppressions from "%s" (line %d): %s.' % (
99 self
._file
, self
._line
, self
._report
)
102 def ReadSuppressionsFromFile(filename
):
103 """Given a file, returns a list of suppressions."""
104 input_file
= file(filename
, 'r')
111 for line
in input_file
:
114 if line
.startswith('#'):
116 elif line
.startswith('{'):
118 elif line
.startswith('}'):
119 result
.append(Suppression(cur_type
, cur_descr
, cur_stack
))
129 elif line
.startswith('fun:'):
131 cur_stack
.append(line
.strip())
132 elif line
.startswith(ELLIPSIS
):
133 cur_stack
.append(ELLIPSIS
)
135 raise SuppressionError(filename
, nline
,
136 '"fun:function_name" or "..." expected')
137 except SuppressionError
:
144 """Tests the Suppression.Match() capabilities."""
147 return Suppression('', '', list(lines
))
149 assert not empty
.Match([])
150 assert not empty
.Match(['foo', 'bar'])
151 asterisk
= GenSupp('*bar')
152 assert asterisk
.Match(['foobar', 'foobaz'])
153 assert not asterisk
.Match(['foobaz', 'foobar'])
154 ellipsis
= GenSupp('...', 'foo')
155 assert ellipsis
.Match(['foo', 'bar'])
156 assert ellipsis
.Match(['bar', 'baz', 'foo'])
157 assert not ellipsis
.Match(['bar', 'baz', 'bah'])
158 mixed
= GenSupp('...', 'foo*', 'function')
159 assert mixed
.Match(['foobar', 'foobaz', 'function'])
160 assert not mixed
.Match(['foobar', 'blah', 'function'])
161 at_and_dollar
= GenSupp('foo@GLIBC', 'bar@NOCANCEL')
162 assert at_and_dollar
.Match(['foo@GLIBC', 'bar@NOCANCEL'])
163 re_chars
= GenSupp('.*')
164 assert re_chars
.Match(['.foobar'])
165 assert not re_chars
.Match(['foobar'])
169 if __name__
== '__main__':