| Index: tools/bisect-builds.py
|
| diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
|
| index 701682027f3b55f0d59df7e08e375fac90d06265..11711cd02d28fae3ab37de268b9203fcf85e2a82 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."""
|
| @@ -183,12 +194,27 @@ def ParseDirectoryIndex(context):
|
| return revisions
|
|
|
|
|
| +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 +245,8 @@ def TryRevision(context, rev, profile, args):
|
| print 'Fetching ' + download_url
|
| urllib.urlretrieve(download_url, context.archive_name, _ReportHook)
|
| print
|
| + # 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 +279,7 @@ def AskIsGoodBuild(rev):
|
| return response == 'g'
|
|
|
|
|
| -def Bisect(good,
|
| - bad,
|
| - revlist,
|
| +def Bisect(revlist,
|
| context,
|
| try_args=(),
|
| profile='profile',
|
| @@ -261,8 +287,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 +295,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 +317,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 +345,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 +366,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 +406,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)
|
|
|