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.
7 from collections
import defaultdict
17 def ReadReportsFromFile(filename
):
18 """ Returns a list of (report_hash, report) and the URL of the report on the
21 input_file
= file(filename
, 'r')
22 # reports is a list of (error hash, report) pairs.
24 in_suppression
= False
26 # This stores the last error hash found while reading the file.
28 for line
in input_file
:
30 line
= line
.replace("</span><span class=\"stdout\">", "")
31 line
= line
.replace("</span><span class=\"stderr\">", "")
32 line
= line
.replace("<", "<")
33 line
= line
.replace(">", ">")
37 reports
+= [[last_hash
, "\n".join(cur_supp
)]]
38 in_suppression
= False
42 cur_supp
+= [" "*3 + line
]
46 elif line
.find("Suppression (error hash=#") == 0:
47 last_hash
= line
[25:41]
48 # The line at the end of the file is assumed to store the URL of the report.
52 """ Demangle a list of C++ symbols, return a list of human-readable symbols.
54 # -n is not the default on Mac.
55 args
= ['c++filt', '-n']
56 pipe
= subprocess
.Popen(args
, stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
)
57 stdout
, _
= pipe
.communicate(input='\n'.join(names
))
58 demangled
= stdout
.split("\n")
59 # Each line ends with a newline, so the final entry of the split output
61 assert len(demangled
) == len(names
)
64 def GetSymbolsFromReport(report
):
65 """Extract all symbols from a suppression report."""
68 prefix_len
= len(prefix
)
69 for line
in report
.splitlines():
70 index
= line
.find(prefix
)
72 symbols
.append(line
[index
+ prefix_len
:])
75 def PrintTopSymbols(symbol_reports
, top_count
):
76 """Print the |top_count| symbols with the most occurrences."""
77 boring_symbols
=['malloc', '_Znw*', 'TestBody']
78 sorted_reports
= sorted(filter(lambda x
:x
[0] not in boring_symbols
,
79 symbol_reports
.iteritems()),
80 key
=lambda x
:len(x
[1]), reverse
=True)
81 symbols
= symbol_reports
.keys()
82 demangled
= Demangle(symbols
)
83 assert len(demangled
) == len(symbols
)
84 symboltable
= dict(zip(symbols
, demangled
))
87 print "Top %d symbols" % top_count
88 for (symbol
, suppressions
) in sorted_reports
[:top_count
]:
89 print "%4d occurrences : %s" % (len(suppressions
), symboltable
[symbol
])
91 def ReadHashExclusions(exclusions
):
92 input_file
= file(exclusions
, 'r')
93 contents
= json
.load(input_file
)
94 return contents
['hashes']
98 supp
= suppressions
.GetSuppressions()
100 # all_reports is a map {report: list of urls containing this report}
101 all_reports
= defaultdict(list)
103 symbol_reports
= defaultdict(list)
105 # Create argument parser.
106 parser
= argparse
.ArgumentParser()
107 parser
.add_argument('--top-symbols', type=int, default
=0,
108 help='Print a list of the top <n> symbols')
109 parser
.add_argument('--symbol-filter', action
='append',
110 help='Filter out all suppressions not containing the specified symbol(s). '
111 'Matches against the mangled names.')
112 parser
.add_argument('--exclude-symbol', action
='append',
113 help='Filter out all suppressions containing the specified symbol(s). '
114 'Matches against the mangled names.')
115 parser
.add_argument('--exclude-hashes', action
='append',
116 help='Specify a .json file with a list of hashes to exclude.')
118 parser
.add_argument('reports', metavar
='report file', nargs
='+',
119 help='List of report files')
120 args
= parser
.parse_args(argv
)
122 # exclude_hashes is a list of strings, each string an error hash.
126 if args
.exclude_hashes
:
127 for excl
in args
.exclude_hashes
:
128 print "reading exclusion", excl
129 exclude_hashes
+= ReadHashExclusions(excl
)
131 for f
in args
.reports
:
132 f_reports
, url
= ReadReportsFromFile(f
)
133 for (hash, report
) in f_reports
:
134 if hash in exclude_hashes
:
136 all_reports
[report
] += [url
]
137 report_hashes
[report
] = hash
140 for r
in all_reports
:
141 cur_supp
= supp
['common_suppressions']
142 if all([re
.search("%20Mac%20|mac_valgrind", url
)
143 for url
in all_reports
[r
]]):
144 # Include mac suppressions if the report is only present on Mac
145 cur_supp
+= supp
['mac_suppressions']
146 elif all([re
.search("Linux%20", url
) for url
in all_reports
[r
]]):
147 cur_supp
+= supp
['linux_suppressions']
148 if all(["DrMemory" in url
for url
in all_reports
[r
]]):
149 cur_supp
+= supp
['drmem_suppressions']
150 if all(["DrMemory%20full" in url
for url
in all_reports
[r
]]):
151 cur_supp
+= supp
['drmem_full_suppressions']
153 # Test if this report is already suppressed
156 if s
.Match(r
.split("\n")):
160 # Skip reports if none of the symbols are in the report.
161 if args
.symbol_filter
and all(not s
in r
for s
in args
.symbol_filter
):
163 if args
.exclude_symbol
and any(s
in r
for s
in args
.exclude_symbol
):
168 print "==================================="
169 print "This report observed at"
170 for url
in all_reports
[r
]:
172 print "didn't match any suppressions:"
173 print "Suppression (error hash=#%s#):" % (report_hashes
[r
])
175 print "==================================="
177 if args
.top_symbols
> 0:
178 symbols
= GetSymbolsFromReport(r
)
179 for symbol
in symbols
:
180 symbol_reports
[symbol
].append(report_hashes
[r
])
182 if reports_count
> 0:
183 print ("%d unique reports don't match any of the suppressions" %
185 if args
.top_symbols
> 0:
186 PrintTopSymbols(symbol_reports
, args
.top_symbols
)
189 print "Congratulations! All reports are suppressed!"
190 # TODO(timurrrr): also make sure none of the old suppressions
191 # were narrowed too much.
194 if __name__
== "__main__":