Index: tools/bisect-builds.py |
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py |
index 701682027f3b55f0d59df7e08e375fac90d06265..764f1f4333a3a5db5facf50b0fe7e1549a46d750 100644 |
--- a/tools/bisect-builds.py |
+++ b/tools/bisect-builds.py |
@@ -14,6 +14,7 @@ it will ask you whether it is good or bad before continuing the search. |
# The root URL for storage. |
BASE_URL = 'http://commondatastorage.googleapis.com/chromium-browser-snapshots' |
+BASE_URL_RECENT = 'http://build.chromium.org/f/chromium/snapshots' |
# URL to the ViewVC commit page. |
BUILD_VIEWVC_URL = 'http://src.chromium.org/viewvc/chrome?view=rev&revision=%d' |
@@ -39,12 +40,13 @@ import zipfile |
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, use_recent): |
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.use_recent = use_recent |
# The name of the ZIP file in a revision directory on the server. |
self.archive_name = None |
@@ -82,10 +84,19 @@ class PathContext(object): |
return BASE_URL + '/?delimiter=/&prefix=' + self._listing_platform_dir + \ |
marker_param |
+ def GetListingURLRecent(self): |
+ """Returns the URL for a directory listing of recent builds.""" |
+ return BASE_URL_RECENT + '/' + self._listing_platform_dir |
+ |
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.use_recent: |
+ return "%s/%s%d/%s" % ( |
+ BASE_URL_RECENT, self._listing_platform_dir, revision, |
+ self.archive_name) |
+ else: |
+ return "%s/%s%d/%s" % ( |
+ BASE_URL, self._listing_platform_dir, revision, self.archive_name) |
def GetLastChangeURL(self): |
"""Returns a URL to the LAST_CHANGE file.""" |
@@ -182,13 +193,27 @@ def ParseDirectoryIndex(context): |
return revisions |
Robert Sesek
2011/07/20 21:29:17
nit: two blank lines between top-level definitions
jbates
2011/07/20 21:32:10
Done.
|
+def ParseDirectoryIndexRecent(context): |
+ """Parses the recent builds directory listing into a list of revision |
+ numbers.""" |
+ handle = urllib.urlopen(context.GetListingURLRecent()) |
+ document = handle.read() |
+ |
+ # Looking for: <a href="92976/">92976/</a> |
+ return re.findall(r"<a href=\"(\d+)/\">\1/</a>", document) |
+ |
def GetRevList(context): |
"""Gets the list of revision numbers between |good_revision| and |
|bad_revision| of the |context|.""" |
# Download the revlist and filter for just the range between good and bad. |
rev_range = range(context.good_revision, context.bad_revision) |
- revlist = map(int, ParseDirectoryIndex(context)) |
+ revisions = [] |
+ if context.use_recent: |
+ revisions = ParseDirectoryIndexRecent(context) |
+ else: |
+ revisions = ParseDirectoryIndex(context) |
+ revlist = map(int, revisions) |
revlist = filter(lambda r: r in rev_range, revlist) |
revlist.sort() |
return revlist |
@@ -219,6 +244,8 @@ def TryRevision(context, rev, profile, args): |
print 'Fetching ' + download_url |
urllib.urlretrieve(download_url, context.archive_name, _ReportHook) |
+ # Throw an exception if the download was less than 1000 bytes. |
+ if os.path.getsize(context.archive_name) < 1000: raise Exception() |
except Exception, e: |
print('Could not retrieve the download. Sorry.') |
sys.exit(-1) |
@@ -251,9 +278,7 @@ def AskIsGoodBuild(rev): |
return response == 'g' |
-def Bisect(good, |
- bad, |
- revlist, |
+def Bisect(revlist, |
context, |
try_args=(), |
profile='profile', |
@@ -261,8 +286,6 @@ def Bisect(good, |
"""Tries to find the exact commit where a regression was introduced by |
running a binary search on all archived builds in a given revision range. |
- @param good The index in revlist of the last known good revision. |
- @param bad The index in revlist of the first known bad revision. |
@param revlist A list of chromium revision numbers to check. |
@param context A PathContext object. |
@param try_args A tuple of arguments to pass to the predicate function. |
@@ -271,6 +294,8 @@ def Bisect(good, |
chromium revision is good. |
""" |
+ good = 0 |
+ bad = len(revlist) - 1 |
last_known_good_rev = revlist[good] |
first_known_bad_rev = revlist[bad] |
@@ -291,7 +316,7 @@ def Bisect(good, |
# Let the user give this rev a spin (in her own profile, if she wants). |
TryRevision(context, test_rev, profile, try_args) |
if predicate(test_rev): |
- last_known_good_rev = revlist[good] |
+ last_known_good_rev = test_rev |
good = test + 1 |
else: |
bad = test |
@@ -319,6 +344,12 @@ def main(): |
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.', default = 'profile') |
+ parser.add_option('-r', '--recent', |
+ dest = "recent", |
+ default = False, |
+ action = "store_true", |
+ help = 'Use recent builds from about the last 2 months ' + |
+ 'for higher granularity bisecting.') |
(opts, args) = parser.parse_args() |
if opts.archive is None: |
@@ -334,7 +365,7 @@ def main(): |
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.recent) |
# Pick a starting point, try to get HEAD for this. |
if opts.bad: |
@@ -374,16 +405,8 @@ def main(): |
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, first_known_bad_rev) = Bisect( |
- good, bad, revlist, context, args, opts.profile) |
+ revlist, context, args, opts.profile) |
# 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) |