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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
55 import urllib | 55 import urllib |
56 from distutils.version import LooseVersion | 56 from distutils.version import LooseVersion |
57 from xml.etree import ElementTree | 57 from xml.etree import ElementTree |
58 import zipfile | 58 import zipfile |
59 | 59 |
60 | 60 |
61 class PathContext(object): | 61 class PathContext(object): |
62 """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 |
63 paths when dealing with the storage server and archives.""" | 63 paths when dealing with the storage server and archives.""" |
64 def __init__(self, base_url, platform, good_revision, bad_revision, | 64 def __init__(self, base_url, platform, good_revision, bad_revision, |
65 is_official, is_aura, flash_path = None): | 65 is_official, is_aura, flash_path = None, pdf_path = None): |
66 super(PathContext, self).__init__() | 66 super(PathContext, self).__init__() |
67 # Store off the input parameters. | 67 # Store off the input parameters. |
68 self.base_url = base_url | 68 self.base_url = base_url |
69 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. |
70 self.good_revision = good_revision | 70 self.good_revision = good_revision |
71 self.bad_revision = bad_revision | 71 self.bad_revision = bad_revision |
72 self.is_official = is_official | 72 self.is_official = is_official |
73 self.is_aura = is_aura | 73 self.is_aura = is_aura |
74 self.flash_path = flash_path | 74 self.flash_path = flash_path |
75 self.pdf_path = pdf_path | |
75 | 76 |
76 # The name of the ZIP file in a revision directory on the server. | 77 # The name of the ZIP file in a revision directory on the server. |
77 self.archive_name = None | 78 self.archive_name = None |
78 | 79 |
79 # Set some internal members: | 80 # Set some internal members: |
80 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'. | 81 # _listing_platform_dir = Directory that holds revisions. Ends with a '/'. |
81 # _archive_extract_dir = Uncompressed directory in the archive_name file. | 82 # _archive_extract_dir = Uncompressed directory in the archive_name file. |
82 # _binary_name = The name of the executable to run. | 83 # _binary_name = The name of the executable to run. |
83 if self.platform in ('linux', 'linux64', 'linux-arm'): | 84 if self.platform in ('linux', 'linux64', 'linux-arm'): |
84 self._binary_name = 'chrome' | 85 self._binary_name = 'chrome' |
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 | 356 |
356 # Create a temp directory and unzip the revision into it. | 357 # Create a temp directory and unzip the revision into it. |
357 cwd = os.getcwd() | 358 cwd = os.getcwd() |
358 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') | 359 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') |
359 UnzipFilenameToDir(zipfile, tempdir) | 360 UnzipFilenameToDir(zipfile, tempdir) |
360 os.chdir(tempdir) | 361 os.chdir(tempdir) |
361 | 362 |
362 # Run the build as many times as specified. | 363 # Run the build as many times as specified. |
363 testargs = ['--user-data-dir=%s' % profile] + args | 364 testargs = ['--user-data-dir=%s' % profile] + args |
364 # The sandbox must be run as root on Official Chrome, so bypass it. | 365 # The sandbox must be run as root on Official Chrome, so bypass it. |
365 if ((context.is_official or context.flash_path) and | 366 if ((context.is_official or context.flash_path or context.pdf_path) and |
366 context.platform.startswith('linux')): | 367 context.platform.startswith('linux')): |
367 testargs.append('--no-sandbox') | 368 testargs.append('--no-sandbox') |
368 if context.flash_path: | 369 if context.flash_path: |
369 testargs.append('--ppapi-flash-path=%s' % context.flash_path) | 370 testargs.append('--ppapi-flash-path=%s' % context.flash_path) |
370 # We have to pass a large enough Flash version, which currently needs not | 371 # 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 # be correct. Instead of requiring the user of the script to figure out and |
372 # pass the correct version we just spoof it. | 373 # pass the correct version we just spoof it. |
373 testargs.append('--ppapi-flash-version=99.9.999.999') | 374 testargs.append('--ppapi-flash-version=99.9.999.999') |
374 | 375 |
376 if context.pdf_path: | |
377 shutil.copy(context.pdf_path, os.path.dirname(context.GetLaunchPath())) | |
Robert Sesek
2014/01/08 15:31:22
Why do you need to copy this? Can't you use --regi
| |
378 testargs.append('--enable-print-preview') | |
379 | |
375 runcommand = [] | 380 runcommand = [] |
376 for token in shlex.split(command): | 381 for token in shlex.split(command): |
377 if token == "%a": | 382 if token == "%a": |
378 runcommand.extend(testargs) | 383 runcommand.extend(testargs) |
379 else: | 384 else: |
380 runcommand.append( \ | 385 runcommand.append( \ |
381 token.replace('%p', context.GetLaunchPath()) \ | 386 token.replace('%p', context.GetLaunchPath()) \ |
382 .replace('%s', ' '.join(testargs))) | 387 .replace('%s', ' '.join(testargs))) |
383 | 388 |
384 for i in range(0, num_runs): | 389 for i in range(0, num_runs): |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
452 platform, | 457 platform, |
453 official_builds, | 458 official_builds, |
454 is_aura, | 459 is_aura, |
455 good_rev=0, | 460 good_rev=0, |
456 bad_rev=0, | 461 bad_rev=0, |
457 num_runs=1, | 462 num_runs=1, |
458 command="%p %a", | 463 command="%p %a", |
459 try_args=(), | 464 try_args=(), |
460 profile=None, | 465 profile=None, |
461 flash_path=None, | 466 flash_path=None, |
467 pdf_path=None, | |
462 evaluate=AskIsGoodBuild): | 468 evaluate=AskIsGoodBuild): |
463 """Given known good and known bad revisions, run a binary search on all | 469 """Given known good and known bad revisions, run a binary search on all |
464 archived revisions to determine the last known good revision. | 470 archived revisions to determine the last known good revision. |
465 | 471 |
466 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). | 472 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). |
467 @param official_builds Specify build type (Chromium or Official build). | 473 @param official_builds Specify build type (Chromium or Official build). |
468 @param good_rev Number/tag of the known good revision. | 474 @param good_rev Number/tag of the known good revision. |
469 @param bad_rev Number/tag of the known bad revision. | 475 @param bad_rev Number/tag of the known bad revision. |
470 @param num_runs Number of times to run each build for asking good/bad. | 476 @param num_runs Number of times to run each build for asking good/bad. |
471 @param try_args A tuple of arguments to pass to the test application. | 477 @param try_args A tuple of arguments to pass to the test application. |
(...skipping 13 matching lines...) Expand all Loading... | |
485 is run on rev 75. | 491 is run on rev 75. |
486 | 492 |
487 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test | 493 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test |
488 is run on rev 25. | 494 is run on rev 25. |
489 """ | 495 """ |
490 | 496 |
491 if not profile: | 497 if not profile: |
492 profile = 'profile' | 498 profile = 'profile' |
493 | 499 |
494 context = PathContext(base_url, platform, good_rev, bad_rev, | 500 context = PathContext(base_url, platform, good_rev, bad_rev, |
495 official_builds, is_aura, flash_path) | 501 official_builds, is_aura, flash_path, pdf_path) |
496 cwd = os.getcwd() | 502 cwd = os.getcwd() |
497 | 503 |
498 print "Downloading list of known revisions..." | 504 print "Downloading list of known revisions..." |
499 _GetDownloadPath = lambda rev: os.path.join(cwd, | 505 _GetDownloadPath = lambda rev: os.path.join(cwd, |
500 '%s-%s' % (str(rev), context.archive_name)) | 506 '%s-%s' % (str(rev), context.archive_name)) |
501 if official_builds: | 507 if official_builds: |
502 revlist = context.GetOfficialBuildsList() | 508 revlist = context.GetOfficialBuildsList() |
503 else: | 509 else: |
504 revlist = context.GetRevList() | 510 revlist = context.GetRevList() |
505 | 511 |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
726 'Chromium archives.') | 732 'Chromium archives.') |
727 parser.add_option('-b', '--bad', type = 'str', | 733 parser.add_option('-b', '--bad', type = 'str', |
728 help = 'A bad revision to start bisection. ' + | 734 help = 'A bad revision to start bisection. ' + |
729 'May be earlier or later than the good revision. ' + | 735 'May be earlier or later than the good revision. ' + |
730 'Default is HEAD.') | 736 'Default is HEAD.') |
731 parser.add_option('-f', '--flash_path', type = 'str', | 737 parser.add_option('-f', '--flash_path', type = 'str', |
732 help = 'Absolute path to a recent Adobe Pepper Flash ' + | 738 help = 'Absolute path to a recent Adobe Pepper Flash ' + |
733 'binary to be used in this bisection (e.g. ' + | 739 'binary to be used in this bisection (e.g. ' + |
734 'on Windows C:\...\pepflashplayer.dll and on Linux ' + | 740 'on Windows C:\...\pepflashplayer.dll and on Linux ' + |
735 '/opt/google/chrome/PepperFlash/libpepflashplayer.so).') | 741 '/opt/google/chrome/PepperFlash/libpepflashplayer.so).') |
742 parser.add_option('-d', '--pdf_path', type = 'str', | |
743 help = 'Absolute path to a recent PDF pluggin ' + | |
744 'binary to be used in this bisection (e.g. ' + | |
745 'on Windows C:\...\pdf.dll and on Linux ' + | |
746 '/opt/google/chrome/libpdf.so). Option also enables ' + | |
747 'print preview.') | |
736 parser.add_option('-g', '--good', type = 'str', | 748 parser.add_option('-g', '--good', type = 'str', |
737 help = 'A good revision to start bisection. ' + | 749 help = 'A good revision to start bisection. ' + |
738 'May be earlier or later than the bad revision. ' + | 750 'May be earlier or later than the bad revision. ' + |
739 'Default is 0.') | 751 'Default is 0.') |
740 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', | 752 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', |
741 help = 'Profile to use; this will not reset every run. ' + | 753 help = 'Profile to use; this will not reset every run. ' + |
742 'Defaults to a clean profile.', default = 'profile') | 754 'Defaults to a clean profile.', default = 'profile') |
743 parser.add_option('-t', '--times', type = 'int', | 755 parser.add_option('-t', '--times', type = 'int', |
744 help = 'Number of times to run each build before asking ' + | 756 help = 'Number of times to run each build before asking ' + |
745 'if it\'s good or bad. Temporary profiles are reused.', | 757 'if it\'s good or bad. Temporary profiles are reused.', |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
793 if opts.good: | 805 if opts.good: |
794 good_rev = opts.good | 806 good_rev = opts.good |
795 else: | 807 else: |
796 good_rev = '0.0.0.0' if opts.official_builds else 0 | 808 good_rev = '0.0.0.0' if opts.official_builds else 0 |
797 | 809 |
798 if opts.flash_path: | 810 if opts.flash_path: |
799 flash_path = opts.flash_path | 811 flash_path = opts.flash_path |
800 msg = 'Could not find Flash binary at %s' % flash_path | 812 msg = 'Could not find Flash binary at %s' % flash_path |
801 assert os.path.exists(flash_path), msg | 813 assert os.path.exists(flash_path), msg |
802 | 814 |
815 if opts.pdf_path: | |
816 pdf_path = opts.pdf_path | |
817 msg = 'Could not find PDF binary at %s' % pdf_path | |
818 assert os.path.exists(pdf_path), msg | |
819 | |
803 if opts.official_builds: | 820 if opts.official_builds: |
804 good_rev = LooseVersion(good_rev) | 821 good_rev = LooseVersion(good_rev) |
805 bad_rev = LooseVersion(bad_rev) | 822 bad_rev = LooseVersion(bad_rev) |
806 else: | 823 else: |
807 good_rev = int(good_rev) | 824 good_rev = int(good_rev) |
808 bad_rev = int(bad_rev) | 825 bad_rev = int(bad_rev) |
809 | 826 |
810 if opts.times < 1: | 827 if opts.times < 1: |
811 print('Number of times to run (%d) must be greater than or equal to 1.' % | 828 print('Number of times to run (%d) must be greater than or equal to 1.' % |
812 opts.times) | 829 opts.times) |
813 parser.print_help() | 830 parser.print_help() |
814 return 1 | 831 return 1 |
815 | 832 |
816 (min_chromium_rev, max_chromium_rev) = Bisect( | 833 (min_chromium_rev, max_chromium_rev) = Bisect( |
817 base_url, opts.archive, opts.official_builds, opts.aura, good_rev, | 834 base_url, opts.archive, opts.official_builds, opts.aura, good_rev, |
818 bad_rev, opts.times, opts.command, args, opts.profile, opts.flash_path) | 835 bad_rev, opts.times, opts.command, args, opts.profile, opts.flash_path, |
836 opts.pdf_path) | |
819 | 837 |
820 # Get corresponding blink revisions. | 838 # Get corresponding blink revisions. |
821 try: | 839 try: |
822 min_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 840 min_blink_rev = GetBlinkRevisionForChromiumRevision(context, |
823 min_chromium_rev) | 841 min_chromium_rev) |
824 max_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 842 max_blink_rev = GetBlinkRevisionForChromiumRevision(context, |
825 max_chromium_rev) | 843 max_chromium_rev) |
826 except Exception, e: | 844 except Exception, e: |
827 # Silently ignore the failure. | 845 # Silently ignore the failure. |
828 min_blink_rev, max_blink_rev = 0, 0 | 846 min_blink_rev, max_blink_rev = 0, 0 |
(...skipping 21 matching lines...) Expand all Loading... | |
850 "you might also want to do a Blink bisect.") | 868 "you might also want to do a Blink bisect.") |
851 | 869 |
852 print 'CHANGELOG URL:' | 870 print 'CHANGELOG URL:' |
853 if opts.official_builds: | 871 if opts.official_builds: |
854 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 872 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
855 else: | 873 else: |
856 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 874 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
857 | 875 |
858 if __name__ == '__main__': | 876 if __name__ == '__main__': |
859 sys.exit(main()) | 877 sys.exit(main()) |
OLD | NEW |