Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / valgrind / test_suppressions.py
blob432a0b4397b1ce11efb30c78679b244b911c2844
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 import argparse
7 from collections import defaultdict
8 import json
9 import os
10 import re
11 import subprocess
12 import sys
14 import suppressions
17 def ReadReportsFromFile(filename):
18 """ Returns a list of (report_hash, report) and the URL of the report on the
19 waterfall.
20 """
21 input_file = file(filename, 'r')
22 # reports is a list of (error hash, report) pairs.
23 reports = []
24 in_suppression = False
25 cur_supp = []
26 # This stores the last error hash found while reading the file.
27 last_hash = ""
28 for line in input_file:
29 line = line.strip()
30 line = line.replace("</span><span class=\"stdout\">", "")
31 line = line.replace("</span><span class=\"stderr\">", "")
32 line = line.replace("&lt;", "<")
33 line = line.replace("&gt;", ">")
34 if in_suppression:
35 if line == "}":
36 cur_supp += ["}"]
37 reports += [[last_hash, "\n".join(cur_supp)]]
38 in_suppression = False
39 cur_supp = []
40 last_hash = ""
41 else:
42 cur_supp += [" "*3 + line]
43 elif line == "{":
44 in_suppression = True
45 cur_supp = ["{"]
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.
49 return reports,line
51 def Demangle(names):
52 """ Demangle a list of C++ symbols, return a list of human-readable symbols.
53 """
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
60 # will always be ''.
61 assert len(demangled) == len(names)
62 return demangled
64 def GetSymbolsFromReport(report):
65 """Extract all symbols from a suppression report."""
66 symbols = []
67 prefix = "fun:"
68 prefix_len = len(prefix)
69 for line in report.splitlines():
70 index = line.find(prefix)
71 if index != -1:
72 symbols.append(line[index + prefix_len:])
73 return symbols
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))
86 print "\n"
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']
97 def main(argv):
98 supp = suppressions.GetSuppressions()
100 # all_reports is a map {report: list of urls containing this report}
101 all_reports = defaultdict(list)
102 report_hashes = {}
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.
123 exclude_hashes = []
125 exclude_hashes = []
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:
135 continue
136 all_reports[report] += [url]
137 report_hashes[report] = hash
139 reports_count = 0
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
154 skip = False
155 for s in cur_supp:
156 if s.Match(r.split("\n")):
157 skip = True
158 break
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):
162 skip = True
163 if args.exclude_symbol and any(s in r for s in args.exclude_symbol):
164 skip = True
166 if not skip:
167 reports_count += 1
168 print "==================================="
169 print "This report observed at"
170 for url in all_reports[r]:
171 print " %s" % url
172 print "didn't match any suppressions:"
173 print "Suppression (error hash=#%s#):" % (report_hashes[r])
174 print 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" %
184 reports_count)
185 if args.top_symbols > 0:
186 PrintTopSymbols(symbol_reports, args.top_symbols)
188 else:
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__":
195 main(sys.argv[1:])