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

Unified Diff: bisect-builds.py

Issue 10408045: Modified the bisect script to work for official Chrome builds. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/tools/
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: bisect-builds.py
===================================================================
--- bisect-builds.py (revision 129264)
+++ bisect-builds.py (working copy)
@@ -15,16 +15,21 @@
# The root URL for storage.
BASE_URL = 'http://commondatastorage.googleapis.com/chromium-browser-snapshots'
+# The root URL for official builds.
+OFFICIAL_BASE_URL = 'http://chrome4linux.mtv.corp.google.com/archives/'
Robert Sesek 2012/05/21 21:39:47 Do not end this with a / for consistency with the
Robert Sesek 2012/05/21 21:39:47 This instead of chrome-master2.mtv/official_builds
anantha 2012/05/22 22:55:39 Done.
anantha 2012/05/22 22:55:39 This location has files from chrome-master2.mtv an
+
# 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/f/chromium/' \
'perf/dashboard/ui/changelog.html?url=/trunk/src&range=%d:%d'
+# Official Changelogs URL.
+OFFICIAL_CHANGELOG_URL = 'http://omahaproxy.appspot.com/'\
+ 'changelog?old_version=%s&new_version=%s'
# DEPS file URL.
DEPS_FILE= 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d'
-
# WebKit Changelogs URL.
WEBKIT_CHANGELOG_URL = 'http://trac.webkit.org/log/' \
'trunk/?rev=%d&stop_rev=%d&verbose=on'
@@ -42,6 +47,7 @@
import tempfile
import threading
import urllib
+from distutils.version import LooseVersion
from xml.etree import ElementTree
import zipfile
@@ -49,12 +55,13 @@
class PathContext(object):
"""A PathContext is used to carry the information used to construct URLs and
paths when dealing with the storage server and archives."""
- def __init__(self, platform, good_revision, bad_revision):
+ def __init__(self, platform, good_revision, bad_revision, build_type):
super(PathContext, self).__init__()
# Store off the input parameters.
self.platform = platform # What's passed in to the '-a/--archive' option.
self.good_revision = good_revision
self.bad_revision = bad_revision
+ self.build_type = build_type
Robert Sesek 2012/05/21 21:39:47 self.is_official per comment in Main()
anantha 2012/05/22 22:55:39 Done.
# The name of the ZIP file in a revision directory on the server.
self.archive_name = None
@@ -65,19 +72,41 @@
# _binary_name = The name of the executable to run.
if self.platform == 'linux' or self.platform == 'linux64':
self._listing_platform_dir = 'Linux/'
- self.archive_name = 'chrome-linux.zip'
- self._archive_extract_dir = 'chrome-linux'
+ if build_type != 'Official':
Robert Sesek 2012/05/21 21:39:47 This is getting messy. Beak this down into two top
anantha 2012/05/22 22:55:39 Done.
+ self._archive_extract_dir = 'chrome-linux'
+ else:
+ self._archive_extract_dir = 'chrome-lucid64bit'
self._binary_name = 'chrome'
# Linux and x64 share all the same path data except for the archive dir.
if self.platform == 'linux64':
- self._listing_platform_dir = 'Linux_x64/'
+ if build_type != 'Official':
+ self.archive_name = 'chrome-linux.zip'
+ self._listing_platform_dir = 'Linux_x64/'
+ else:
+ self.archive_name = 'chrome-lucid64bit.zip'
+ self._listing_platform_dir = 'lucid64bit/'
+ else:
+ if build_type != 'Official':
+ self.archive_name = 'chrome-linux.zip'
+ self._listing_platform_dir = 'Linux_x32/'
+ else:
+ self.archive_name = 'chrome-lucid32bit.zip'
+ self._listing_platform_dir = 'lucid32bit/'
elif self.platform == 'mac':
- self._listing_platform_dir = 'Mac/'
+ if build_type != 'Official':
+ self._listing_platform_dir = 'Mac/'
+ self._binary_name = 'Chromium.app/Contents/MacOS/Chromium'
+ else:
+ self._listing_platform_dir = 'mac/'
+ self._binary_name = 'Google Chrome.app/Contents/MacOS/Google Chrome'
self.archive_name = 'chrome-mac.zip'
self._archive_extract_dir = 'chrome-mac'
- self._binary_name = 'Chromium.app/Contents/MacOS/Chromium'
+
elif self.platform == 'win':
- self._listing_platform_dir = 'Win/'
+ if build_type != 'Official':
+ self._listing_platform_dir = 'Win/'
+ else:
+ self._listing_platform_dir = 'win/'
self.archive_name = 'chrome-win32.zip'
self._archive_extract_dir = 'chrome-win32'
self._binary_name = 'chrome.exe'
@@ -94,8 +123,13 @@
def GetDownloadURL(self, revision):
"""Gets the download URL for a build archive of a specific revision."""
- return "%s/%s%d/%s" % (
- BASE_URL, self._listing_platform_dir, revision, self.archive_name)
+ if self.build_type != 'Official':
+ return "%s/%s%d/%s" % (
+ BASE_URL, self._listing_platform_dir, revision, self.archive_name)
+ else:
+ return "%s%s/%s%s" % (
+ OFFICIAL_BASE_URL, revision, self._listing_platform_dir,
+ self.archive_name)
def GetLastChangeURL(self):
"""Returns a URL to the LAST_CHANGE file."""
@@ -150,19 +184,23 @@
except ValueError:
pass
return (revisions, next_marker)
+ if self.build_type != 'Official':
Robert Sesek 2012/05/21 21:39:47 This logic doesn't belong in this function, as it'
anantha 2012/05/22 22:55:39 Correct. I forgot to clean this up.
+ # Fetch the first list of revisions.
+ (revisions, next_marker) = _FetchAndParse(self.GetListingURL())
- # Fetch the first list of revisions.
- (revisions, next_marker) = _FetchAndParse(self.GetListingURL())
+ # If the result list was truncated, refetch with the next marker. Do this
+ # until an entire directory listing is done.
+ while next_marker:
+ next_url = self.GetListingURL(next_marker)
+ (new_revisions, next_marker) = _FetchAndParse(next_url)
+ revisions.extend(new_revisions)
+ return revisions
+ else:
+ handle = urllib.urlopen(OFFICIAL_BASE_URL)
+ dirindex = handle.read()
+ handle.close()
+ return re.findall(r'<a href="([0-9][0-9].*)/">', dirindex)
- # If the result list was truncated, refetch with the next marker. Do this
- # until an entire directory listing is done.
- while next_marker:
- next_url = self.GetListingURL(next_marker)
- (new_revisions, next_marker) = _FetchAndParse(next_url)
- revisions.extend(new_revisions)
-
- return revisions
-
def GetRevList(self):
"""Gets the list of revision numbers between self.good_revision and
self.bad_revision."""
@@ -170,11 +208,42 @@
minrev = self.good_revision
maxrev = self.bad_revision
revlist = map(int, self.ParseDirectoryIndex())
- revlist = [x for x in revlist if x >= minrev and x <= maxrev]
+ revlist = [x for x in revlist if x >= int(minrev) and x <= int(maxrev)]
revlist.sort()
return revlist
+ def GetBuildsList(self):
Robert Sesek 2012/05/21 21:39:47 naming: GetOfficialBuildsList
anantha 2012/05/22 22:55:39 Done.
+ """Gets the list of revision numbers between self.good_revision and
Robert Sesek 2012/05/21 21:39:47 nit: this isn't returning revision numbers, this i
anantha 2012/05/22 22:55:39 Done.
anantha 2012/05/22 22:55:39 Done.
+ self.bad_revision."""
+ # Download the revlist and filter for just the range between good and bad.
+ minrev = self.good_revision
+ maxrev = self.bad_revision
+ handle = urllib.urlopen(OFFICIAL_BASE_URL)
+ dirindex = handle.read()
+ handle.close()
+ build_numbers = re.findall(r'<a href="([0-9][0-9].*)/">', dirindex)
Robert Sesek 2012/05/21 21:39:47 This logic is duplicated above.
anantha 2012/05/22 22:55:39 Removed from above.
+ final_list = []
+ start_index = '0'
+ end_index = '0'
+ i = 0
+ parsed_build_numbers = [LooseVersion(x) for x in build_numbers]
+ for build_number in sorted(parsed_build_numbers):
+ path = OFFICIAL_BASE_URL + str(build_number) + '/' + \
Robert Sesek 2012/05/21 21:39:47 nit: should be indented 2
anantha 2012/05/22 22:55:39 Done.
+ self._listing_platform_dir + self.archive_name
+ i = i+1
+ try:
+ connection = urllib.urlopen(path)
Robert Sesek 2012/05/21 21:39:47 nit: over-indented
anantha 2012/05/22 22:55:39 Done.
anantha 2012/05/22 22:55:39 Done.
+ connection.close()
+ final_list.append(str(build_number))
+ if str(build_number) == minrev:
+ start_index = i
+ if str(build_number) == maxrev:
+ end_index = i
+ except urllib.HTTPError, e:
+ print e.getcode()
+ return final_list[start_index:end_index]
+
def UnzipFilenameToDir(filename, dir):
"""Unzip |filename| to directory |dir|."""
cwd = os.getcwd()
@@ -220,7 +289,10 @@
"""
def ReportHook(blocknum, blocksize, totalsize):
if quit_event and quit_event.isSet():
- raise RuntimeError("Aborting download of revision %d" % rev)
+ if context.build_type != 'Official':
Robert Sesek 2012/05/21 21:39:47 Don't do this. Just cast rev to a string and forma
anantha 2012/05/22 22:55:39 Done.
+ raise RuntimeError("Aborting download of revision %d" % rev)
+ else:
+ raise RuntimeError("Aborting download of revision %s" % rev)
if progress_event and progress_event.isSet():
size = blocknum * blocksize
if totalsize == -1: # Total size not known.
@@ -235,16 +307,17 @@
download_url = context.GetDownloadURL(rev)
try:
- urllib.urlretrieve(download_url, filename, ReportHook)
- if progress_event and progress_event.isSet():
Robert Sesek 2012/05/21 21:39:47 Why'd you remove this?
- print
+ urllib.urlretrieve(download_url, filename, ReportHook)
except RuntimeError, e:
- pass
+ pass
Robert Sesek 2012/05/21 21:39:47 nit: over-indented
anantha 2012/05/22 22:55:39 Done.
def RunRevision(context, revision, zipfile, profile, num_runs, args):
"""Given a zipped revision, unzip it and run the test."""
- print "Trying revision %d..." % revision
+ if context.build_type != 'Official':
+ print "Trying revision %d..." % revision
+ else:
+ print "Trying revision %s..." % revision
# Create a temp directory and unzip the revision into it.
cwd = os.getcwd()
@@ -252,8 +325,19 @@
UnzipFilenameToDir(zipfile, tempdir)
os.chdir(tempdir)
- # Run the build as many times as specified.
- testargs = [context.GetLaunchPath(), '--user-data-dir=%s' % profile] + args
+ if context.platform == 'linux' or context.platform == 'linux64':
+ if context.build_type == 'Official':
+ # Run the build as many times as specified.
+ testargs = [context.GetLaunchPath(), '--no-sandbox',
Robert Sesek 2012/05/21 21:39:47 Why this? I think this should just be done manuall
anantha 2012/05/22 22:55:39 Looks like we need root permission to setup the sa
+ '--user-data-dir=%s' % profile] + args
+ else:
+ # Run the build as many times as specified.
+ testargs = [context.GetLaunchPath(),
+ '--user-data-dir=%s' % profile] + args
+ else:
+ # Run the build as many times as specified.
+ testargs = [context.GetLaunchPath(), '--user-data-dir=%s' % profile] + args
+
for i in range(0, num_runs):
subproc = subprocess.Popen(testargs,
bufsize=-1,
@@ -270,18 +354,21 @@
return (subproc.returncode, stdout, stderr)
-def AskIsGoodBuild(rev, status, stdout, stderr):
+def AskIsGoodBuild(rev, build_type, status, stdout, stderr):
"""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('Revision %d is [(g)ood/(b)ad/(q)uit]: ' % int(rev))
+ if build_type != 'Official':
+ response = raw_input('Revision %d is [(g)ood/(b)ad/(q)uit]: ' % int(rev))
+ else:
+ response = raw_input('Revision %s is [(g)ood/(b)ad/(q)uit]: ' % str(rev))
if response and response in ('g', 'b'):
return response == 'g'
if response and response == 'q':
raise SystemExit()
-def Bisect(platform,
+def Bisect(platform, build_type,
Robert Sesek 2012/05/21 21:39:47 nit: goes on its own line, and the parameter also
anantha 2012/05/22 22:55:39 Done.
good_rev=0,
bad_rev=0,
num_runs=1,
@@ -318,16 +405,21 @@
if not profile:
profile = 'profile'
- context = PathContext(platform, good_rev, bad_rev)
+ context = PathContext(platform, good_rev, bad_rev, build_type)
cwd = os.getcwd()
- _GetDownloadPath = lambda rev: os.path.join(cwd,
- '%d-%s' % (rev, context.archive_name))
+
print "Downloading list of known revisions..."
+ if build_type != 'Official':
+ _GetDownloadPath = lambda rev: os.path.join(cwd,
+ '%d-%s' % (rev, context.archive_name))
+ revlist = context.GetRevList()
+ else:
+ _GetDownloadPath = lambda rev: os.path.join(cwd,
+ '%s-%s' % (rev, context.archive_name))
+ revlist = context.GetBuildsList()
- revlist = context.GetRevList()
-
# Get a list of revisions to bisect across.
if len(revlist) < 2: # Don't have enough builds to bisect.
msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist
@@ -341,7 +433,10 @@
zipfile = _GetDownloadPath(rev)
progress_event = threading.Event()
progress_event.set()
- print "Downloading revision %d..." % rev
+ if build_type != 'Official':
+ print "Downloading revision %d..." % rev
+ else:
+ print "Downloading revision %s..." % rev
FetchRevision(context, rev, zipfile,
quit_event=None, progress_event=progress_event)
@@ -400,14 +495,17 @@
# On that basis, kill one of the background downloads and complete the
# other, as described in the comments above.
try:
- if predicate(rev, status, stdout, stderr):
+ if predicate(rev, build_type, status, stdout, stderr):
good = pivot
if down_thread:
down_quit_event.set() # Kill the download of older revision.
down_thread.join()
os.unlink(down_zipfile)
if up_thread:
- print "Downloading revision %d..." % up_rev
+ if build_type == 'Official':
+ print "Downloading revision %s..." % up_rev
+ else:
+ print "Downloading revision %d..." % up_rev
up_progress_event.set() # Display progress of download.
up_thread.join() # Wait for newer revision to finish downloading.
pivot = up_pivot
@@ -419,7 +517,10 @@
up_thread.join()
os.unlink(up_zipfile)
if down_thread:
- print "Downloading revision %d..." % down_rev
+ if build_type == 'Official':
+ print "Downloading revision %s..." % down_rev
+ else:
+ print "Downloading revision %d..." % down_rev
down_progress_event.set() # Display progress of download.
down_thread.join() # Wait for older revision to finish downloading.
pivot = down_pivot
@@ -466,9 +567,11 @@
choices = choices,
help = 'The buildbot archive to bisect [%s].' %
'|'.join(choices))
- parser.add_option('-b', '--bad', type = 'int',
+ parser.add_option('-o', '--build_type', type = 'str',
Robert Sesek 2012/05/21 21:39:47 This should just be type bool and make the flag --
anantha 2012/05/22 22:55:39 Done.
+ help = 'Specify if it is a chromium or official build.')
+ parser.add_option('-b', '--bad', type = 'str',
help = 'The bad revision to bisect to.')
- parser.add_option('-g', '--good', type = 'int',
+ parser.add_option('-g', '--good', type = 'str',
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. ' +
@@ -492,8 +595,15 @@
return 1
# Create the context. Initialize 0 for the revisions as they are set below.
- context = PathContext(opts.archive, 0, 0)
+ context = PathContext(opts.archive, 0, 0, opts.build_type)
+ if opts.build_type == 'Official':
+ if opts.bad is None:
Robert Sesek 2012/05/21 21:39:47 Join this and the previous line with an |and|
anantha 2012/05/22 22:55:39 Done.
+ print ' Need a bad build range'
Robert Sesek 2012/05/21 21:39:47 print >>sys.stderr, 'Needs...'
anantha 2012/05/22 22:55:39 Done.
Robert Sesek 2012/05/23 21:24:00 Not done. Also, maybe change the error to "Bisecti
+ print
+ parser.print_help()
+ return 1
+
# Pick a starting point, try to get HEAD for this.
if opts.bad:
bad_rev = opts.bad
@@ -529,7 +639,8 @@
return 1
(last_known_good_rev, first_known_bad_rev) = Bisect(
- opts.archive, good_rev, bad_rev, opts.times, args, opts.profile)
+ opts.archive, opts.build_type, good_rev, bad_rev, opts.times, args,
+ opts.profile)
# Get corresponding webkit revisions.
try:
@@ -542,16 +653,23 @@
last_known_good_webkit_rev, first_known_bad_webkit_rev = 0, 0
# We're done. Let the user know the results in an official manner.
- print('You are probably looking for build %d.' % first_known_bad_rev)
+ if opts.build_type != 'Official':
+ print('You are probably looking for build %d.' % first_known_bad_rev)
+ else:
+ print('You are probably looking for build %s.' % first_known_bad_rev)
+
if last_known_good_webkit_rev != first_known_bad_webkit_rev:
print 'WEBKIT CHANGELOG URL:'
print WEBKIT_CHANGELOG_URL % (first_known_bad_webkit_rev,
last_known_good_webkit_rev)
print 'CHANGELOG URL:'
- print CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)
- print 'Built at revision:'
- print BUILD_VIEWVC_URL % first_known_bad_rev
+ if opts.build_type == 'Official':
+ print OFFICIAL_CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)
+ else:
+ print CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)
+ print 'Built at revision:'
+ print BUILD_VIEWVC_URL % first_known_bad_rev
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(main())
Robert Sesek 2012/05/21 21:39:47 What's this change?
anantha 2012/05/22 22:55:39 I somehow ended up adding two spaces. So deleted t
« 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