OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
197 def ParseDirectoryIndexRecent(context): | 197 def ParseDirectoryIndexRecent(context): |
198 """Parses the recent builds directory listing into a list of revision | 198 """Parses the recent builds directory listing into a list of revision |
199 numbers.""" | 199 numbers.""" |
200 handle = urllib.urlopen(context.GetListingURLRecent()) | 200 handle = urllib.urlopen(context.GetListingURLRecent()) |
201 document = handle.read() | 201 document = handle.read() |
202 | 202 |
203 # Looking for: <a href="92976/">92976/</a> | 203 # Looking for: <a href="92976/">92976/</a> |
204 return re.findall(r"<a href=\"(\d+)/\">\1/</a>", document) | 204 return re.findall(r"<a href=\"(\d+)/\">\1/</a>", document) |
205 | 205 |
206 | 206 |
207 def GetRevList(context): | 207 def GetRevList(context, revlist): |
208 """Gets the list of revision numbers between |good_revision| and | 208 """Gets the list of revision numbers between |good_revision| and |
209 |bad_revision| of the |context|.""" | 209 |bad_revision| of the |context|.""" |
Nico
2011/07/21 16:17:31
Update docstring.
jbates
2011/07/21 17:36:01
Done.
| |
210 # Download the revlist and filter for just the range between good and bad. | 210 # Download the revlist and filter for just the range between good and bad. |
211 rev_range = range(context.good_revision, context.bad_revision) | 211 rev_range = range(context.good_revision, context.bad_revision) |
212 revisions = [] | |
213 if context.use_recent: | |
214 revisions = ParseDirectoryIndexRecent(context) | |
215 else: | |
216 revisions = ParseDirectoryIndex(context) | |
217 revlist = map(int, revisions) | |
218 revlist = filter(lambda r: r in rev_range, revlist) | 212 revlist = filter(lambda r: r in rev_range, revlist) |
219 revlist.sort() | 213 revlist.sort() |
220 return revlist | 214 return revlist |
221 | 215 |
222 | 216 |
223 def TryRevision(context, rev, profile, args): | 217 def TryRevision(context, rev, profile, args): |
224 """Downloads revision |rev|, unzips it, and opens it for the user to test. | 218 """Downloads revision |rev|, unzips it, and opens it for the user to test. |
225 |profile| is the profile to use.""" | 219 |profile| is the profile to use.""" |
226 # Do this in a temp dir so we don't collide with user files. | 220 # Do this in a temp dir so we don't collide with user files. |
227 cwd = os.getcwd() | 221 cwd = os.getcwd() |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 choices = choices, | 332 choices = choices, |
339 help = 'The buildbot archive to bisect [%s].' % | 333 help = 'The buildbot archive to bisect [%s].' % |
340 '|'.join(choices)) | 334 '|'.join(choices)) |
341 parser.add_option('-b', '--bad', type = 'int', | 335 parser.add_option('-b', '--bad', type = 'int', |
342 help = 'The bad revision to bisect to.') | 336 help = 'The bad revision to bisect to.') |
343 parser.add_option('-g', '--good', type = 'int', | 337 parser.add_option('-g', '--good', type = 'int', |
344 help = 'The last known good revision to bisect from.') | 338 help = 'The last known good revision to bisect from.') |
345 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', | 339 parser.add_option('-p', '--profile', '--user-data-dir', type = 'str', |
346 help = 'Profile to use; this will not reset every run. ' + | 340 help = 'Profile to use; this will not reset every run. ' + |
347 'Defaults to a clean profile.', default = 'profile') | 341 'Defaults to a clean profile.', default = 'profile') |
348 parser.add_option('-r', '--recent', | |
349 dest = "recent", | |
350 default = False, | |
351 action = "store_true", | |
352 help = 'Use recent builds from about the last 2 months ' + | |
353 'for higher granularity bisecting.') | |
354 (opts, args) = parser.parse_args() | 342 (opts, args) = parser.parse_args() |
355 | 343 |
356 if opts.archive is None: | 344 if opts.archive is None: |
357 print 'Error: missing required parameter: --archive' | 345 print 'Error: missing required parameter: --archive' |
358 print | 346 print |
359 parser.print_help() | 347 parser.print_help() |
360 return 1 | 348 return 1 |
361 | 349 |
362 if opts.bad and opts.good and (opts.good > opts.bad): | 350 if opts.bad and opts.good and (opts.good > opts.bad): |
363 print ('The good revision (%d) must precede the bad revision (%d).\n' % | 351 print ('The good revision (%d) must precede the bad revision (%d).\n' % |
364 (opts.good, opts.bad)) | 352 (opts.good, opts.bad)) |
365 parser.print_help() | 353 parser.print_help() |
366 return 1 | 354 return 1 |
367 | 355 |
368 # Create the context. Initialize 0 for the revisions as they are set below. | 356 # Create the context. Initialize 0 for the revisions as they are set below. |
369 context = PathContext(opts.archive, 0, 0, opts.recent) | 357 context = PathContext(opts.archive, 0, 0, False) |
Nico
2011/07/21 16:17:31
Name parameter (|..., use_recent=False)|) – more r
jbates
2011/07/21 17:36:01
Done.
| |
370 | 358 |
371 # Pick a starting point, try to get HEAD for this. | 359 # Pick a starting point, try to get HEAD for this. |
372 if opts.bad: | 360 if opts.bad: |
373 bad_rev = opts.bad | 361 bad_rev = opts.bad |
374 else: | 362 else: |
375 bad_rev = 0 | 363 bad_rev = 0 |
376 try: | 364 try: |
377 # Location of the latest build revision number | 365 # Location of the latest build revision number |
378 nh = urllib.urlopen(context.GetLastChangeURL()) | 366 nh = urllib.urlopen(context.GetLastChangeURL()) |
379 latest = int(nh.read()) | 367 latest = int(nh.read()) |
(...skipping 13 matching lines...) Expand all Loading... | |
393 good_rev = 0 | 381 good_rev = 0 |
394 try: | 382 try: |
395 good_rev = int(raw_input('Last known good [0]: ')) | 383 good_rev = int(raw_input('Last known good [0]: ')) |
396 except Exception, e: | 384 except Exception, e: |
397 pass | 385 pass |
398 | 386 |
399 # Set the input parameters now that they've been validated. | 387 # Set the input parameters now that they've been validated. |
400 context.good_revision = good_rev | 388 context.good_revision = good_rev |
401 context.bad_revision = bad_rev | 389 context.bad_revision = bad_rev |
402 | 390 |
391 # Get recent revision list and check whether it's sufficient. | |
392 all_revs_recent = map(int, ParseDirectoryIndexRecent(context)) | |
393 all_revs_recent.sort() | |
Nico
2011/07/21 16:17:31
I think you want
all_revs_recent = all_revs_re
jbates
2011/07/21 17:36:01
Done.
| |
394 # Skipping 0 since it might be deleted off the server soon: | |
395 oldest_recent_rev = all_revs_recent[1] | |
Nico
2011/07/21 16:17:31
and then use [0] here
jbates
2011/07/21 17:36:01
Done.
| |
396 if good_rev >= oldest_recent_rev: | |
397 # The range is within recent builds, so switch on use_recent. | |
398 context.use_recent = True | |
399 elif bad_rev >= oldest_recent_rev: | |
400 # The range spans both old and recent builds. | |
401 # If oldest_recent_rev is good, we bisect the recent builds. | |
402 context.use_recent = True # set True to test recent build | |
Nico
2011/07/21 16:17:31
Remove comment
jbates
2011/07/21 17:36:01
Done.
| |
403 TryRevision(context, oldest_recent_rev, opts.profile, args) | |
404 if AskIsGoodBuild(oldest_recent_rev): | |
405 # context.use_recent is True | |
406 context.good_revision = oldest_recent_rev | |
407 else: | |
408 context.use_recent = False | |
409 context.bad_revision = oldest_recent_rev | |
410 | |
411 all_revs = [] | |
412 if context.use_recent: | |
413 all_revs = all_revs_recent | |
Nico
2011/07/21 16:17:31
This includes 0 -- probably not intentional? (henc
jbates
2011/07/21 17:36:01
0 would be filtered out before bisecting, but I li
| |
414 else: | |
415 all_revs = map(int, ParseDirectoryIndex(context)) | |
416 | |
403 # Get a list of revisions to bisect across. | 417 # Get a list of revisions to bisect across. |
404 revlist = GetRevList(context) | 418 revlist = GetRevList(context, all_revs) |
405 if len(revlist) < 2: # Don't have enough builds to bisect | 419 if len(revlist) < 2: # Don't have enough builds to bisect |
406 print 'We don\'t have enough builds to bisect. revlist: %s' % revlist | 420 print 'We don\'t have enough builds to bisect. revlist: %s' % revlist |
407 sys.exit(1) | 421 sys.exit(1) |
408 | 422 |
409 (last_known_good_rev, first_known_bad_rev) = Bisect( | 423 (last_known_good_rev, first_known_bad_rev) = Bisect( |
410 revlist, context, args, opts.profile) | 424 revlist, context, args, opts.profile) |
411 | 425 |
412 # We're done. Let the user know the results in an official manner. | 426 # We're done. Let the user know the results in an official manner. |
413 print('You are probably looking for build %d.' % first_known_bad_rev) | 427 print('You are probably looking for build %d.' % first_known_bad_rev) |
414 print('CHANGELOG URL:') | 428 print('CHANGELOG URL:') |
415 print(CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)) | 429 print(CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)) |
416 print('Built at revision:') | 430 print('Built at revision:') |
417 print(BUILD_VIEWVC_URL % first_known_bad_rev) | 431 print(BUILD_VIEWVC_URL % first_known_bad_rev) |
418 | 432 |
419 if __name__ == '__main__': | 433 if __name__ == '__main__': |
420 sys.exit(main()) | 434 sys.exit(main()) |
OLD | NEW |