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

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

Issue 7606001: Restored download progress, and cleaned up terminal messages. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nit Created 9 years, 4 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/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 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 out.write(zf.read(name)) 192 out.write(zf.read(name))
193 out.close() 193 out.close()
194 # Set permissions. Permission info in external_attr is shifted 16 bits. 194 # Set permissions. Permission info in external_attr is shifted 16 bits.
195 os.chmod(name, info.external_attr >> 16L) 195 os.chmod(name, info.external_attr >> 16L)
196 os.chdir(cwd) 196 os.chdir(cwd)
197 except Exception, e: 197 except Exception, e:
198 print >>sys.stderr, e 198 print >>sys.stderr, e
199 sys.exit(1) 199 sys.exit(1)
200 200
201 201
202 def FetchRevision(context, rev, filename, quit_event=None): 202 def FetchRevision(context, rev, filename, quit_event=None, progress_event=None):
203 """Downloads and unzips revision |rev|. 203 """Downloads and unzips revision |rev|.
204 @param context A PathContext instance. 204 @param context A PathContext instance.
205 @param rev The Chromium revision number/tag to download. 205 @param rev The Chromium revision number/tag to download.
206 @param filename The destination for the downloaded file. 206 @param filename The destination for the downloaded file.
207 @param quit_event A threading.Event which will be set by the master thread to 207 @param quit_event A threading.Event which will be set by the master thread to
208 indicate that the download should be aborted. 208 indicate that the download should be aborted.
209 @param progress_event A threading.Event which will be set by the master thread
210 to indicate that the progress of the download should be
211 displayed.
209 """ 212 """
210 def ReportHook(blocknum, blocksize, totalsize): 213 def ReportHook(blocknum, blocksize, totalsize):
211 if quit_event and quit_event.is_set(): 214 if quit_event and quit_event.is_set():
212 raise RuntimeError("Aborting download of revision %d" % rev) 215 raise RuntimeError("Aborting download of revision %d" % rev)
216 if progress_event and progress_event.is_set():
217 size = blocknum * blocksize
218 if totalsize == -1: # Total size not known.
219 progress = "Received %d bytes" % size
220 else:
221 size = min(totalsize, size)
222 progress = "Received %d of %d bytes, %.2f%%" % (
223 size, totalsize, 100.0 * size / totalsize)
224 # Send a \r to let all progress messages use just one line of output.
225 sys.stdout.write("\r" + progress)
226 sys.stdout.flush()
213 227
214 download_url = context.GetDownloadURL(rev) 228 download_url = context.GetDownloadURL(rev)
215 try: 229 try:
216 urllib.urlretrieve(download_url, filename, ReportHook) 230 urllib.urlretrieve(download_url, filename, ReportHook)
231 if progress_event and progress_event.is_set():
232 print()
217 except RuntimeError, e: 233 except RuntimeError, e:
218 pass 234 pass
219 235
220 236
221 def RunRevision(context, revision, zipfile, profile, args): 237 def RunRevision(context, revision, zipfile, profile, args):
222 """Given a zipped revision, unzip it and run the test.""" 238 """Given a zipped revision, unzip it and run the test."""
223 print "Trying revision %d..." % revision 239 print "Trying revision %d..." % revision
224 240
225 # Create a temp directory and unzip the revision into it. 241 # Create a temp directory and unzip the revision into it.
226 cwd = os.getcwd() 242 cwd = os.getcwd()
(...skipping 14 matching lines...) Expand all
241 shutil.rmtree(tempdir, True) 257 shutil.rmtree(tempdir, True)
242 except Exception, e: 258 except Exception, e:
243 pass 259 pass
244 260
245 return (subproc.returncode, stdout, stderr) 261 return (subproc.returncode, stdout, stderr)
246 262
247 def AskIsGoodBuild(rev, status, stdout, stderr): 263 def AskIsGoodBuild(rev, status, stdout, stderr):
248 """Ask the user whether build |rev| is good or bad.""" 264 """Ask the user whether build |rev| is good or bad."""
249 # Loop until we get a response that we can parse. 265 # Loop until we get a response that we can parse.
250 while True: 266 while True:
251 response = raw_input('\nRevision %d is [(g)ood/(b)ad/(q)uit]: ' % int(rev)) 267 response = raw_input('Revision %d is [(g)ood/(b)ad/(q)uit]: ' % int(rev))
252 if response and response in ('g', 'b'): 268 if response and response in ('g', 'b'):
253 return response == 'g' 269 return response == 'g'
254 if response and response == 'q': 270 if response and response == 'q':
255 raise SystemExit() 271 raise SystemExit()
256 272
257 def Bisect(platform, 273 def Bisect(platform,
258 good_rev=0, 274 good_rev=0,
259 bad_rev=0, 275 bad_rev=0,
260 try_args=(), 276 try_args=(),
261 profile=None, 277 profile=None,
(...skipping 26 matching lines...) Expand all
288 304
289 if not profile: 305 if not profile:
290 profile = 'profile' 306 profile = 'profile'
291 307
292 context = PathContext(platform, good_rev, bad_rev) 308 context = PathContext(platform, good_rev, bad_rev)
293 cwd = os.getcwd() 309 cwd = os.getcwd()
294 310
295 _GetDownloadPath = lambda rev: os.path.join(cwd, 311 _GetDownloadPath = lambda rev: os.path.join(cwd,
296 '%d-%s' % (rev, context.archive_name)) 312 '%d-%s' % (rev, context.archive_name))
297 313
314 print "Downloading list of known revisions..."
315
298 revlist = context.GetRevList() 316 revlist = context.GetRevList()
299 317
300 # Get a list of revisions to bisect across. 318 # Get a list of revisions to bisect across.
301 if len(revlist) < 2: # Don't have enough builds to bisect. 319 if len(revlist) < 2: # Don't have enough builds to bisect.
302 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist 320 msg = 'We don\'t have enough builds to bisect. revlist: %s' % revlist
303 raise RuntimeError(msg) 321 raise RuntimeError(msg)
304 322
305 # Figure out our bookends and first pivot point; fetch the pivot revision. 323 # Figure out our bookends and first pivot point; fetch the pivot revision.
306 good = 0 324 good = 0
307 bad = len(revlist) - 1 325 bad = len(revlist) - 1
308 pivot = bad / 2 326 pivot = bad / 2
309 rev = revlist[pivot] 327 rev = revlist[pivot]
310 zipfile = _GetDownloadPath(rev) 328 zipfile = _GetDownloadPath(rev)
329 progress_event = threading.Event()
330 progress_event.set()
311 print "Downloading revision %d..." % rev 331 print "Downloading revision %d..." % rev
312 FetchRevision(context, rev, zipfile) 332 FetchRevision(context, rev, zipfile, None, progress_event)
Nico 2011/08/09 18:34:08 I'd do FetchRevision(..., quit_event=None, progr
szager1 2011/08/09 18:39:06 Done.
313 333
314 # Binary search time! 334 # Binary search time!
315 while zipfile and bad - good > 1: 335 while zipfile and bad - good > 1:
316 # Pre-fetch next two possible pivots 336 # Pre-fetch next two possible pivots
317 # - down_pivot is the next revision to check if the current revision turns 337 # - down_pivot is the next revision to check if the current revision turns
318 # out to be bad. 338 # out to be bad.
319 # - up_pivot is the next revision to check if the current revision turns 339 # - up_pivot is the next revision to check if the current revision turns
320 # out to be good. 340 # out to be good.
321 down_pivot = int((pivot - good) / 2) + good 341 down_pivot = int((pivot - good) / 2) + good
322 down_thread = None 342 down_thread = None
323 if down_pivot != pivot and down_pivot != good: 343 if down_pivot != pivot and down_pivot != good:
324 down_rev = revlist[down_pivot] 344 down_rev = revlist[down_pivot]
325 down_zipfile = _GetDownloadPath(down_rev) 345 down_zipfile = _GetDownloadPath(down_rev)
326 down_event = threading.Event() 346 down_quit_event = threading.Event()
327 fetchargs = (context, down_rev, down_zipfile, down_event) 347 down_progress_event = threading.Event()
348 fetchargs = (context,
349 down_rev,
350 down_zipfile,
351 down_quit_event,
352 down_progress_event)
328 down_thread = threading.Thread(target=FetchRevision, 353 down_thread = threading.Thread(target=FetchRevision,
329 name='down_fetch', 354 name='down_fetch',
330 args=fetchargs) 355 args=fetchargs)
331 down_thread.start() 356 down_thread.start()
332 357
333 up_pivot = int((bad - pivot) / 2) + pivot 358 up_pivot = int((bad - pivot) / 2) + pivot
334 up_thread = None 359 up_thread = None
335 if up_pivot != pivot and up_pivot != bad: 360 if up_pivot != pivot and up_pivot != bad:
336 up_rev = revlist[up_pivot] 361 up_rev = revlist[up_pivot]
337 up_zipfile = _GetDownloadPath(up_rev) 362 up_zipfile = _GetDownloadPath(up_rev)
338 up_event = threading.Event() 363 up_quit_event = threading.Event()
339 fetchargs = (context, up_rev, up_zipfile, up_event) 364 up_progress_event = threading.Event()
365 fetchargs = (context,
366 up_rev,
367 up_zipfile,
368 up_quit_event,
369 up_progress_event)
340 up_thread = threading.Thread(target=FetchRevision, 370 up_thread = threading.Thread(target=FetchRevision,
341 name='up_fetch', 371 name='up_fetch',
342 args=fetchargs) 372 args=fetchargs)
343 up_thread.start() 373 up_thread.start()
344 374
345 # Run test on the pivot revision. 375 # Run test on the pivot revision.
346 (status, stdout, stderr) = RunRevision(context, 376 (status, stdout, stderr) = RunRevision(context,
347 rev, 377 rev,
348 zipfile, 378 zipfile,
349 profile, 379 profile,
350 try_args) 380 try_args)
351 os.unlink(zipfile) 381 os.unlink(zipfile)
352 zipfile = None 382 zipfile = None
353 383
354 # Call the predicate function to see if the current revision is good or bad. 384 # Call the predicate function to see if the current revision is good or bad.
355 # On that basis, kill one of the background downloads and complete the 385 # On that basis, kill one of the background downloads and complete the
356 # other, as described in the comments above. 386 # other, as described in the comments above.
357 try: 387 try:
358 if predicate(rev, status, stdout, stderr): 388 if predicate(rev, status, stdout, stderr):
359 good = pivot 389 good = pivot
360 if down_thread: 390 if down_thread:
361 down_event.set() # Kill the download of older revision. 391 down_quit_event.set() # Kill the download of older revision.
362 down_thread.join() 392 down_thread.join()
363 os.unlink(down_zipfile) 393 os.unlink(down_zipfile)
364 if up_thread: 394 if up_thread:
365 print "Downloading revision %d..." % up_rev 395 print "Downloading revision %d..." % up_rev
396 up_progress_event.set() # Display progress of download.
366 up_thread.join() # Wait for newer revision to finish downloading. 397 up_thread.join() # Wait for newer revision to finish downloading.
367 pivot = up_pivot 398 pivot = up_pivot
368 zipfile = up_zipfile 399 zipfile = up_zipfile
369 else: 400 else:
370 bad = pivot 401 bad = pivot
371 if up_thread: 402 if up_thread:
372 up_event.set() # Kill download of newer revision. 403 up_quit_event.set() # Kill download of newer revision.
373 up_thread.join() 404 up_thread.join()
374 os.unlink(up_zipfile) 405 os.unlink(up_zipfile)
375 if down_thread: 406 if down_thread:
376 print "Downloading revision %d..." % down_rev 407 print "Downloading revision %d..." % down_rev
408 down_progress_event.set() # Display progress of download.
377 down_thread.join() # Wait for older revision to finish downloading. 409 down_thread.join() # Wait for older revision to finish downloading.
378 pivot = down_pivot 410 pivot = down_pivot
379 zipfile = down_zipfile 411 zipfile = down_zipfile
380 except SystemExit: 412 except SystemExit:
413 print "Cleaning up..."
381 for f in [down_zipfile, up_zipfile]: 414 for f in [down_zipfile, up_zipfile]:
382 try: 415 try:
383 os.unlink(f) 416 os.unlink(f)
384 except OSError: 417 except OSError:
385 pass 418 pass
386 sys.exit(0) 419 sys.exit(0)
387 420
388 rev = revlist[pivot] 421 rev = revlist[pivot]
389 422
390 return (revlist[good], revlist[bad]) 423 return (revlist[good], revlist[bad])
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 493
461 # We're done. Let the user know the results in an official manner. 494 # We're done. Let the user know the results in an official manner.
462 print('You are probably looking for build %d.' % first_known_bad_rev) 495 print('You are probably looking for build %d.' % first_known_bad_rev)
463 print('CHANGELOG URL:') 496 print('CHANGELOG URL:')
464 print(CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev)) 497 print(CHANGELOG_URL % (last_known_good_rev, first_known_bad_rev))
465 print('Built at revision:') 498 print('Built at revision:')
466 print(BUILD_VIEWVC_URL % first_known_bad_rev) 499 print(BUILD_VIEWVC_URL % first_known_bad_rev)
467 500
468 if __name__ == '__main__': 501 if __name__ == '__main__':
469 sys.exit(main()) 502 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