diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-05-09 14:22:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-05-09 15:11:45 +0000 |
commit | 2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch) | |
tree | e75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/tools/valgrind/scan-build.py | |
parent | a4f3d46271c57e8155ba912df46a05559d14726e (diff) | |
download | qtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz |
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion.
Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/tools/valgrind/scan-build.py')
-rwxr-xr-x | chromium/tools/valgrind/scan-build.py | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/chromium/tools/valgrind/scan-build.py b/chromium/tools/valgrind/scan-build.py new file mode 100755 index 00000000000..b117d1ea58a --- /dev/null +++ b/chromium/tools/valgrind/scan-build.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# Copyright (c) 2013 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. + +import argparse +import errno +import json +import os +import re +import sys +import urllib +import urllib2 + +# Where all the data lives. +ROOT_URL = "http://build.chromium.org/p/chromium.memory.fyi/builders" + +# TODO(groby) - support multi-line search from the command line. Useful when +# scanning for classes of failures, see below. +SEARCH_STRING = """<p class=\"failure result\"> +Failed memory test: content +</p>""" + +# Location of the log cache. +CACHE_DIR = "buildlogs.tmp" + +# If we don't find anything after searching |CUTOFF| logs, we're probably done. +CUTOFF = 200 + +def EnsurePath(path): + """Makes sure |path| does exist, tries to create it if it doesn't.""" + try: + os.makedirs(path) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + +class Cache(object): + def __init__(self, root_dir): + self._root_dir = os.path.abspath(root_dir) + + def _LocalName(self, name): + """If name is a relative path, treat it as relative to cache root. + If it is absolute and under cache root, pass it through. + Otherwise, raise error. + """ + if os.path.isabs(name): + assert os.path.commonprefix([name, self._root_dir]) == self._root_dir + else: + name = os.path.join(self._root_dir, name) + return name + + def _FetchLocal(self, local_name): + local_name = self._LocalName(local_name) + EnsurePath(os.path.dirname(local_name)) + if os.path.exists(local_name): + f = open(local_name, 'r') + return f.readlines(); + return None + + def _FetchRemote(self, remote_name): + try: + response = urllib2.urlopen(remote_name) + except: + print "Could not fetch", remote_name + raise + return response.read() + + def Update(self, local_name, remote_name): + local_name = self._LocalName(local_name) + EnsurePath(os.path.dirname(local_name)) + blob = self._FetchRemote(remote_name) + f = open(local_name, "w") + f.write(blob) + return blob.splitlines() + + def FetchData(self, local_name, remote_name): + result = self._FetchLocal(local_name) + if result: + return result + # If we get here, the local cache does not exist yet. Fetch, and store. + return self.Update(local_name, remote_name) + + +class Builder(object): + def __init__(self, waterfall, name): + self._name = name + self._waterfall = waterfall + + def Name(self): + return self._name + + def LatestBuild(self): + return self._waterfall.GetLatestBuild(self._name) + + def GetBuildPath(self, build_num): + return "%s/%s/builds/%d" % ( + self._waterfall._root_url, urllib.quote(self._name), build_num) + + def _FetchBuildLog(self, build_num): + local_build_path = "builds/%s" % self._name + local_build_file = os.path.join(local_build_path, "%d.log" % build_num) + return self._waterfall._cache.FetchData(local_build_file, + self.GetBuildPath(build_num)) + + def _CheckLog(self, build_num, tester): + log_lines = self._FetchBuildLog(build_num) + return any(tester(line) for line in log_lines) + + def ScanLogs(self, tester): + occurrences = [] + build = self.LatestBuild() + no_results = 0 + while build != 0 and no_results < CUTOFF: + if self._CheckLog(build, tester): + occurrences.append(build) + else: + no_results = no_results + 1 + build = build - 1 + return occurrences + + +class Waterfall(object): + def __init__(self, root_url, cache_dir): + self._root_url = root_url + self._builders = {} + self._top_revision = {} + self._cache = Cache(cache_dir) + + def Builders(self): + return self._builders.values() + + def Update(self): + self._cache.Update("builders", self._root_url) + self.FetchInfo() + + def FetchInfo(self): + if self._top_revision: + return + + html = self._cache.FetchData("builders", self._root_url) + + """ Search for both builders and latest build number in HTML + <td class="box"><a href="builders/<builder-name>"> identifies a builder + <a href="builders/<builder-name>/builds/<build-num>"> is the latest build. + """ + box_matcher = re.compile('.*a href[^>]*>([^<]*)\<') + build_matcher = re.compile('.*a href=\"builders/(.*)/builds/([0-9]+)\".*') + last_builder = "" + for line in html: + if 'a href="builders/' in line: + if 'td class="box"' in line: + last_builder = box_matcher.match(line).group(1) + self._builders[last_builder] = Builder(self, last_builder) + else: + result = build_matcher.match(line) + builder = result.group(1) + assert builder == urllib.quote(last_builder) + self._top_revision[last_builder] = int(result.group(2)) + + def GetLatestBuild(self, name): + self.FetchInfo() + assert self._top_revision + return self._top_revision[name] + + +class MultiLineChange(object): + def __init__(self, lines): + self._tracked_lines = lines + self._current = 0 + + def __call__(self, line): + """ Test a single line against multi-line change. + + If it matches the currently active line, advance one line. + If the current line is the last line, report a match. + """ + if self._tracked_lines[self._current] in line: + self._current = self._current + 1 + if self._current == len(self._tracked_lines): + self._current = 0 + return True + else: + self._current = 0 + return False + + +def main(argv): + # Create argument parser. + parser = argparse.ArgumentParser() + commands = parser.add_mutually_exclusive_group(required=True) + commands.add_argument("--update", action='store_true') + commands.add_argument("--find", metavar='search term') + parser.add_argument("--json", action='store_true', + help="Output in JSON format") + args = parser.parse_args() + + path = os.path.abspath(os.path.dirname(argv[0])) + cache_path = os.path.join(path, CACHE_DIR) + + fyi = Waterfall(ROOT_URL, cache_path) + + if args.update: + fyi.Update() + for builder in fyi.Builders(): + print "Updating", builder.Name() + builder.ScanLogs(lambda x:False) + + if args.find: + result = [] + tester = MultiLineChange(args.find.splitlines()) + fyi.FetchInfo() + + if not args.json: + print "SCANNING FOR ", args.find + for builder in fyi.Builders(): + if not args.json: + print "Scanning", builder.Name() + occurrences = builder.ScanLogs(tester) + if occurrences: + min_build = min(occurrences) + path = builder.GetBuildPath(min_build) + if args.json: + data = {} + data['builder'] = builder.Name() + data['first_affected'] = min_build + data['last_affected'] = max(occurrences) + data['last_build'] = builder.LatestBuild() + data['frequency'] = ((int(builder.LatestBuild()) - int(min_build)) / + len(occurrences)) + data['total'] = len(occurrences) + data['first_url'] = path + result.append(data) + else: + print "Earliest occurrence in build %d" % min_build + print "Latest occurrence in build %d" % max(occurrences) + print "Latest build: %d" % builder.LatestBuild() + print path + print "%d total" % len(occurrences) + if args.json: + json.dump(result, sys.stdout, indent=2, sort_keys=True) + +if __name__ == "__main__": + sys.exit(main(sys.argv)) + |