Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: tools/bisect-builds.py

Issue 10704213: Bisect official build with providing bad/good revisions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698