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 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 import urllib | 72 import urllib |
73 from distutils.version import LooseVersion | 73 from distutils.version import LooseVersion |
74 from xml.etree import ElementTree | 74 from xml.etree import ElementTree |
75 import zipfile | 75 import zipfile |
76 | 76 |
77 | 77 |
78 class PathContext(object): | 78 class PathContext(object): |
79 """A PathContext is used to carry the information used to construct URLs and | 79 """A PathContext is used to carry the information used to construct URLs and |
80 paths when dealing with the storage server and archives.""" | 80 paths when dealing with the storage server and archives.""" |
81 def __init__(self, base_url, platform, good_revision, bad_revision, | 81 def __init__(self, base_url, platform, good_revision, bad_revision, |
82 is_official, is_aura, use_local_repo, flash_path = None): | 82 is_official, is_aura, use_local_repo, flash_path = None, |
| 83 pdf_path = None): |
83 super(PathContext, self).__init__() | 84 super(PathContext, self).__init__() |
84 # Store off the input parameters. | 85 # Store off the input parameters. |
85 self.base_url = base_url | 86 self.base_url = base_url |
86 self.platform = platform # What's passed in to the '-a/--archive' option. | 87 self.platform = platform # What's passed in to the '-a/--archive' option. |
87 self.good_revision = good_revision | 88 self.good_revision = good_revision |
88 self.bad_revision = bad_revision | 89 self.bad_revision = bad_revision |
89 self.is_official = is_official | 90 self.is_official = is_official |
90 self.is_aura = is_aura | 91 self.is_aura = is_aura |
91 self.flash_path = flash_path | 92 self.flash_path = flash_path |
92 # Dictionary which stores svn revision number as key and it's | 93 # Dictionary which stores svn revision number as key and it's |
93 # corresponding git hash as value. This data is populated in | 94 # corresponding git hash as value. This data is populated in |
94 # _FetchAndParse and used later in GetDownloadURL while downloading | 95 # _FetchAndParse and used later in GetDownloadURL while downloading |
95 # the build. | 96 # the build. |
96 self.githash_svn_dict = {} | 97 self.githash_svn_dict = {} |
| 98 self.pdf_path = pdf_path |
97 | 99 |
98 # The name of the ZIP file in a revision directory on the server. | 100 # The name of the ZIP file in a revision directory on the server. |
99 self.archive_name = None | 101 self.archive_name = None |
100 | 102 |
101 # If the script is run from a local Chromium checkout, | 103 # If the script is run from a local Chromium checkout, |
102 # "--use-local-repo" option can be used to make the script run faster. | 104 # "--use-local-repo" option can be used to make the script run faster. |
103 # It uses "git svn find-rev <SHA1>" command to convert git hash to svn | 105 # It uses "git svn find-rev <SHA1>" command to convert git hash to svn |
104 # revision number. | 106 # revision number. |
105 self.use_local_repo = use_local_repo | 107 self.use_local_repo = use_local_repo |
106 | 108 |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 | 447 |
446 # Create a temp directory and unzip the revision into it. | 448 # Create a temp directory and unzip the revision into it. |
447 cwd = os.getcwd() | 449 cwd = os.getcwd() |
448 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') | 450 tempdir = tempfile.mkdtemp(prefix='bisect_tmp') |
449 UnzipFilenameToDir(zipfile, tempdir) | 451 UnzipFilenameToDir(zipfile, tempdir) |
450 os.chdir(tempdir) | 452 os.chdir(tempdir) |
451 | 453 |
452 # Run the build as many times as specified. | 454 # Run the build as many times as specified. |
453 testargs = ['--user-data-dir=%s' % profile] + args | 455 testargs = ['--user-data-dir=%s' % profile] + args |
454 # The sandbox must be run as root on Official Chrome, so bypass it. | 456 # The sandbox must be run as root on Official Chrome, so bypass it. |
455 if ((context.is_official or context.flash_path) and | 457 if ((context.is_official or context.flash_path or context.pdf_path) and |
456 context.platform.startswith('linux')): | 458 context.platform.startswith('linux')): |
457 testargs.append('--no-sandbox') | 459 testargs.append('--no-sandbox') |
458 if context.flash_path: | 460 if context.flash_path: |
459 testargs.append('--ppapi-flash-path=%s' % context.flash_path) | 461 testargs.append('--ppapi-flash-path=%s' % context.flash_path) |
460 # We have to pass a large enough Flash version, which currently needs not | 462 # We have to pass a large enough Flash version, which currently needs not |
461 # be correct. Instead of requiring the user of the script to figure out and | 463 # be correct. Instead of requiring the user of the script to figure out and |
462 # pass the correct version we just spoof it. | 464 # pass the correct version we just spoof it. |
463 testargs.append('--ppapi-flash-version=99.9.999.999') | 465 testargs.append('--ppapi-flash-version=99.9.999.999') |
464 | 466 |
| 467 # TODO(vitalybuka): Remove in the future. See crbug.com/395687. |
| 468 if context.pdf_path: |
| 469 shutil.copy(context.pdf_path, os.path.dirname(context.GetLaunchPath())) |
| 470 testargs.append('--enable-print-preview') |
| 471 |
465 runcommand = [] | 472 runcommand = [] |
466 for token in shlex.split(command): | 473 for token in shlex.split(command): |
467 if token == "%a": | 474 if token == "%a": |
468 runcommand.extend(testargs) | 475 runcommand.extend(testargs) |
469 else: | 476 else: |
470 runcommand.append( \ | 477 runcommand.append( \ |
471 token.replace('%p', os.path.abspath(context.GetLaunchPath())) \ | 478 token.replace('%p', os.path.abspath(context.GetLaunchPath())) \ |
472 .replace('%s', ' '.join(testargs))) | 479 .replace('%s', ' '.join(testargs))) |
473 | 480 |
474 results = [] | 481 results = [] |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 official_builds, | 555 official_builds, |
549 is_aura, | 556 is_aura, |
550 use_local_repo, | 557 use_local_repo, |
551 good_rev=0, | 558 good_rev=0, |
552 bad_rev=0, | 559 bad_rev=0, |
553 num_runs=1, | 560 num_runs=1, |
554 command="%p %a", | 561 command="%p %a", |
555 try_args=(), | 562 try_args=(), |
556 profile=None, | 563 profile=None, |
557 flash_path=None, | 564 flash_path=None, |
| 565 pdf_path=None, |
558 interactive=True, | 566 interactive=True, |
559 evaluate=AskIsGoodBuild): | 567 evaluate=AskIsGoodBuild): |
560 """Given known good and known bad revisions, run a binary search on all | 568 """Given known good and known bad revisions, run a binary search on all |
561 archived revisions to determine the last known good revision. | 569 archived revisions to determine the last known good revision. |
562 | 570 |
563 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). | 571 @param platform Which build to download/run ('mac', 'win', 'linux64', etc.). |
564 @param official_builds Specify build type (Chromium or Official build). | 572 @param official_builds Specify build type (Chromium or Official build). |
565 @param good_rev Number/tag of the known good revision. | 573 @param good_rev Number/tag of the known good revision. |
566 @param bad_rev Number/tag of the known bad revision. | 574 @param bad_rev Number/tag of the known bad revision. |
567 @param num_runs Number of times to run each build for asking good/bad. | 575 @param num_runs Number of times to run each build for asking good/bad. |
(...skipping 16 matching lines...) Expand all Loading... |
584 is run on rev 75. | 592 is run on rev 75. |
585 | 593 |
586 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test | 594 - If rev 50 is bad, the download of rev 75 is cancelled, and the next test |
587 is run on rev 25. | 595 is run on rev 25. |
588 """ | 596 """ |
589 | 597 |
590 if not profile: | 598 if not profile: |
591 profile = 'profile' | 599 profile = 'profile' |
592 | 600 |
593 context = PathContext(base_url, platform, good_rev, bad_rev, | 601 context = PathContext(base_url, platform, good_rev, bad_rev, |
594 official_builds, is_aura, use_local_repo, flash_path) | 602 official_builds, is_aura, use_local_repo, flash_path, |
| 603 pdf_path) |
595 cwd = os.getcwd() | 604 cwd = os.getcwd() |
596 | 605 |
597 print "Downloading list of known revisions..." | 606 print "Downloading list of known revisions..." |
598 _GetDownloadPath = lambda rev: os.path.join(cwd, | 607 _GetDownloadPath = lambda rev: os.path.join(cwd, |
599 '%s-%s' % (str(rev), context.archive_name)) | 608 '%s-%s' % (str(rev), context.archive_name)) |
600 if official_builds: | 609 if official_builds: |
601 revlist = context.GetOfficialBuildsList() | 610 revlist = context.GetOfficialBuildsList() |
602 else: | 611 else: |
603 revlist = context.GetRevList() | 612 revlist = context.GetRevList() |
604 | 613 |
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
845 'Chromium archives.') | 854 'Chromium archives.') |
846 parser.add_option('-b', '--bad', type = 'str', | 855 parser.add_option('-b', '--bad', type = 'str', |
847 help = 'A bad revision to start bisection. ' + | 856 help = 'A bad revision to start bisection. ' + |
848 'May be earlier or later than the good revision. ' + | 857 'May be earlier or later than the good revision. ' + |
849 'Default is HEAD.') | 858 'Default is HEAD.') |
850 parser.add_option('-f', '--flash_path', type = 'str', | 859 parser.add_option('-f', '--flash_path', type = 'str', |
851 help = 'Absolute path to a recent Adobe Pepper Flash ' + | 860 help = 'Absolute path to a recent Adobe Pepper Flash ' + |
852 'binary to be used in this bisection (e.g. ' + | 861 'binary to be used in this bisection (e.g. ' + |
853 'on Windows C:\...\pepflashplayer.dll and on Linux ' + | 862 'on Windows C:\...\pepflashplayer.dll and on Linux ' + |
854 '/opt/google/chrome/PepperFlash/libpepflashplayer.so).') | 863 '/opt/google/chrome/PepperFlash/libpepflashplayer.so).') |
| 864 parser.add_option('-d', '--pdf_path', type = 'str', |
| 865 help = 'Absolute path to a recent PDF pluggin ' + |
| 866 'binary to be used in this bisection (e.g. ' + |
| 867 'on Windows C:\...\pdf.dll and on Linux ' + |
| 868 '/opt/google/chrome/libpdf.so). Option also enables ' + |
| 869 'print preview.') |
855 parser.add_option('-g', '--good', type = 'str', | 870 parser.add_option('-g', '--good', type = 'str', |
856 help = 'A good revision to start bisection. ' + | 871 help = 'A good revision to start bisection. ' + |
857 'May be earlier or later than the bad revision. ' + | 872 'May be earlier or later than the bad revision. ' + |
858 'Default is 0.') | 873 'Default is 0.') |
859 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', | 874 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', |
860 help = 'Profile to use; this will not reset every run. ' + | 875 help = 'Profile to use; this will not reset every run. ' + |
861 'Defaults to a clean profile.', default = 'profile') | 876 'Defaults to a clean profile.', default = 'profile') |
862 parser.add_option('-t', '--times', type = 'int', | 877 parser.add_option('-t', '--times', type = 'int', |
863 help = 'Number of times to run each build before asking ' + | 878 help = 'Number of times to run each build before asking ' + |
864 'if it\'s good or bad. Temporary profiles are reused.', | 879 'if it\'s good or bad. Temporary profiles are reused.', |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
923 if opts.good: | 938 if opts.good: |
924 good_rev = opts.good | 939 good_rev = opts.good |
925 else: | 940 else: |
926 good_rev = '0.0.0.0' if opts.official_builds else 0 | 941 good_rev = '0.0.0.0' if opts.official_builds else 0 |
927 | 942 |
928 if opts.flash_path: | 943 if opts.flash_path: |
929 flash_path = opts.flash_path | 944 flash_path = opts.flash_path |
930 msg = 'Could not find Flash binary at %s' % flash_path | 945 msg = 'Could not find Flash binary at %s' % flash_path |
931 assert os.path.exists(flash_path), msg | 946 assert os.path.exists(flash_path), msg |
932 | 947 |
| 948 if opts.pdf_path: |
| 949 pdf_path = opts.pdf_path |
| 950 msg = 'Could not find PDF binary at %s' % pdf_path |
| 951 assert os.path.exists(pdf_path), msg |
| 952 |
933 if opts.official_builds: | 953 if opts.official_builds: |
934 good_rev = LooseVersion(good_rev) | 954 good_rev = LooseVersion(good_rev) |
935 bad_rev = LooseVersion(bad_rev) | 955 bad_rev = LooseVersion(bad_rev) |
936 else: | 956 else: |
937 good_rev = int(good_rev) | 957 good_rev = int(good_rev) |
938 bad_rev = int(bad_rev) | 958 bad_rev = int(bad_rev) |
939 | 959 |
940 if opts.times < 1: | 960 if opts.times < 1: |
941 print('Number of times to run (%d) must be greater than or equal to 1.' % | 961 print('Number of times to run (%d) must be greater than or equal to 1.' % |
942 opts.times) | 962 opts.times) |
943 parser.print_help() | 963 parser.print_help() |
944 return 1 | 964 return 1 |
945 | 965 |
946 (min_chromium_rev, max_chromium_rev) = Bisect( | 966 (min_chromium_rev, max_chromium_rev) = Bisect( |
947 base_url, opts.archive, opts.official_builds, opts.aura, | 967 base_url, opts.archive, opts.official_builds, opts.aura, |
948 opts.use_local_repo, good_rev, bad_rev, opts.times, opts.command, | 968 opts.use_local_repo, good_rev, bad_rev, opts.times, opts.command, |
949 args, opts.profile, opts.flash_path, not opts.not_interactive) | 969 args, opts.profile, opts.flash_path, opts.pdf_path, |
| 970 not opts.not_interactive) |
950 | 971 |
951 # Get corresponding blink revisions. | 972 # Get corresponding blink revisions. |
952 try: | 973 try: |
953 min_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 974 min_blink_rev = GetBlinkRevisionForChromiumRevision(context, |
954 min_chromium_rev) | 975 min_chromium_rev) |
955 max_blink_rev = GetBlinkRevisionForChromiumRevision(context, | 976 max_blink_rev = GetBlinkRevisionForChromiumRevision(context, |
956 max_chromium_rev) | 977 max_chromium_rev) |
957 except Exception, e: | 978 except Exception, e: |
958 # Silently ignore the failure. | 979 # Silently ignore the failure. |
959 min_blink_rev, max_blink_rev = 0, 0 | 980 min_blink_rev, max_blink_rev = 0, 0 |
(...skipping 21 matching lines...) Expand all Loading... |
981 "you might also want to do a Blink bisect.") | 1002 "you might also want to do a Blink bisect.") |
982 | 1003 |
983 print 'CHANGELOG URL:' | 1004 print 'CHANGELOG URL:' |
984 if opts.official_builds: | 1005 if opts.official_builds: |
985 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 1006 print OFFICIAL_CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
986 else: | 1007 else: |
987 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) | 1008 print ' ' + CHANGELOG_URL % (min_chromium_rev, max_chromium_rev) |
988 | 1009 |
989 if __name__ == '__main__': | 1010 if __name__ == '__main__': |
990 sys.exit(main()) | 1011 sys.exit(main()) |
OLD | NEW |