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 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 """Gets the list of official build numbers between self.good_revision and | 209 """Gets the list of official build numbers between self.good_revision and |
210 self.bad_revision.""" | 210 self.bad_revision.""" |
211 # Download the revlist and filter for just the range between good and bad. | 211 # Download the revlist and filter for just the range between good and bad. |
212 minrev = self.good_revision | 212 minrev = self.good_revision |
213 maxrev = self.bad_revision | 213 maxrev = self.bad_revision |
214 handle = urllib.urlopen(OFFICIAL_BASE_URL) | 214 handle = urllib.urlopen(OFFICIAL_BASE_URL) |
215 dirindex = handle.read() | 215 dirindex = handle.read() |
216 handle.close() | 216 handle.close() |
217 build_numbers = re.findall(r'<a href="([0-9][0-9].*)/">', dirindex) | 217 build_numbers = re.findall(r'<a href="([0-9][0-9].*)/">', dirindex) |
218 final_list = [] | 218 final_list = [] |
219 start_index = 0 | |
220 end_index = 0 | |
221 i = 0 | 219 i = 0 |
222 | |
223 parsed_build_numbers = [LooseVersion(x) for x in build_numbers] | 220 parsed_build_numbers = [LooseVersion(x) for x in build_numbers] |
224 for build_number in sorted(parsed_build_numbers): | 221 for build_number in sorted(parsed_build_numbers): |
225 path = OFFICIAL_BASE_URL + '/' + str(build_number) + '/' + \ | 222 path = OFFICIAL_BASE_URL + '/' + str(build_number) + '/' + \ |
226 self._listing_platform_dir + self.archive_name | 223 self._listing_platform_dir + self.archive_name |
227 i = i + 1 | 224 i = i + 1 |
228 try: | 225 try: |
229 connection = urllib.urlopen(path) | 226 connection = urllib.urlopen(path) |
230 connection.close() | 227 connection.close() |
228 # Keep only one revision before and after if no exact match found. | |
229 if build_number <= minrev: | |
230 final_list = [] | |
231 final_list.append(str(build_number)) | 231 final_list.append(str(build_number)) |
Nico
2012/07/20 17:19:45
This appends on every revision and then resets to
Vitaly Buka (NO REVIEWS)
2012/07/20 17:37:01
It's not what I tried to do.
Example:
we have rev
Nico
2012/07/20 17:39:11
I see. The code on the left did what my code did t
| |
232 if str(build_number) == minrev: | 232 if build_number >= maxrev: |
233 start_index = i | 233 break; |
Nico
2012/07/20 17:19:45
no semicolon
Vitaly Buka (NO REVIEWS)
2012/07/20 17:37:01
Done.
| |
234 if str(build_number) == maxrev: | |
235 end_index = i | |
236 except urllib.HTTPError, e: | 234 except urllib.HTTPError, e: |
237 pass | 235 pass |
238 return final_list[start_index:end_index] | 236 return final_list |
239 | 237 |
240 def UnzipFilenameToDir(filename, dir): | 238 def UnzipFilenameToDir(filename, dir): |
241 """Unzip |filename| to directory |dir|.""" | 239 """Unzip |filename| to directory |dir|.""" |
242 cwd = os.getcwd() | 240 cwd = os.getcwd() |
243 if not os.path.isabs(filename): | 241 if not os.path.isabs(filename): |
244 filename = os.path.join(cwd, filename) | 242 filename = os.path.join(cwd, filename) |
245 zf = zipfile.ZipFile(filename) | 243 zf = zipfile.ZipFile(filename) |
246 # Make base. | 244 # Make base. |
247 try: | 245 try: |
248 if not os.path.isdir(dir): | 246 if not os.path.isdir(dir): |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
437 if official_builds: | 435 if official_builds: |
438 revlist = context.GetOfficialBuildsList() | 436 revlist = context.GetOfficialBuildsList() |
439 else: | 437 else: |
440 revlist = context.GetRevList() | 438 revlist = context.GetRevList() |
441 | 439 |
442 # Get a list of revisions to bisect across. | 440 # Get a list of revisions to bisect across. |
443 if len(revlist) < 2: # Don't have enough builds to bisect. | 441 if len(revlist) < 2: # Don't have enough builds to bisect. |
444 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist | 442 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist |
445 raise RuntimeError(msg) | 443 raise RuntimeError(msg) |
446 | 444 |
445 print 'Bisecting range [%s, %s].' % (revlist[0], revlist[-1]) | |
446 | |
447 # Figure out our bookends and first pivot point; fetch the pivot revision. | 447 # Figure out our bookends and first pivot point; fetch the pivot revision. |
448 good = 0 | 448 good = 0 |
449 bad = len(revlist) - 1 | 449 bad = len(revlist) - 1 |
450 pivot = bad / 2 | 450 pivot = bad / 2 |
451 rev = revlist[pivot] | 451 rev = revlist[pivot] |
452 zipfile = _GetDownloadPath(rev) | 452 zipfile = _GetDownloadPath(rev) |
453 initial_fetch = DownloadJob(context, 'initial_fetch', rev, zipfile) | 453 initial_fetch = DownloadJob(context, 'initial_fetch', rev, zipfile) |
454 initial_fetch.Start() | 454 initial_fetch.Start() |
455 initial_fetch.WaitFor() | 455 initial_fetch.WaitFor() |
456 | 456 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
561 webkit_re = re.compile(r'webkit_revision.:\D*(\d+)') | 561 webkit_re = re.compile(r'webkit_revision.:\D*(\d+)') |
562 url = urllib.urlopen(DEPS_FILE % rev) | 562 url = urllib.urlopen(DEPS_FILE % rev) |
563 m = webkit_re.search(url.read()) | 563 m = webkit_re.search(url.read()) |
564 url.close() | 564 url.close() |
565 if m: | 565 if m: |
566 return int(m.group(1)) | 566 return int(m.group(1)) |
567 else: | 567 else: |
568 raise Exception('Could not get webkit revision for cr rev %d' % rev) | 568 raise Exception('Could not get webkit revision for cr rev %d' % rev) |
569 | 569 |
570 | 570 |
571 def GetChromiumRevision(url): | |
572 """Returns the chromium revision read from given URL.""" | |
573 try: | |
574 # Location of the latest build revision number | |
575 nh = urllib.urlopen(url) | |
Nico
2012/07/20 17:19:45
No need for a nh variable
Vitaly Buka (NO REVIEWS)
2012/07/20 17:37:01
Done.
| |
576 return int(nh.read()) | |
577 except Exception, e: | |
578 print('Could not determine latest revision. This could be bad...') | |
Nico
2012/07/20 17:19:45
Why catch this? Just let it propagate, no?
…ah, t
Vitaly Buka (NO REVIEWS)
2012/07/20 17:37:01
Done.
| |
579 return 0 | |
580 | |
581 | |
571 def main(): | 582 def main(): |
572 usage = ('%prog [options] [-- chromium-options]\n' | 583 usage = ('%prog [options] [-- chromium-options]\n' |
573 'Perform binary search on the snapshot builds.\n' | 584 'Perform binary search on the snapshot builds.\n' |
574 '\n' | 585 '\n' |
575 'Tip: add "-- --no-first-run" to bypass the first run prompts.') | 586 'Tip: add "-- --no-first-run" to bypass the first run prompts.') |
576 parser = optparse.OptionParser(usage=usage) | 587 parser = optparse.OptionParser(usage=usage) |
577 # Strangely, the default help output doesn't include the choice list. | 588 # Strangely, the default help output doesn't include the choice list. |
578 choices = ['mac', 'win', 'linux', 'linux64'] | 589 choices = ['mac', 'win', 'linux', 'linux64'] |
579 # linux-chromiumos lacks a continuous archive http://crbug.com/78158 | 590 # linux-chromiumos lacks a continuous archive http://crbug.com/78158 |
580 parser.add_option('-a', '--archive', | 591 parser.add_option('-a', '--archive', |
(...skipping 16 matching lines...) Expand all Loading... | |
597 'if it\'s good or bad. Temporary profiles are reused.', | 608 'if it\'s good or bad. Temporary profiles are reused.', |
598 default = 1) | 609 default = 1) |
599 (opts, args) = parser.parse_args() | 610 (opts, args) = parser.parse_args() |
600 | 611 |
601 if opts.archive is None: | 612 if opts.archive is None: |
602 print 'Error: missing required parameter: --archive' | 613 print 'Error: missing required parameter: --archive' |
603 print | 614 print |
604 parser.print_help() | 615 parser.print_help() |
605 return 1 | 616 return 1 |
606 | 617 |
607 if opts.bad and opts.good and (opts.good > opts.bad): | |
608 print ('The good revision (%d) must precede the bad revision (%d).\n' % | |
609 (opts.good, opts.bad)) | |
610 parser.print_help() | |
611 return 1 | |
612 | |
613 # Create the context. Initialize 0 for the revisions as they are set below. | 618 # Create the context. Initialize 0 for the revisions as they are set below. |
614 context = PathContext(opts.archive, 0, 0, opts.official_builds) | 619 context = PathContext(opts.archive, 0, 0, opts.official_builds) |
615 | |
616 if opts.official_builds and opts.bad is None: | |
617 print >>sys.stderr, 'Bisecting official builds requires a bad build number.' | |
618 parser.print_help() | |
619 return 1 | |
620 | |
621 # Pick a starting point, try to get HEAD for this. | 620 # Pick a starting point, try to get HEAD for this. |
622 if opts.bad: | 621 if opts.bad: |
623 bad_rev = opts.bad | 622 bad_rev = opts.bad |
624 else: | 623 else: |
625 bad_rev = 0 | 624 bad_rev = '999.0.0.0' |
626 try: | 625 if not opts.official_builds: |
627 # Location of the latest build revision number | 626 bad_rev = GetChromiumRevision(context.GetLastChangeURL()) |
628 nh = urllib.urlopen(context.GetLastChangeURL()) | |
629 latest = int(nh.read()) | |
630 nh.close() | |
631 bad_rev = raw_input('Bad revision [HEAD:%d]: ' % latest) | |
632 if (bad_rev == ''): | |
633 bad_rev = latest | |
634 bad_rev = int(bad_rev) | |
635 except Exception, e: | |
636 print('Could not determine latest revision. This could be bad...') | |
637 bad_rev = int(raw_input('Bad revision: ')) | |
638 | 627 |
639 # Find out when we were good. | 628 # Find out when we were good. |
640 if opts.good: | 629 if opts.good: |
641 good_rev = opts.good | 630 good_rev = opts.good |
642 else: | 631 else: |
643 good_rev = 0 | 632 good_rev = '0.0.0.0' if opts.official_builds else 0 |
644 try: | 633 |
645 good_rev = int(raw_input('Last known good [0]: ')) | 634 if opts.official_builds: |
646 except Exception, e: | 635 good_rev = LooseVersion(good_rev) |
647 pass | 636 bad_rev = LooseVersion(bad_rev) |
637 else: | |
638 good_rev = int(good_rev) | |
639 bad_rev = int(bad_rev) | |
640 | |
641 if good_rev > bad_rev: | |
642 print ('The good revision (%s) must precede the bad revision (%s).\n' % | |
643 (good_rev, bad_rev)) | |
644 parser.print_help() | |
645 return 1 | |
648 | 646 |
649 if opts.times < 1: | 647 if opts.times < 1: |
650 print('Number of times to run (%d) must be greater than or equal to 1.' % | 648 print('Number of times to run (%d) must be greater than or equal to 1.' % |
651 opts.times) | 649 opts.times) |
652 parser.print_help() | 650 parser.print_help() |
653 return 1 | 651 return 1 |
654 | 652 |
655 (last_known_good_rev, first_known_bad_rev) = Bisect( | 653 (last_known_good_rev, first_known_bad_rev) = Bisect( |
656 opts.archive, opts.official_builds, good_rev, bad_rev, opts.times, args, | 654 opts.archive, opts.official_builds, good_rev, bad_rev, opts.times, args, |
657 opts.profile) | 655 opts.profile) |
(...skipping 15 matching lines...) Expand all Loading... | |
673 print ' ' + WEBKIT_CHANGELOG_URL % (first_known_bad_webkit_rev, | 671 print ' ' + WEBKIT_CHANGELOG_URL % (first_known_bad_webkit_rev, |
674 last_known_good_webkit_rev) | 672 last_known_good_webkit_rev) |
675 print 'CHANGELOG URL:' | 673 print 'CHANGELOG URL:' |
676 if opts.official_builds: | 674 if opts.official_builds: |
677 print OFFICIAL_CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev) | 675 print OFFICIAL_CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev) |
678 else: | 676 else: |
679 print ' ' + CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev) | 677 print ' ' + CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev) |
680 | 678 |
681 if __name__ == '__main__': | 679 if __name__ == '__main__': |
682 sys.exit(main()) | 680 sys.exit(main()) |
OLD | NEW |