| OLD | NEW | 
|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be | 
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. | 
| 5 | 5 | 
| 6 """Snapshot Build Bisect Tool | 6 """Snapshot Build Bisect Tool | 
| 7 | 7 | 
| 8 This script bisects a snapshot archive using binary search. It starts at | 8 This script bisects a snapshot archive using binary search. It starts at | 
| 9 a bad revision (it will try to guess HEAD) and asks for a last known-good | 9 a bad revision (it will try to guess HEAD) and asks for a last known-good | 
| 10 revision. It will then binary search across this revision range by downloading, | 10 revision. It will then binary search across this revision range by downloading, | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 22 # Changelogs URL. | 22 # Changelogs URL. | 
| 23 CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ | 23 CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ | 
| 24                 'perf/dashboard/ui/changelog.html?' \ | 24                 'perf/dashboard/ui/changelog.html?' \ | 
| 25                 'url=/trunk/src&range=%d%%3A%d' | 25                 'url=/trunk/src&range=%d%%3A%d' | 
| 26 | 26 | 
| 27 # Official Changelogs URL. | 27 # Official Changelogs URL. | 
| 28 OFFICIAL_CHANGELOG_URL = 'http://omahaproxy.appspot.com/'\ | 28 OFFICIAL_CHANGELOG_URL = 'http://omahaproxy.appspot.com/'\ | 
| 29                          'changelog?old_version=%s&new_version=%s' | 29                          'changelog?old_version=%s&new_version=%s' | 
| 30 | 30 | 
| 31 # DEPS file URL. | 31 # DEPS file URL. | 
| 32 DEPS_FILE= 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d' | 32 DEPS_FILE = 'http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?revision=%d' | 
| 33 # Blink Changelogs URL. | 33 # Blink Changelogs URL. | 
| 34 BLINK_CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ | 34 BLINK_CHANGELOG_URL = 'http://build.chromium.org/f/chromium/' \ | 
| 35                       'perf/dashboard/ui/changelog_blink.html?' \ | 35                       'perf/dashboard/ui/changelog_blink.html?' \ | 
| 36                       'url=/trunk&range=%d%%3A%d' | 36                       'url=/trunk&range=%d%%3A%d' | 
| 37 | 37 | 
| 38 DONE_MESSAGE_GOOD_MIN = 'You are probably looking for a change made after %s ' \ | 38 DONE_MESSAGE_GOOD_MIN = 'You are probably looking for a change made after %s ' \ | 
| 39                         '(known good), but no later than %s (first known bad).' | 39                         '(known good), but no later than %s (first known bad).' | 
| 40 DONE_MESSAGE_GOOD_MAX = 'You are probably looking for a change made after %s ' \ | 40 DONE_MESSAGE_GOOD_MAX = 'You are probably looking for a change made after %s ' \ | 
| 41                         '(known bad), but no later than %s (first known good).' | 41                         '(known bad), but no later than %s (first known good).' | 
| 42 | 42 | 
| 43 ############################################################################### | 43 ############################################################################### | 
| 44 | 44 | 
| 45 import json | 45 import json | 
| 46 import math |  | 
| 47 import optparse | 46 import optparse | 
| 48 import os | 47 import os | 
| 49 import pipes |  | 
| 50 import re | 48 import re | 
| 51 import shlex | 49 import shlex | 
| 52 import shutil | 50 import shutil | 
| 53 import subprocess | 51 import subprocess | 
| 54 import sys | 52 import sys | 
| 55 import tempfile | 53 import tempfile | 
| 56 import threading | 54 import threading | 
| 57 import urllib | 55 import urllib | 
| 58 from distutils.version import LooseVersion | 56 from distutils.version import LooseVersion | 
| 59 from xml.etree import ElementTree | 57 from xml.etree import ElementTree | 
| 60 import zipfile | 58 import zipfile | 
| 61 | 59 | 
| 62 | 60 | 
| 63 class PathContext(object): | 61 class PathContext(object): | 
| 64   """A PathContext is used to carry the information used to construct URLs and | 62   """A PathContext is used to carry the information used to construct URLs and | 
| 65   paths when dealing with the storage server and archives.""" | 63   paths when dealing with the storage server and archives.""" | 
| 66   def __init__(self, base_url, platform, good_revision, bad_revision, | 64   def __init__(self, base_url, platform, good_revision, bad_revision, | 
| 67                is_official, is_aura): | 65                is_official, is_aura, flash_path = None): | 
| 68     super(PathContext, self).__init__() | 66     super(PathContext, self).__init__() | 
| 69     # Store off the input parameters. | 67     # Store off the input parameters. | 
| 70     self.base_url = base_url | 68     self.base_url = base_url | 
| 71     self.platform = platform  # What's passed in to the '-a/--archive' option. | 69     self.platform = platform  # What's passed in to the '-a/--archive' option. | 
| 72     self.good_revision = good_revision | 70     self.good_revision = good_revision | 
| 73     self.bad_revision = bad_revision | 71     self.bad_revision = bad_revision | 
| 74     self.is_official = is_official | 72     self.is_official = is_official | 
| 75     self.is_aura = is_aura | 73     self.is_aura = is_aura | 
|  | 74     self.flash_path = flash_path | 
| 76 | 75 | 
| 77     # The name of the ZIP file in a revision directory on the server. | 76     # The name of the ZIP file in a revision directory on the server. | 
| 78     self.archive_name = None | 77     self.archive_name = None | 
| 79 | 78 | 
| 80     # Set some internal members: | 79     # Set some internal members: | 
| 81     #   _listing_platform_dir = Directory that holds revisions. Ends with a '/'. | 80     #   _listing_platform_dir = Directory that holds revisions. Ends with a '/'. | 
| 82     #   _archive_extract_dir = Uncompressed directory in the archive_name file. | 81     #   _archive_extract_dir = Uncompressed directory in the archive_name file. | 
| 83     #   _binary_name = The name of the executable to run. | 82     #   _binary_name = The name of the executable to run. | 
| 84     if self.platform in ('linux', 'linux64', 'linux-arm'): | 83     if self.platform in ('linux', 'linux64', 'linux-arm'): | 
| 85       self._binary_name = 'chrome' | 84       self._binary_name = 'chrome' | 
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 280           # If we are bisecting only official builds (without --aura), | 279           # If we are bisecting only official builds (without --aura), | 
| 281           # we can not include builds which ends with '.1' or '.2' since | 280           # we can not include builds which ends with '.1' or '.2' since | 
| 282           # they have different folder hierarchy inside. | 281           # they have different folder hierarchy inside. | 
| 283           elif (not self.IsAuraBuild(str(build_number)) and | 282           elif (not self.IsAuraBuild(str(build_number)) and | 
| 284                 not self.IsASANBuild(str(build_number))): | 283                 not self.IsASANBuild(str(build_number))): | 
| 285             final_list.append(str(build_number)) | 284             final_list.append(str(build_number)) | 
| 286       except urllib.HTTPError, e: | 285       except urllib.HTTPError, e: | 
| 287         pass | 286         pass | 
| 288     return final_list | 287     return final_list | 
| 289 | 288 | 
| 290 def UnzipFilenameToDir(filename, dir): | 289 def UnzipFilenameToDir(filename, directory): | 
| 291   """Unzip |filename| to directory |dir|.""" | 290   """Unzip |filename| to |directory|.""" | 
| 292   cwd = os.getcwd() | 291   cwd = os.getcwd() | 
| 293   if not os.path.isabs(filename): | 292   if not os.path.isabs(filename): | 
| 294     filename = os.path.join(cwd, filename) | 293     filename = os.path.join(cwd, filename) | 
| 295   zf = zipfile.ZipFile(filename) | 294   zf = zipfile.ZipFile(filename) | 
| 296   # Make base. | 295   # Make base. | 
| 297   if not os.path.isdir(dir): | 296   if not os.path.isdir(directory): | 
| 298     os.mkdir(dir) | 297     os.mkdir(directory) | 
| 299   os.chdir(dir) | 298   os.chdir(directory) | 
| 300   # Extract files. | 299   # Extract files. | 
| 301   for info in zf.infolist(): | 300   for info in zf.infolist(): | 
| 302     name = info.filename | 301     name = info.filename | 
| 303     if name.endswith('/'):  # dir | 302     if name.endswith('/'):  # dir | 
| 304       if not os.path.isdir(name): | 303       if not os.path.isdir(name): | 
| 305         os.makedirs(name) | 304         os.makedirs(name) | 
| 306     else:  # file | 305     else:  # file | 
| 307       dir = os.path.dirname(name) | 306       directory = os.path.dirname(name) | 
| 308       if not os.path.isdir(dir): | 307       if not os.path.isdir(directory): | 
| 309         os.makedirs(dir) | 308         os.makedirs(directory) | 
| 310       out = open(name, 'wb') | 309       out = open(name, 'wb') | 
| 311       out.write(zf.read(name)) | 310       out.write(zf.read(name)) | 
| 312       out.close() | 311       out.close() | 
| 313     # Set permissions. Permission info in external_attr is shifted 16 bits. | 312     # Set permissions. Permission info in external_attr is shifted 16 bits. | 
| 314     os.chmod(name, info.external_attr >> 16L) | 313     os.chmod(name, info.external_attr >> 16L) | 
| 315   os.chdir(cwd) | 314   os.chdir(cwd) | 
| 316 | 315 | 
| 317 | 316 | 
| 318 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None): | 317 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None): | 
| 319   """Downloads and unzips revision |rev|. | 318   """Downloads and unzips revision |rev|. | 
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 356 | 355 | 
| 357   # Create a temp directory and unzip the revision into it. | 356   # Create a temp directory and unzip the revision into it. | 
| 358   cwd = os.getcwd() | 357   cwd = os.getcwd() | 
| 359   tempdir = tempfile.mkdtemp(prefix='bisect_tmp') | 358   tempdir = tempfile.mkdtemp(prefix='bisect_tmp') | 
| 360   UnzipFilenameToDir(zipfile, tempdir) | 359   UnzipFilenameToDir(zipfile, tempdir) | 
| 361   os.chdir(tempdir) | 360   os.chdir(tempdir) | 
| 362 | 361 | 
| 363   # Run the build as many times as specified. | 362   # Run the build as many times as specified. | 
| 364   testargs = ['--user-data-dir=%s' % profile] + args | 363   testargs = ['--user-data-dir=%s' % profile] + args | 
| 365   # The sandbox must be run as root on Official Chrome, so bypass it. | 364   # The sandbox must be run as root on Official Chrome, so bypass it. | 
| 366   if context.is_official and context.platform.startswith('linux'): | 365   if ((context.is_official or context.flash_path) and | 
|  | 366       context.platform.startswith('linux')): | 
| 367     testargs.append('--no-sandbox') | 367     testargs.append('--no-sandbox') | 
|  | 368   if context.flash_path: | 
|  | 369     testargs.append('--ppapi-flash-path=%s' % context.flash_path) | 
|  | 370     # We have to pass a large enough Flash version, which currently needs not | 
|  | 371     # be correct. Instead of requiring the user of the script to figure out and | 
|  | 372     # pass the correct version we just spoof it. | 
|  | 373     testargs.append('--ppapi-flash-version=99.9.999.999') | 
| 368 | 374 | 
| 369   runcommand = [] | 375   runcommand = [] | 
| 370   for token in shlex.split(command): | 376   for token in shlex.split(command): | 
| 371     if token == "%a": | 377     if token == "%a": | 
| 372       runcommand.extend(testargs) | 378       runcommand.extend(testargs) | 
| 373     else: | 379     else: | 
| 374       runcommand.append( \ | 380       runcommand.append( \ | 
| 375           token.replace('%p', context.GetLaunchPath()) \ | 381           token.replace('%p', context.GetLaunchPath()) \ | 
| 376                .replace('%s', ' '.join(testargs))) | 382                .replace('%s', ' '.join(testargs))) | 
| 377 | 383 | 
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 445 def Bisect(base_url, | 451 def Bisect(base_url, | 
| 446            platform, | 452            platform, | 
| 447            official_builds, | 453            official_builds, | 
| 448            is_aura, | 454            is_aura, | 
| 449            good_rev=0, | 455            good_rev=0, | 
| 450            bad_rev=0, | 456            bad_rev=0, | 
| 451            num_runs=1, | 457            num_runs=1, | 
| 452            command="%p %a", | 458            command="%p %a", | 
| 453            try_args=(), | 459            try_args=(), | 
| 454            profile=None, | 460            profile=None, | 
|  | 461            flash_path=None, | 
| 455            evaluate=AskIsGoodBuild): | 462            evaluate=AskIsGoodBuild): | 
| 456   """Given known good and known bad revisions, run a binary search on all | 463   """Given known good and known bad revisions, run a binary search on all | 
| 457   archived revisions to determine the last known good revision. | 464   archived revisions to determine the last known good revision. | 
| 458 | 465 | 
| 459   @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). | 466   @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). | 
| 460   @param official_builds Specify build type (Chromium or Official build). | 467   @param official_builds Specify build type (Chromium or Official build). | 
| 461   @param good_rev Number/tag of the known good revision. | 468   @param good_rev Number/tag of the known good revision. | 
| 462   @param bad_rev Number/tag of the known bad revision. | 469   @param bad_rev Number/tag of the known bad revision. | 
| 463   @param num_runs Number of times to run each build for asking good/bad. | 470   @param num_runs Number of times to run each build for asking good/bad. | 
| 464   @param try_args A tuple of arguments to pass to the test application. | 471   @param try_args A tuple of arguments to pass to the test application. | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 478       is run on rev 75. | 485       is run on rev 75. | 
| 479 | 486 | 
| 480     - If rev 50 is bad, the download of rev 75 is cancelled, and the next test | 487     - If rev 50 is bad, the download of rev 75 is cancelled, and the next test | 
| 481       is run on rev 25. | 488       is run on rev 25. | 
| 482   """ | 489   """ | 
| 483 | 490 | 
| 484   if not profile: | 491   if not profile: | 
| 485     profile = 'profile' | 492     profile = 'profile' | 
| 486 | 493 | 
| 487   context = PathContext(base_url, platform, good_rev, bad_rev, | 494   context = PathContext(base_url, platform, good_rev, bad_rev, | 
| 488                         official_builds, is_aura) | 495                         official_builds, is_aura, flash_path) | 
| 489   cwd = os.getcwd() | 496   cwd = os.getcwd() | 
| 490 | 497 | 
| 491   print "Downloading list of known revisions..." | 498   print "Downloading list of known revisions..." | 
| 492   _GetDownloadPath = lambda rev: os.path.join(cwd, | 499   _GetDownloadPath = lambda rev: os.path.join(cwd, | 
| 493       '%s-%s' % (str(rev), context.archive_name)) | 500       '%s-%s' % (str(rev), context.archive_name)) | 
| 494   if official_builds: | 501   if official_builds: | 
| 495     revlist = context.GetOfficialBuildsList() | 502     revlist = context.GetOfficialBuildsList() | 
| 496   else: | 503   else: | 
| 497     revlist = context.GetRevList() | 504     revlist = context.GetRevList() | 
| 498 | 505 | 
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 547     stderr = None | 554     stderr = None | 
| 548     try: | 555     try: | 
| 549       (status, stdout, stderr) = RunRevision(context, | 556       (status, stdout, stderr) = RunRevision(context, | 
| 550                                              rev, | 557                                              rev, | 
| 551                                              fetch.zipfile, | 558                                              fetch.zipfile, | 
| 552                                              profile, | 559                                              profile, | 
| 553                                              num_runs, | 560                                              num_runs, | 
| 554                                              command, | 561                                              command, | 
| 555                                              try_args) | 562                                              try_args) | 
| 556     except Exception, e: | 563     except Exception, e: | 
| 557       print >>sys.stderr, e | 564       print >> sys.stderr, e | 
| 558 | 565 | 
| 559     # Call the evaluate function to see if the current revision is good or bad. | 566     # Call the evaluate function to see if the current revision is good or bad. | 
| 560     # On that basis, kill one of the background downloads and complete the | 567     # On that basis, kill one of the background downloads and complete the | 
| 561     # other, as described in the comments above. | 568     # other, as described in the comments above. | 
| 562     try: | 569     try: | 
| 563       answer = evaluate(rev, official_builds, status, stdout, stderr) | 570       answer = evaluate(rev, official_builds, status, stdout, stderr) | 
| 564       if answer == 'g' and good_rev < bad_rev or \ | 571       if answer == 'g' and good_rev < bad_rev or \ | 
| 565           answer == 'b' and bad_rev < good_rev: | 572           answer == 'b' and bad_rev < good_rev: | 
| 566         fetch.Stop() | 573         fetch.Stop() | 
| 567         minrev = pivot | 574         minrev = pivot | 
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 714                     help = 'The buildbot archive to bisect [%s].' % | 721                     help = 'The buildbot archive to bisect [%s].' % | 
| 715                            '|'.join(choices)) | 722                            '|'.join(choices)) | 
| 716   parser.add_option('-o', action="store_true", dest='official_builds', | 723   parser.add_option('-o', action="store_true", dest='official_builds', | 
| 717                     help = 'Bisect across official ' + | 724                     help = 'Bisect across official ' + | 
| 718                     'Chrome builds (internal only) instead of ' + | 725                     'Chrome builds (internal only) instead of ' + | 
| 719                     'Chromium archives.') | 726                     'Chromium archives.') | 
| 720   parser.add_option('-b', '--bad', type = 'str', | 727   parser.add_option('-b', '--bad', type = 'str', | 
| 721                     help = 'A bad revision to start bisection. ' + | 728                     help = 'A bad revision to start bisection. ' + | 
| 722                     'May be earlier or later than the good revision. ' + | 729                     'May be earlier or later than the good revision. ' + | 
| 723                     'Default is HEAD.') | 730                     'Default is HEAD.') | 
|  | 731   parser.add_option('-f', '--flash_path', type = 'str', | 
|  | 732                     help = 'Absolute path to a recent Adobe Pepper Flash ' + | 
|  | 733                     'binary to be used in this bisection (e.g. ' + | 
|  | 734                     'on Windows C:\...\pepflashplayer.dll and on Linux ' + | 
|  | 735                     '/opt/google/chrome/PepperFlash/libpepflashplayer.so).') | 
| 724   parser.add_option('-g', '--good', type = 'str', | 736   parser.add_option('-g', '--good', type = 'str', | 
| 725                     help = 'A good revision to start bisection. ' + | 737                     help = 'A good revision to start bisection. ' + | 
| 726                     'May be earlier or later than the bad revision. ' + | 738                     'May be earlier or later than the bad revision. ' + | 
| 727                     'Default is 0.') | 739                     'Default is 0.') | 
| 728   parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', | 740   parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', | 
| 729                     help = 'Profile to use; this will not reset every run. ' + | 741                     help = 'Profile to use; this will not reset every run. ' + | 
| 730                     'Defaults to a clean profile.', default = 'profile') | 742                     'Defaults to a clean profile.', default = 'profile') | 
| 731   parser.add_option('-t', '--times', type = 'int', | 743   parser.add_option('-t', '--times', type = 'int', | 
| 732                     help = 'Number of times to run each build before asking ' + | 744                     help = 'Number of times to run each build before asking ' + | 
| 733                     'if it\'s good or bad. Temporary profiles are reused.', | 745                     'if it\'s good or bad. Temporary profiles are reused.', | 
| 734                     default = 1) | 746                     default = 1) | 
| 735   parser.add_option('-c', '--command', type = 'str', | 747   parser.add_option('-c', '--command', type = 'str', | 
| 736                     help = 'Command to execute. %p and %a refer to Chrome ' + | 748                     help = 'Command to execute. %p and %a refer to Chrome ' + | 
| 737                     'executable and specified extra arguments respectively. ' + | 749                     'executable and specified extra arguments respectively. ' + | 
| 738                     'Use %s to specify all extra arguments as one string. ' + | 750                     'Use %s to specify all extra arguments as one string. ' + | 
| 739                     'Defaults to "%p %a". Note that any extra paths ' + | 751                     'Defaults to "%p %a". Note that any extra paths ' + | 
| 740                     'specified should be absolute.', | 752                     'specified should be absolute.', | 
| 741                     default = '%p %a'); | 753                     default = '%p %a') | 
| 742   parser.add_option('-l', '--blink', action='store_true', | 754   parser.add_option('-l', '--blink', action='store_true', | 
| 743                     help = 'Use Blink bisect instead of Chromium. ') | 755                     help = 'Use Blink bisect instead of Chromium. ') | 
| 744   parser.add_option('--aura', | 756   parser.add_option('--aura', | 
| 745                     dest='aura', | 757                     dest='aura', | 
| 746                     action='store_true', | 758                     action='store_true', | 
| 747                     default=False, | 759                     default=False, | 
| 748                     help='Allow the script to bisect aura builds') | 760                     help='Allow the script to bisect aura builds') | 
| 749 | 761 | 
| 750   (opts, args) = parser.parse_args() | 762   (opts, args) = parser.parse_args() | 
| 751 | 763 | 
| 752   if opts.archive is None: | 764   if opts.archive is None: | 
| 753     print 'Error: missing required parameter: --archive' | 765     print 'Error: missing required parameter: --archive' | 
| 754     print | 766     print | 
| 755     parser.print_help() | 767     parser.print_help() | 
| 756     return 1 | 768     return 1 | 
| 757 | 769 | 
| 758   if opts.aura: | 770   if opts.aura: | 
| 759     if opts.archive != 'win' or not opts.official_builds: | 771     if opts.archive != 'win' or not opts.official_builds: | 
| 760       print 'Error: Aura is supported only on Windows platform '\ | 772       print 'Error: Aura is supported only on Windows platform '\ | 
| 761             'and official builds.' | 773             'and official builds.' | 
| 762       return 1 | 774       return 1 | 
| 763 | 775 | 
| 764   if opts.blink: | 776   if opts.blink: | 
| 765     base_url = WEBKIT_BASE_URL | 777     base_url = WEBKIT_BASE_URL | 
| 766   else: | 778   else: | 
| 767     base_url = CHROMIUM_BASE_URL | 779     base_url = CHROMIUM_BASE_URL | 
| 768 | 780 | 
| 769   # Create the context. Initialize 0 for the revisions as they are set below. | 781   # Create the context. Initialize 0 for the revisions as they are set below. | 
| 770   context = PathContext(base_url, opts.archive, 0, 0, | 782   context = PathContext(base_url, opts.archive, 0, 0, | 
| 771                         opts.official_builds, opts.aura) | 783                         opts.official_builds, opts.aura, None) | 
| 772   # Pick a starting point, try to get HEAD for this. | 784   # Pick a starting point, try to get HEAD for this. | 
| 773   if opts.bad: | 785   if opts.bad: | 
| 774     bad_rev = opts.bad | 786     bad_rev = opts.bad | 
| 775   else: | 787   else: | 
| 776     bad_rev = '999.0.0.0' | 788     bad_rev = '999.0.0.0' | 
| 777     if not opts.official_builds: | 789     if not opts.official_builds: | 
| 778       bad_rev = GetChromiumRevision(context.GetLastChangeURL()) | 790       bad_rev = GetChromiumRevision(context.GetLastChangeURL()) | 
| 779 | 791 | 
| 780   # Find out when we were good. | 792   # Find out when we were good. | 
| 781   if opts.good: | 793   if opts.good: | 
| 782     good_rev = opts.good | 794     good_rev = opts.good | 
| 783   else: | 795   else: | 
| 784     good_rev = '0.0.0.0' if opts.official_builds else 0 | 796     good_rev = '0.0.0.0' if opts.official_builds else 0 | 
| 785 | 797 | 
|  | 798   if opts.flash_path: | 
|  | 799     flash_path = opts.flash_path | 
|  | 800     msg = 'Could not find Flash binary at %s' % flash_path | 
|  | 801     assert os.path.exists(flash_path), msg | 
|  | 802 | 
| 786   if opts.official_builds: | 803   if opts.official_builds: | 
| 787     good_rev = LooseVersion(good_rev) | 804     good_rev = LooseVersion(good_rev) | 
| 788     bad_rev = LooseVersion(bad_rev) | 805     bad_rev = LooseVersion(bad_rev) | 
| 789   else: | 806   else: | 
| 790     good_rev = int(good_rev) | 807     good_rev = int(good_rev) | 
| 791     bad_rev = int(bad_rev) | 808     bad_rev = int(bad_rev) | 
| 792 | 809 | 
| 793   if opts.times < 1: | 810   if opts.times < 1: | 
| 794     print('Number of times to run (%d) must be greater than or equal to 1.' % | 811     print('Number of times to run (%d) must be greater than or equal to 1.' % | 
| 795           opts.times) | 812           opts.times) | 
| 796     parser.print_help() | 813     parser.print_help() | 
| 797     return 1 | 814     return 1 | 
| 798 | 815 | 
| 799   (min_chromium_rev, max_chromium_rev) = Bisect( | 816   (min_chromium_rev, max_chromium_rev) = Bisect( | 
| 800       base_url, opts.archive, opts.official_builds, opts.aura, good_rev, | 817       base_url, opts.archive, opts.official_builds, opts.aura, good_rev, | 
| 801       bad_rev, opts.times, opts.command, args, opts.profile) | 818       bad_rev, opts.times, opts.command, args, opts.profile, opts.flash_path) | 
| 802 | 819 | 
| 803   # Get corresponding blink revisions. | 820   # Get corresponding blink revisions. | 
| 804   try: | 821   try: | 
| 805     min_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 822     min_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 
| 806                                                         min_chromium_rev) | 823                                                         min_chromium_rev) | 
| 807     max_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 824     max_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 
| 808                                                         max_chromium_rev) | 825                                                         max_chromium_rev) | 
| 809   except Exception, e: | 826   except Exception, e: | 
| 810     # Silently ignore the failure. | 827     # Silently ignore the failure. | 
| 811     min_blink_rev, max_blink_rev = 0, 0 | 828     min_blink_rev, max_blink_rev = 0, 0 | 
| (...skipping 21 matching lines...) Expand all  Loading... | 
| 833              "you might also want to do a Blink bisect.") | 850              "you might also want to do a Blink bisect.") | 
| 834 | 851 | 
| 835     print 'CHANGELOG URL:' | 852     print 'CHANGELOG URL:' | 
| 836     if opts.official_builds: | 853     if opts.official_builds: | 
| 837       print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 854       print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 
| 838     else: | 855     else: | 
| 839       print '  ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 856       print '  ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 
| 840 | 857 | 
| 841 if __name__ == '__main__': | 858 if __name__ == '__main__': | 
| 842   sys.exit(main()) | 859   sys.exit(main()) | 
| OLD | NEW | 
|---|