| Index: tools/bisect-builds.py
|
| diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
|
| index 04b4689d4e42dd5935ff9fcb4b6b21bc11c7001f..e601e4792094263d96eb11369110f7e2e882ce69 100755
|
| --- a/tools/bisect-builds.py
|
| +++ b/tools/bisect-builds.py
|
| @@ -32,8 +32,10 @@ DEPS_FILE= 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d'
|
| WEBKIT_CHANGELOG_URL = 'http://trac.webkit.org/log/' \
|
| 'trunk/?rev=%d&stop_rev=%d&verbose=on&limit=10000'
|
|
|
| -DONE_MESSAGE = 'You are probably looking for a change made after ' \
|
| - '%s (known good), but no later than %s (first known bad).'
|
| +DONE_MESSAGE_GOOD_MIN = 'You are probably looking for a change made after %s ' \
|
| + '(known good), but no later than %s (first known bad).'
|
| +DONE_MESSAGE_GOOD_MAX = 'You are probably looking for a change made after %s ' \
|
| + '(known bad), but no later than %s (first known good).'
|
|
|
| ###############################################################################
|
|
|
| @@ -140,8 +142,7 @@ class PathContext(object):
|
|
|
| def ParseDirectoryIndex(self):
|
| """Parses the Google Storage directory listing into a list of revision
|
| - numbers. The range starts with self.good_revision and goes until
|
| - self.bad_revision."""
|
| + numbers."""
|
|
|
| def _FetchAndParse(url):
|
| """Fetches a URL and returns a 2-Tuple of ([revisions], next-marker). If
|
| @@ -198,8 +199,8 @@ class PathContext(object):
|
| """Gets the list of revision numbers between self.good_revision and
|
| self.bad_revision."""
|
| # Download the revlist and filter for just the range between good and bad.
|
| - minrev = self.good_revision
|
| - maxrev = self.bad_revision
|
| + minrev = min(self.good_revision, self.bad_revision)
|
| + maxrev = max(self.good_revision, self.bad_revision)
|
| revlist = map(int, self.ParseDirectoryIndex())
|
| revlist = [x for x in revlist if x >= int(minrev) and x <= int(maxrev)]
|
| revlist.sort()
|
| @@ -209,8 +210,8 @@ class PathContext(object):
|
| """Gets the list of official build numbers between self.good_revision and
|
| self.bad_revision."""
|
| # Download the revlist and filter for just the range between good and bad.
|
| - minrev = self.good_revision
|
| - maxrev = self.bad_revision
|
| + minrev = min(self.good_revision, self.bad_revision)
|
| + maxrev = max(self.good_revision, self.bad_revision)
|
| handle = urllib.urlopen(OFFICIAL_BASE_URL)
|
| dirindex = handle.read()
|
| handle.close()
|
| @@ -392,8 +393,8 @@ def Bisect(platform,
|
|
|
| @param platform Which build to download/run ('mac', 'win', 'linux64', etc.).
|
| @param official_builds Specify build type (Chromium or Official build).
|
| - @param good_rev Number/tag of the last known good revision.
|
| - @param bad_rev Number/tag of the first known bad revision.
|
| + @param good_rev Number/tag of the known good revision.
|
| + @param bad_rev Number/tag of the known bad revision.
|
| @param num_runs Number of times to run each build for asking good/bad.
|
| @param try_args A tuple of arguments to pass to the test application.
|
| @param profile The name of the user profile to run with.
|
| @@ -436,36 +437,41 @@ def Bisect(platform,
|
| msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist
|
| raise RuntimeError(msg)
|
|
|
| - print 'Bisecting range [%s, %s].' % (revlist[0], revlist[-1])
|
| -
|
| # Figure out our bookends and first pivot point; fetch the pivot revision.
|
| - good = 0
|
| - bad = len(revlist) - 1
|
| - pivot = bad / 2
|
| + minrev = 0
|
| + maxrev = len(revlist) - 1
|
| + pivot = maxrev / 2
|
| rev = revlist[pivot]
|
| zipfile = _GetDownloadPath(rev)
|
| - initial_fetch = DownloadJob(context, 'initial_fetch', rev, zipfile)
|
| - initial_fetch.Start()
|
| - initial_fetch.WaitFor()
|
| + fetch = DownloadJob(context, 'initial_fetch', rev, zipfile)
|
| + fetch.Start()
|
| + fetch.WaitFor()
|
|
|
| # Binary search time!
|
| - while zipfile and bad - good > 1:
|
| + while fetch and fetch.zipfile and maxrev - minrev > 1:
|
| + if bad_rev < good_rev:
|
| + min_str, max_str = "bad", "good"
|
| + else:
|
| + min_str, max_str = "good", "bad"
|
| + print 'Bisecting range [%s (%s), %s (%s)].' % (revlist[minrev], min_str, \
|
| + revlist[maxrev], max_str)
|
| +
|
| # Pre-fetch next two possible pivots
|
| # - down_pivot is the next revision to check if the current revision turns
|
| # out to be bad.
|
| # - up_pivot is the next revision to check if the current revision turns
|
| # out to be good.
|
| - down_pivot = int((pivot - good) / 2) + good
|
| + down_pivot = int((pivot - minrev) / 2) + minrev
|
| down_fetch = None
|
| - if down_pivot != pivot and down_pivot != good:
|
| + if down_pivot != pivot and down_pivot != minrev:
|
| down_rev = revlist[down_pivot]
|
| down_fetch = DownloadJob(context, 'down_fetch', down_rev,
|
| _GetDownloadPath(down_rev))
|
| down_fetch.Start()
|
|
|
| - up_pivot = int((bad - pivot) / 2) + pivot
|
| + up_pivot = int((maxrev - pivot) / 2) + pivot
|
| up_fetch = None
|
| - if up_pivot != pivot and up_pivot != bad:
|
| + if up_pivot != pivot and up_pivot != maxrev:
|
| up_rev = revlist[up_pivot]
|
| up_fetch = DownloadJob(context, 'up_fetch', up_rev,
|
| _GetDownloadPath(up_rev))
|
| @@ -478,43 +484,44 @@ def Bisect(platform,
|
| try:
|
| (status, stdout, stderr) = RunRevision(context,
|
| rev,
|
| - zipfile,
|
| + fetch.zipfile,
|
| profile,
|
| num_runs,
|
| try_args)
|
| except Exception, e:
|
| print >>sys.stderr, e
|
| - os.unlink(zipfile)
|
| - zipfile = None
|
| + fetch.Stop()
|
| + fetch = None
|
|
|
| # Call the evaluate function to see if the current revision is good or bad.
|
| # On that basis, kill one of the background downloads and complete the
|
| # other, as described in the comments above.
|
| try:
|
| answer = evaluate(rev, official_builds, status, stdout, stderr)
|
| - if answer == 'g':
|
| - good = pivot
|
| + if answer == 'g' and good_rev < bad_rev or \
|
| + answer == 'b' and bad_rev < good_rev:
|
| + minrev = pivot
|
| if down_fetch:
|
| down_fetch.Stop() # Kill the download of the older revision.
|
| if up_fetch:
|
| up_fetch.WaitFor()
|
| pivot = up_pivot
|
| - zipfile = up_fetch.zipfile
|
| - elif answer == 'b':
|
| - bad = pivot
|
| + fetch = up_fetch
|
| + elif answer == 'b' and good_rev < bad_rev or \
|
| + answer == 'g' and bad_rev < good_rev:
|
| + maxrev = pivot
|
| if up_fetch:
|
| up_fetch.Stop() # Kill the download of the newer revision.
|
| if down_fetch:
|
| down_fetch.WaitFor()
|
| pivot = down_pivot
|
| - zipfile = down_fetch.zipfile
|
| + fetch = down_fetch
|
| elif answer == 'u':
|
| # Nuke the revision from the revlist and choose a new pivot.
|
| revlist.pop(pivot)
|
| - bad -= 1 # Assumes bad >= pivot.
|
| + maxrev -= 1 # Assumes maxrev >= pivot.
|
|
|
| - fetch = None
|
| - if bad - good > 1:
|
| + if maxrev - minrev > 1:
|
| # Alternate between using down_pivot or up_pivot for the new pivot
|
| # point, without affecting the range. Do this instead of setting the
|
| # pivot to the midpoint of the new range because adjacent revisions
|
| @@ -551,7 +558,7 @@ def Bisect(platform,
|
|
|
| rev = revlist[pivot]
|
|
|
| - return (revlist[good], revlist[bad])
|
| + return (revlist[minrev], revlist[maxrev])
|
|
|
|
|
| def GetWebKitRevisionForChromiumRevision(rev):
|
| @@ -638,43 +645,38 @@ def main():
|
| good_rev = int(good_rev)
|
| bad_rev = int(bad_rev)
|
|
|
| - if good_rev > bad_rev:
|
| - print ('The good revision (%s) must precede the bad revision (%s).\n' %
|
| - (good_rev, bad_rev))
|
| - parser.print_help()
|
| - return 1
|
| -
|
| if opts.times < 1:
|
| print('Number of times to run (%d) must be greater than or equal to 1.' %
|
| opts.times)
|
| parser.print_help()
|
| return 1
|
|
|
| - (last_known_good_rev, first_known_bad_rev) = Bisect(
|
| + (min_chromium_rev, max_chromium_rev) = Bisect(
|
| opts.archive, opts.official_builds, good_rev, bad_rev, opts.times, args,
|
| opts.profile)
|
|
|
| # Get corresponding webkit revisions.
|
| try:
|
| - last_known_good_webkit_rev = GetWebKitRevisionForChromiumRevision(
|
| - last_known_good_rev)
|
| - first_known_bad_webkit_rev = GetWebKitRevisionForChromiumRevision(
|
| - first_known_bad_rev)
|
| + min_webkit_rev = GetWebKitRevisionForChromiumRevision(min_chromium_rev)
|
| + max_webkit_rev = GetWebKitRevisionForChromiumRevision(max_chromium_rev)
|
| except Exception, e:
|
| # Silently ignore the failure.
|
| - last_known_good_webkit_rev, first_known_bad_webkit_rev = 0, 0
|
| + min_webkit_rev, max_webkit_rev = 0, 0
|
|
|
| # We're done. Let the user know the results in an official manner.
|
| - print DONE_MESSAGE % (str(last_known_good_rev), str(first_known_bad_rev))
|
| - if last_known_good_webkit_rev != first_known_bad_webkit_rev:
|
| + if good_rev > bad_rev:
|
| + print DONE_MESSAGE_GOOD_MAX % (str(min_chromium_rev), str(max_chromium_rev))
|
| + else:
|
| + print DONE_MESSAGE_GOOD_MIN % (str(min_chromium_rev), str(max_chromium_rev))
|
| +
|
| + if min_webkit_rev != max_webkit_rev:
|
| print 'WEBKIT CHANGELOG URL:'
|
| - print ' ' + WEBKIT_CHANGELOG_URL % (first_known_bad_webkit_rev,
|
| - last_known_good_webkit_rev)
|
| + print ' ' + WEBKIT_CHANGELOG_URL % (max_webkit_rev, min_webkit_rev)
|
| print 'CHANGELOG URL:'
|
| if opts.official_builds:
|
| - print OFFICIAL_CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)
|
| + print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev)
|
| else:
|
| - print ' ' + CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)
|
| + print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev)
|
|
|
| if __name__ == '__main__':
|
| sys.exit(main())
|
|
|