Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(371)

Side by Side Diff: tools/valgrind/waterfall_suppressions_used.py

Issue 10452044: Memory waterfall helper script to detect suppressions in use. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
5
6 # This script reports which suppressions are being used by the valgrind and
7 # heapcheck bots.
8
9 import json, urllib, urlparse, httplib
10 import sys
11
12 def Fetch(url):
13 """ Fetch JSON from |url|. """
14 return json.loads(urllib.urlopen(url).read())
15
16
17 def FetchTail(url, tail=4096):
18 """ Fetch the last |tail| bytes of |url|. """
19 urlsplit = urlparse.urlsplit(url)
20 conn = httplib.HTTPConnection(urlsplit.netloc)
21 conn.request('HEAD', urlsplit.path)
22 response = conn.getresponse()
23 if response.status != 200:
24 raise Exception('Could not fetch %s -- got %s' % (url, response.status))
25 length = int(response.getheader('Content-Length'))
26 response.read() # Must be done before next request.
27 conn.request('GET', urlsplit.path,
28 headers={'Range': 'bytes=%d-%d' % (length - tail, length)})
29 response = conn.getresponse()
30 if response.status not in [200, 206]:
31 raise Exception('Could not fetch %s -- got %s' % (url, response.status))
32 response_body = response.read()
33 # Often buildbot will ignore our range request and send the whole data.
34 return response_body[-tail:]
35
36
37 def FindMatchingBuilders(waterfall_url, category):
38 """ Return builders which match the given |category|. """
39 print >> sys.stderr, 'Finding %s bots' % (category)
40 builders = Fetch('%s/json/builders' % waterfall_url)
41 matching_builders = []
42 for name, info in builders.iteritems():
43 if category in info['category']:
44 matching_builders.append(name)
45 return matching_builders
46
47
48 def ParseMemoryTestLog(log):
49 """ Parse memory test log for suppressions used. """
50 suppressions_used = []
51 log = log[log.find('Suppressions used:'):]
52 for line in log.splitlines()[2:]:
53 if line.startswith('-----'):
54 break
55 count, name = line.strip().split(None, 1)
56 suppressions_used.append([name, int(count)])
57 return suppressions_used
58
59
60 def ParseHeapcheckTestLog(log):
61 """ Parse heapcheck test log for suppressions used. """
62 suppressions_used = []
63 log = log[log.find('Suppressions used:'):]
64 for line in log.splitlines()[2:]:
65 if line.startswith('-----'):
66 break
67 count, _, _, name = line.strip().split(None, 3)
68 suppressions_used.append([name, int(count)])
69 return suppressions_used
70
71
72 def FetchBuildSuppressions(steps, step_type, log_parser):
73 """ Fetch suppressions used in |steps| matching |step_type|.
74 Result is { suppression_name : [(step_name, count)] }.
75 """
76 suppressions = {}
77 for step in steps:
78 if step_type in step['name']:
79 stdio_log = None
80 for log_name, log_url in step['logs']:
81 if log_name == 'stdio':
82 stdio_log = log_url
83 break
84 if stdio_log is None:
85 print >> sys.stderr, 'WARNING: no stdio log in %s' % step['name']
86 continue
87 try:
88 for name, count in log_parser(FetchTail('%s/text' % stdio_log)):
89 suppressions.setdefault(name, []).append((step['name'], count))
90 except Exception, e:
91 print >> sys.stderr, 'WARNING: Could not parse %s' % stdio_log
92 print >> sys.stderr, e
93 return suppressions
94
95
96 def FetchBuilderSuppressions(builds, step_type, log_parser):
97 """ Fetches aggregate suppression stats for all |builds|.
98 Result is { suppression_name : { step_name : [avg_count, max_count] } }.
99 """
100 suppressions = {}
101 for build_id, build_info in builds.iteritems():
102 build_suppressions = FetchBuildSuppressions(build_info['steps'], step_type,
103 log_parser)
104 for name, stats in build_suppressions.iteritems():
105 totals = suppressions.setdefault(name, {})
106 for step_name, count in stats:
107 step_totals = totals.setdefault(step_name, [0, 0])
108 step_totals[0] += count
109 step_totals[1] = max(count, step_totals[1])
110 # Normalize averages.
111 for name, stats in suppressions.iteritems():
112 for step_name, step_totals in stats.iteritems():
113 step_totals[0] /= float(len(builds))
114 return suppressions
115
116
117 def FetchSuppressions(waterfall_url, builder_type, verbose=False, count=3):
118 """ Fetch suppression stats for |builder_type| from last |count| builds. """
119 builder_category, step_type, log_parser = builder_type
120 # The last one is still building, so skip it.
121 select = '&'.join(['select=%d' % (-i-2) for i in range(count)])
122
123 suppressions = {}
124 builders = FindMatchingBuilders(waterfall_url, builder_category)
125
126 for builder in builders:
127 print >> sys.stderr, 'Fetching suppressions from "%s"' % (builder)
128 builds = Fetch('%s/json/builders/%s/builds?%s' % (waterfall_url, builder,
129 select))
130 builder_suppressions = FetchBuilderSuppressions(builds, step_type,
131 log_parser)
132 for name, builder_stats in builder_suppressions.iteritems():
133 suppressions.setdefault(name, []).append((builder, builder_stats))
134
135 # Output.
136 for name, stats in sorted(suppressions.items()):
137 if verbose:
138 print name
139 # Compute totals across builders.
140 global_avg = 0
141 for builder, builder_stats in sorted(stats):
142 if verbose:
143 print ' %s' % builder
144 for step_name, [avg_count, max_count] in builder_stats.iteritems():
145 if verbose:
146 print ' %-30s %8.1f %8d' % (step_name, avg_count, max_count)
147 global_avg += avg_count
148 global_avg /= float(len(stats))
149 if verbose:
150 print ' %30s %8.1f' % ('TOTAL', global_avg)
151 else:
152 print '%8.1f %s' % (global_avg, name)
153
154
155 WATERFALL_URL = 'http://build.chromium.org/p/chromium.memory.fyi'
156
157 BUILDER_TYPES = {
158 # type is (builder category, step type, log parser)
159 'valgrind' : ('memory_tester', 'memory test', ParseMemoryTestLog),
160 'heapcheck' : ('heapcheck_tester', 'heapcheck test', ParseHeapcheckTestLog),
161 }
162
163 if __name__ == '__main__':
164 import sys
165 import getopt # No argparse before python 2.6
166
167 count = 3
168 verbose = False
169 try:
170 optlist, args = getopt.getopt(sys.argv[1:], 'vc:')
171 for opt, val in optlist:
172 if opt == '-c':
173 count = int(val)
174 if opt == '-v':
175 verbose = True
176 builder_type = BUILDER_TYPES[args[0]]
177 except:
178 print >> sys.stderr, 'Usage: %s [-c build_count] [-v] (%s)' % (
179 sys.argv[0], '|'.join(BUILDER_TYPES.keys()))
180 sys.exit(2)
181
182 FetchSuppressions(WATERFALL_URL, builder_type, count=count, verbose=verbose)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698