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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/valgrind/waterfall_suppressions_used.py
diff --git a/tools/valgrind/waterfall_suppressions_used.py b/tools/valgrind/waterfall_suppressions_used.py
new file mode 100755
index 0000000000000000000000000000000000000000..c6359a77b0b2d756cf31e37cd6f5b530fe39cc39
--- /dev/null
+++ b/tools/valgrind/waterfall_suppressions_used.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script reports which suppressions are being used by the valgrind and
+# heapcheck bots.
+
+import json, urllib, urlparse, httplib
+import sys
+
+def Fetch(url):
+ """ Fetch JSON from |url|. """
+ return json.loads(urllib.urlopen(url).read())
+
+
+def FetchTail(url, tail=4096):
+ """ Fetch the last |tail| bytes of |url|. """
+ urlsplit = urlparse.urlsplit(url)
+ conn = httplib.HTTPConnection(urlsplit.netloc)
+ conn.request('HEAD', urlsplit.path)
+ response = conn.getresponse()
+ if response.status != 200:
+ raise Exception('Could not fetch %s -- got %s' % (url, response.status))
+ length = int(response.getheader('Content-Length'))
+ response.read() # Must be done before next request.
+ conn.request('GET', urlsplit.path,
+ headers={'Range': 'bytes=%d-%d' % (length - tail, length)})
+ response = conn.getresponse()
+ if response.status not in [200, 206]:
+ raise Exception('Could not fetch %s -- got %s' % (url, response.status))
+ response_body = response.read()
+ # Often buildbot will ignore our range request and send the whole data.
+ return response_body[-tail:]
+
+
+def FindMatchingBuilders(waterfall_url, category):
+ """ Return builders which match the given |category|. """
+ print >> sys.stderr, 'Finding %s bots' % (category)
+ builders = Fetch('%s/json/builders' % waterfall_url)
+ matching_builders = []
+ for name, info in builders.iteritems():
+ if category in info['category']:
+ matching_builders.append(name)
+ return matching_builders
+
+
+def ParseMemoryTestLog(log):
+ """ Parse memory test log for suppressions used. """
+ suppressions_used = []
+ log = log[log.find('Suppressions used:'):]
+ for line in log.splitlines()[2:]:
+ if line.startswith('-----'):
+ break
+ count, name = line.strip().split(None, 1)
+ suppressions_used.append([name, int(count)])
+ return suppressions_used
+
+
+def ParseHeapcheckTestLog(log):
+ """ Parse heapcheck test log for suppressions used. """
+ suppressions_used = []
+ log = log[log.find('Suppressions used:'):]
+ for line in log.splitlines()[2:]:
+ if line.startswith('-----'):
+ break
+ count, _, _, name = line.strip().split(None, 3)
+ suppressions_used.append([name, int(count)])
+ return suppressions_used
+
+
+def FetchBuildSuppressions(steps, step_type, log_parser):
+ """ Fetch suppressions used in |steps| matching |step_type|.
+ Result is { suppression_name : [(step_name, count)] }.
+ """
+ suppressions = {}
+ for step in steps:
+ if step_type in step['name']:
+ stdio_log = None
+ for log_name, log_url in step['logs']:
+ if log_name == 'stdio':
+ stdio_log = log_url
+ break
+ if stdio_log is None:
+ print >> sys.stderr, 'WARNING: no stdio log in %s' % step['name']
+ continue
+ try:
+ for name, count in log_parser(FetchTail('%s/text' % stdio_log)):
+ suppressions.setdefault(name, []).append((step['name'], count))
+ except Exception, e:
+ print >> sys.stderr, 'WARNING: Could not parse %s' % stdio_log
+ print >> sys.stderr, e
+ return suppressions
+
+
+def FetchBuilderSuppressions(builds, step_type, log_parser):
+ """ Fetches aggregate suppression stats for all |builds|.
+ Result is { suppression_name : { step_name : [avg_count, max_count] } }.
+ """
+ suppressions = {}
+ for build_id, build_info in builds.iteritems():
+ build_suppressions = FetchBuildSuppressions(build_info['steps'], step_type,
+ log_parser)
+ for name, stats in build_suppressions.iteritems():
+ totals = suppressions.setdefault(name, {})
+ for step_name, count in stats:
+ step_totals = totals.setdefault(step_name, [0, 0])
+ step_totals[0] += count
+ step_totals[1] = max(count, step_totals[1])
+ # Normalize averages.
+ for name, stats in suppressions.iteritems():
+ for step_name, step_totals in stats.iteritems():
+ step_totals[0] /= float(len(builds))
+ return suppressions
+
+
+def FetchSuppressions(waterfall_url, builder_type, verbose=False, count=3):
+ """ Fetch suppression stats for |builder_type| from last |count| builds. """
+ builder_category, step_type, log_parser = builder_type
+ # The last one is still building, so skip it.
+ select = '&'.join(['select=%d' % (-i-2) for i in range(count)])
+
+ suppressions = {}
+ builders = FindMatchingBuilders(waterfall_url, builder_category)
+
+ for builder in builders:
+ print >> sys.stderr, 'Fetching suppressions from "%s"' % (builder)
+ builds = Fetch('%s/json/builders/%s/builds?%s' % (waterfall_url, builder,
+ select))
+ builder_suppressions = FetchBuilderSuppressions(builds, step_type,
+ log_parser)
+ for name, builder_stats in builder_suppressions.iteritems():
+ suppressions.setdefault(name, []).append((builder, builder_stats))
+
+ # Output.
+ for name, stats in sorted(suppressions.items()):
+ if verbose:
+ print name
+ # Compute totals across builders.
+ global_avg = 0
+ for builder, builder_stats in sorted(stats):
+ if verbose:
+ print ' %s' % builder
+ for step_name, [avg_count, max_count] in builder_stats.iteritems():
+ if verbose:
+ print ' %-30s %8.1f %8d' % (step_name, avg_count, max_count)
+ global_avg += avg_count
+ global_avg /= float(len(stats))
+ if verbose:
+ print ' %30s %8.1f' % ('TOTAL', global_avg)
+ else:
+ print '%8.1f %s' % (global_avg, name)
+
+
+WATERFALL_URL = 'http://build.chromium.org/p/chromium.memory.fyi'
+
+BUILDER_TYPES = {
+ # type is (builder category, step type, log parser)
+ 'valgrind' : ('memory_tester', 'memory test', ParseMemoryTestLog),
+ 'heapcheck' : ('heapcheck_tester', 'heapcheck test', ParseHeapcheckTestLog),
+}
+
+if __name__ == '__main__':
+ import sys
+ import getopt # No argparse before python 2.6
+
+ count = 3
+ verbose = False
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], 'vc:')
+ for opt, val in optlist:
+ if opt == '-c':
+ count = int(val)
+ if opt == '-v':
+ verbose = True
+ builder_type = BUILDER_TYPES[args[0]]
+ except:
+ print >> sys.stderr, 'Usage: %s [-c build_count] [-v] (%s)' % (
+ sys.argv[0], '|'.join(BUILDER_TYPES.keys()))
+ sys.exit(2)
+
+ FetchSuppressions(WATERFALL_URL, builder_type, count=count, verbose=verbose)
« 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