summaryrefslogtreecommitdiffstats
path: root/build/build-bisect.py
diff options
context:
space:
mode:
Diffstat (limited to 'build/build-bisect.py')
-rwxr-xr-xbuild/build-bisect.py283
1 files changed, 3 insertions, 280 deletions
diff --git a/build/build-bisect.py b/build/build-bisect.py
index 84807bc..33fcb89 100755
--- a/build/build-bisect.py
+++ b/build/build-bisect.py
@@ -3,286 +3,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""Snapshot Build Bisect Tool
-
-This script bisects a snapshot archive using binary search. It starts at
-a bad revision (it will try to guess HEAD) and asks for a last known-good
-revision. It will then binary search across this revision range by downloading,
-unzipping, and opening Chromium for you. After testing the specific revision,
-it will ask you whether it is good or bad before continuing the search.
-"""
-
-# Base URL to download snapshots from.
-BUILD_BASE_URL = 'http://build.chromium.org/buildbot/snapshots/'
-
-# The type (platform) of the build archive. This is what's passed in to the
-# '-a/--archive' option.
-BUILD_ARCHIVE_TYPE = ''
-
-# The selected archive to bisect.
-BUILD_ARCHIVE_DIR = ''
-
-# The location of the builds.
-BUILD_ARCHIVE_URL = '/%d/'
-
-# Name of the build archive.
-BUILD_ZIP_NAME = ''
-
-# Directory name inside the archive.
-BUILD_DIR_NAME = ''
-
-# Name of the executable.
-BUILD_EXE_NAME = ''
-
-# URL to the ViewVC commit page.
-BUILD_VIEWVC_URL = 'http://src.chromium.org/viewvc/chrome?view=rev&revision=%d'
-
-# Changelogs URL
-CHANGELOG_URL = 'http://build.chromium.org/buildbot/' \
- 'perf/dashboard/ui/changelog.html?url=/trunk/src&range=%d:%d'
-
-###############################################################################
-
-import math
-import optparse
-import os
-import pipes
-import re
-import shutil
import sys
-import tempfile
-import urllib
-import zipfile
-
-
-def UnzipFilenameToDir(filename, dir):
- """Unzip |filename| to directory |dir|."""
- zf = zipfile.ZipFile(filename)
- # Make base.
- pushd = os.getcwd()
- try:
- if not os.path.isdir(dir):
- os.mkdir(dir)
- os.chdir(dir)
- # Extract files.
- for info in zf.infolist():
- name = info.filename
- if name.endswith('/'): # dir
- if not os.path.isdir(name):
- os.makedirs(name)
- else: # file
- dir = os.path.dirname(name)
- if not os.path.isdir(dir):
- os.makedirs(dir)
- out = open(name, 'wb')
- out.write(zf.read(name))
- out.close()
- # Set permissions. Permission info in external_attr is shifted 16 bits.
- os.chmod(name, info.external_attr >> 16L)
- os.chdir(pushd)
- except Exception, e:
- print >>sys.stderr, e
- sys.exit(1)
-
-
-def SetArchiveVars(archive):
- """Set a bunch of global variables appropriate for the specified archive."""
- global BUILD_ARCHIVE_TYPE
- global BUILD_ARCHIVE_DIR
- global BUILD_ZIP_NAME
- global BUILD_DIR_NAME
- global BUILD_EXE_NAME
- global BUILD_BASE_URL
-
- BUILD_ARCHIVE_TYPE = archive
- BUILD_ARCHIVE_DIR = 'chromium-rel-' + BUILD_ARCHIVE_TYPE
-
- if BUILD_ARCHIVE_TYPE in ('linux', 'linux-64', 'linux-chromiumos'):
- BUILD_ZIP_NAME = 'chrome-linux.zip'
- BUILD_DIR_NAME = 'chrome-linux'
- BUILD_EXE_NAME = 'chrome'
- elif BUILD_ARCHIVE_TYPE in ('mac'):
- BUILD_ZIP_NAME = 'chrome-mac.zip'
- BUILD_DIR_NAME = 'chrome-mac'
- BUILD_EXE_NAME = 'Chromium.app/Contents/MacOS/Chromium'
- elif BUILD_ARCHIVE_TYPE in ('xp'):
- BUILD_ZIP_NAME = 'chrome-win32.zip'
- BUILD_DIR_NAME = 'chrome-win32'
- BUILD_EXE_NAME = 'chrome.exe'
-
- BUILD_BASE_URL += BUILD_ARCHIVE_DIR
-
-def ParseDirectoryIndex(url):
- """Parses the HTML directory listing into a list of revision numbers."""
- handle = urllib.urlopen(url)
- dirindex = handle.read()
- handle.close()
- return re.findall(r'<a href="([0-9]*)/">\1/</a>', dirindex)
-
-def GetRevList(good, bad):
- """Gets the list of revision numbers between |good| and |bad|."""
- # Download the main revlist.
- revlist = ParseDirectoryIndex(BUILD_BASE_URL)
- revlist = map(int, revlist)
- revlist = filter(lambda r: range(good, bad).__contains__(int(r)), revlist)
- revlist.sort()
- return revlist
-
-def TryRevision(rev, profile, args):
- """Downloads revision |rev|, unzips it, and opens it for the user to test.
- |profile| is the profile to use."""
- # Do this in a temp dir so we don't collide with user files.
- cwd = os.getcwd()
- tempdir = tempfile.mkdtemp(prefix='bisect_tmp')
- os.chdir(tempdir)
-
- # Download the file.
- download_url = BUILD_BASE_URL + (BUILD_ARCHIVE_URL % rev) + BUILD_ZIP_NAME
- try:
- print 'Fetching ' + download_url
- urllib.urlretrieve(download_url, BUILD_ZIP_NAME)
- except Exception, e:
- print('Could not retrieve the download. Sorry.')
- sys.exit(-1)
-
- # Unzip the file.
- print 'Unziping ...'
- UnzipFilenameToDir(BUILD_ZIP_NAME, os.curdir)
-
- # Tell the system to open the app.
- args = ['--user-data-dir=%s' % profile] + args
- flags = ' '.join(map(pipes.quote, args))
- exe = os.path.join(os.getcwd(), BUILD_DIR_NAME, BUILD_EXE_NAME)
- cmd = '%s %s' % (exe, flags)
- print 'Running %s' % cmd
- os.system(cmd)
-
- os.chdir(cwd)
- print 'Cleaning temp dir ...'
- try:
- shutil.rmtree(tempdir, True)
- except Exception, e:
- pass
-
-
-def AskIsGoodBuild(rev):
- """Ask the user whether build |rev| is good or bad."""
- # Loop until we get a response that we can parse.
- while True:
- response = raw_input('\nBuild %d is [(g)ood/(b)ad]: ' % int(rev))
- if response and response in ('g', 'b'):
- return response == 'g'
-
-def main():
- usage = ('%prog [options] [-- chromium-options]\n'
- 'Perform binary search on the snapshot builds.\n'
- '\n'
- 'Tip: add "-- --no-first-run" to bypass the first run prompts.')
- parser = optparse.OptionParser(usage=usage)
- # Strangely, the default help output doesn't include the choice list.
- choices = ['mac', 'xp', 'linux', 'linux-64', 'linux-chromiumos']
- parser.add_option('-a', '--archive',
- choices = choices,
- help = 'The buildbot archive to bisect [%s].' %
- '|'.join(choices))
- parser.add_option('-b', '--bad', type = 'int',
- help = 'The bad revision to bisect to.')
- parser.add_option('-g', '--good', type = 'int',
- help = 'The last known good revision to bisect from.')
- parser.add_option('-p', '--profile', '--user-data-dir', type = 'str',
- help = 'Profile to use; this will not reset every run. ' +
- 'Defaults to a clean profile.')
- (opts, args) = parser.parse_args()
-
- if opts.archive is None:
- print 'Error: missing required parameter: --archive'
- print
- parser.print_help()
- return 1
-
- if opts.bad and opts.good and (opts.good > opts.bad):
- print ('The good revision (%d) must precede the bad revision (%d).\n' %
- (opts.good, opts.bad))
- parser.print_help()
- return 1
-
- SetArchiveVars(opts.archive)
-
- # Pick a starting point, try to get HEAD for this.
- if opts.bad:
- bad_rev = opts.bad
- else:
- bad_rev = 0
- try:
- # Location of the latest build revision number
- BUILD_LATEST_URL = '%s/LATEST' % (BUILD_BASE_URL)
- nh = urllib.urlopen(BUILD_LATEST_URL)
- latest = int(nh.read())
- nh.close()
- bad_rev = raw_input('Bad revision [HEAD:%d]: ' % latest)
- if (bad_rev == ''):
- bad_rev = latest
- bad_rev = int(bad_rev)
- except Exception, e:
- print('Could not determine latest revision. This could be bad...')
- bad_rev = int(raw_input('Bad revision: '))
-
- # Find out when we were good.
- if opts.good:
- good_rev = opts.good
- else:
- good_rev = 0
- try:
- good_rev = int(raw_input('Last known good [0]: '))
- except Exception, e:
- pass
-
- # Get a list of revisions to bisect across.
- revlist = GetRevList(good_rev, bad_rev)
- if len(revlist) < 2: # Don't have enough builds to bisect
- print 'We don\'t have enough builds to bisect. revlist: %s' % revlist
- sys.exit(1)
-
- # If we don't have a |good_rev|, set it to be the first revision possible.
- if good_rev == 0:
- good_rev = revlist[0]
-
- # These are indexes of |revlist|.
- good = 0
- bad = len(revlist) - 1
- last_known_good_rev = revlist[good]
-
- # Binary search time!
- while good < bad:
- candidates = revlist[good:bad]
- num_poss = len(candidates)
- if num_poss > 10:
- print('%d candidates. %d tries left.' %
- (num_poss, round(math.log(num_poss, 2))))
- else:
- print('Candidates: %s' % revlist[good:bad])
-
- # Cut the problem in half...
- test = int((bad - good) / 2) + good
- test_rev = revlist[test]
-
- # Let the user give this rev a spin (in her own profile, if she wants).
- profile = opts.profile
- if not profile:
- profile = 'profile' # In a temp dir.
- TryRevision(test_rev, profile, args)
- if AskIsGoodBuild(test_rev):
- last_known_good_rev = revlist[good]
- good = test + 1
- else:
- bad = test
- # We're done. Let the user know the results in an official manner.
- print('You are probably looking for build %d.' % revlist[bad])
- print('CHANGELOG URL:')
- print(CHANGELOG_URL % (last_known_good_rev, revlist[bad]))
- print('Built at revision:')
- print(BUILD_VIEWVC_URL % revlist[bad])
+print "This script has been moved to tools/bisect-builds.py."
+print "Please update any docs you're working from!"
-if __name__ == '__main__':
- sys.exit(main())
+sys.exit(1)