Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 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 """Performance Test Bisect Tool | 6 """Performance Test Bisect Tool |
| 7 | 7 |
| 8 This script bisects a series of changelists using binary search. It starts at | 8 This script bisects a series of changelists using binary search. It starts at |
| 9 a bad revision where a performance metric has regressed, and asks for a last | 9 a bad revision where a performance metric has regressed, and asks for a last |
| 10 known-good revision. It will then binary search across this revision range by | 10 known-good revision. It will then binary search across this revision range by |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 176 @@ -0,0 +1 @@ | 176 @@ -0,0 +1 @@ |
| 177 +%(deps_sha)s | 177 +%(deps_sha)s |
| 178 """ | 178 """ |
| 179 | 179 |
| 180 # The possible values of the --bisect_mode flag, which determines what to | 180 # The possible values of the --bisect_mode flag, which determines what to |
| 181 # use when classifying a revision as "good" or "bad". | 181 # use when classifying a revision as "good" or "bad". |
| 182 BISECT_MODE_MEAN = 'mean' | 182 BISECT_MODE_MEAN = 'mean' |
| 183 BISECT_MODE_STD_DEV = 'std_dev' | 183 BISECT_MODE_STD_DEV = 'std_dev' |
| 184 BISECT_MODE_RETURN_CODE = 'return_code' | 184 BISECT_MODE_RETURN_CODE = 'return_code' |
| 185 | 185 |
| 186 # The perf dashboard specifically looks for the string | |
| 187 # "Estimated Confidence: 95%" to decide whether or not | |
| 188 # to cc the author(s). If you change this, please update the perf | |
| 189 # dashboard as well. | |
| 190 RESULTS_BANNER = """ | |
| 191 ===== BISECT JOB RESULTS ===== | |
| 192 Status: %s | |
| 193 | |
| 194 Test Command: %s | |
| 195 Test Metric: %s | |
| 196 Relative Change: %s | |
| 197 Estimated Confidence: %d%%""" | |
| 198 | |
| 199 # The perf dashboard specifically looks for the string | |
| 200 # "Author : " to parse out who to cc on a bug. If you change the | |
| 201 # formatting here, please update the perf dashboard as well. | |
| 202 RESULTS_REVISION_INFO = """ | |
| 203 ===== SUSPECTED CL(s) ===== | |
| 204 Subject : %(subject)s | |
| 205 Author : %(author)s%(email_info)s%(commit_info)s | |
| 206 Date : %(cl_date)s""" | |
| 207 | |
| 208 REPRO_STEPS_LOCAL =""" | |
|
shatch
2014/07/10 23:03:09
nit: space here and on the thankyou
prasadv
2014/07/11 00:34:54
Done.
| |
| 209 ==== INSTRUCTIONS TO REPRODUCE ==== | |
| 210 To run locally: | |
| 211 $%(command)s""" | |
| 212 | |
| 213 REPRO_STEPS_TRYJOB = """ | |
| 214 To reproduce on Performance trybot: | |
| 215 1. Create new git branch or check out existing branch. | |
| 216 2. Edit tools/run-perf-test.cfg (instructions in file) or \ | |
| 217 third_party/WebKit/Tools/run-perf-test.cfg. | |
| 218 a) Take care to strip any src/ directories from the head of \ | |
| 219 relative path names. | |
| 220 b) On desktop, only --browser=release is supported, on android \ | |
| 221 --browser=android-chromium-testshell. | |
| 222 c) Test command to use: %(command)s | |
| 223 3. Upload your patch. --bypass-hooks is necessary to upload the changes you \ | |
| 224 committed locally to run-perf-test.cfg. | |
| 225 Note: *DO NOT* commit run-perf-test.cfg changes to the project repository. | |
| 226 $ git cl upload --bypass-hooks | |
| 227 4. Send your try job to the tryserver. \ | |
| 228 [Please make sure to use appropriate bot to reproduce] | |
| 229 $ git cl try -m tryserver.chromium.perf -b <bot> | |
| 230 | |
| 231 For more details please visit \nhttps://sites.google.com/a/chromium.org/dev/\ | |
| 232 developers/performance-try-bots""" | |
| 233 | |
| 234 RESULTS_THANKYOU =""" | |
| 235 ===== THANK YOU FOR CHOOSING BISECT AIRLINES ===== | |
| 236 Visit http://www.chromium.org/developers/core-principles for Chrome's policy | |
| 237 on perf regressions. | |
| 238 Contact chrome-perf-dashboard-team with any questions or suggestions about | |
| 239 bisecting. | |
| 240 .------. | |
| 241 .---. \ \==) | |
| 242 |PERF\ \ \\ | |
| 243 | ---------'-------'-----------. | |
| 244 . 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 `-. | |
| 245 \______________.-------._______________) | |
| 246 / / | |
| 247 / / | |
| 248 / /==) | |
| 249 ._______.""" | |
| 250 | |
| 186 | 251 |
| 187 def _AddAdditionalDepotInfo(depot_info): | 252 def _AddAdditionalDepotInfo(depot_info): |
| 188 """Adds additional depot info to the global depot variables.""" | 253 """Adds additional depot info to the global depot variables.""" |
| 189 global DEPOT_DEPS_NAME | 254 global DEPOT_DEPS_NAME |
| 190 global DEPOT_NAMES | 255 global DEPOT_NAMES |
| 191 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() + | 256 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() + |
| 192 depot_info.items()) | 257 depot_info.items()) |
| 193 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() | 258 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() |
| 194 | 259 |
| 195 | 260 |
| (...skipping 3005 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3201 results_dict['last_broken_revision'], | 3266 results_dict['last_broken_revision'], |
| 3202 100, final_step=False) | 3267 100, final_step=False) |
| 3203 | 3268 |
| 3204 def _PrintConfidence(self, results_dict): | 3269 def _PrintConfidence(self, results_dict): |
| 3205 # The perf dashboard specifically looks for the string | 3270 # The perf dashboard specifically looks for the string |
| 3206 # "Confidence in Bisection Results: 100%" to decide whether or not | 3271 # "Confidence in Bisection Results: 100%" to decide whether or not |
| 3207 # to cc the author(s). If you change this, please update the perf | 3272 # to cc the author(s). If you change this, please update the perf |
| 3208 # dashboard as well. | 3273 # dashboard as well. |
| 3209 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence'] | 3274 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence'] |
| 3210 | 3275 |
| 3276 def _ConfidenceLevelStatus(self, results_dict): | |
| 3277 if not results_dict['confidence']: | |
| 3278 return None | |
| 3279 confidence_status = 'Successful with %(level)s confidence%(warning)s.' | |
| 3280 if results_dict['confidence'] >= 95: | |
| 3281 level = 'high' | |
| 3282 else: | |
| 3283 level = 'low' | |
| 3284 warning = ' and warnings' | |
| 3285 if not self.warnings: | |
| 3286 warning = '' | |
| 3287 return confidence_status % {'level': level, 'warning': warning} | |
| 3288 | |
| 3289 def _PrintThankYou(self): | |
| 3290 print RESULTS_THANKYOU | |
| 3291 | |
| 3211 def _PrintBanner(self, results_dict): | 3292 def _PrintBanner(self, results_dict): |
| 3212 print | |
| 3213 print " __o_\___ Aw Snap! We hit a speed bump!" | |
| 3214 print "=-O----O-'__.~.___________________________________" | |
| 3215 print | |
| 3216 if self._IsBisectModeReturnCode(): | 3293 if self._IsBisectModeReturnCode(): |
| 3217 print ('Bisect reproduced a change in return codes while running the ' | 3294 metrics = 'N/A' |
| 3218 'performance test.') | 3295 change = 'Yes' |
| 3219 else: | 3296 else: |
| 3220 print ('Bisect reproduced a %.02f%% (+-%.02f%%) change in the ' | 3297 metrics = '/'.join(self.opts.metric) |
| 3221 '%s metric.' % (results_dict['regression_size'], | 3298 change = '%.02f%% (+/-%.02f%%)' % ( |
| 3222 results_dict['regression_std_err'], '/'.join(self.opts.metric))) | 3299 results_dict['regression_size'], results_dict['regression_std_err']) |
| 3223 self._PrintConfidence(results_dict) | 3300 |
| 3301 if results_dict['culprit_revisions'] and results_dict['confidence']: | |
| 3302 status = self._ConfidenceLevelStatus(results_dict) | |
| 3303 else: | |
| 3304 status = 'Failure, could not reproduce.' | |
| 3305 change = 'Bisect could not reproduce a change.' | |
| 3306 print RESULTS_BANNER % ( | |
|
shatch
2014/07/10 23:03:09
I like your use of keyword arguments for the other
prasadv
2014/07/11 00:34:54
Done.
| |
| 3307 status, | |
| 3308 self.opts.command, | |
| 3309 metrics, | |
| 3310 change, | |
| 3311 results_dict['confidence'], | |
| 3312 ) | |
| 3313 | |
| 3224 | 3314 |
| 3225 def _PrintFailedBanner(self, results_dict): | 3315 def _PrintFailedBanner(self, results_dict): |
| 3226 print | 3316 print |
| 3227 if self._IsBisectModeReturnCode(): | 3317 if self._IsBisectModeReturnCode(): |
| 3228 print 'Bisect could not reproduce a change in the return code.' | 3318 print 'Bisect could not reproduce a change in the return code.' |
| 3229 else: | 3319 else: |
| 3230 print ('Bisect could not reproduce a change in the ' | 3320 print ('Bisect could not reproduce a change in the ' |
| 3231 '%s metric.' % '/'.join(self.opts.metric)) | 3321 '%s metric.' % '/'.join(self.opts.metric)) |
| 3232 print | 3322 print |
| 3233 | 3323 |
| 3234 def _GetViewVCLinkFromDepotAndHash(self, cl, depot): | 3324 def _GetViewVCLinkFromDepotAndHash(self, cl, depot): |
| 3235 info = self.source_control.QueryRevisionInfo(cl, | 3325 info = self.source_control.QueryRevisionInfo(cl, |
| 3236 self._GetDepotDirectory(depot)) | 3326 self._GetDepotDirectory(depot)) |
| 3237 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'): | 3327 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'): |
| 3238 try: | 3328 try: |
| 3239 # Format is "git-svn-id: svn://....@123456 <other data>" | 3329 # Format is "git-svn-id: svn://....@123456 <other data>" |
| 3240 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i] | 3330 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i] |
| 3241 svn_revision = svn_line[0].split('@') | 3331 svn_revision = svn_line[0].split('@') |
| 3242 svn_revision = svn_revision[1].split(' ')[0] | 3332 svn_revision = svn_revision[1].split(' ')[0] |
| 3243 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision | 3333 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision |
| 3244 except IndexError: | 3334 except IndexError: |
| 3245 return '' | 3335 return '' |
| 3246 return '' | 3336 return '' |
| 3247 | 3337 |
| 3248 def _PrintRevisionInfo(self, cl, info, depot=None): | 3338 def _PrintRevisionInfo(self, cl, info, depot=None): |
| 3249 # The perf dashboard specifically looks for the string | 3339 email_info = '' |
| 3250 # "Author : " to parse out who to cc on a bug. If you change the | |
| 3251 # formatting here, please update the perf dashboard as well. | |
| 3252 print | |
| 3253 print 'Subject : %s' % info['subject'] | |
| 3254 print 'Author : %s' % info['author'] | |
| 3255 if not info['email'].startswith(info['author']): | 3340 if not info['email'].startswith(info['author']): |
| 3256 print 'Email : %s' % info['email'] | 3341 email_info = '\nEmail : %s' % info['email'] |
| 3257 commit_link = self._GetViewVCLinkFromDepotAndHash(cl, depot) | 3342 commit_link = self._GetViewVCLinkFromDepotAndHash(cl, depot) |
| 3258 if commit_link: | 3343 if commit_link: |
| 3259 print 'Link : %s' % commit_link | 3344 commit_info = '\nLink : %s' % commit_link |
| 3260 else: | 3345 else: |
| 3261 print | 3346 commit_info = ('\nFailed to parse svn revision from body:\n%s' % |
| 3262 print 'Failed to parse svn revision from body:' | 3347 info['body']) |
| 3263 print | 3348 print RESULTS_REVISION_INFO % { |
| 3264 print info['body'] | 3349 'subject': info['subject'], |
| 3265 print | 3350 'author': info['author'], |
| 3266 print 'Commit : %s' % cl | 3351 'email_info': email_info, |
| 3267 print 'Date : %s' % info['date'] | 3352 'commit_info': commit_info, |
| 3353 'cl_date': info['date'] | |
| 3354 } | |
| 3268 | 3355 |
| 3269 def _PrintTableRow(self, column_widths, row_data): | 3356 def _PrintTableRow(self, column_widths, row_data): |
| 3270 assert len(column_widths) == len(row_data) | 3357 assert len(column_widths) == len(row_data) |
| 3271 | 3358 |
| 3272 text = '' | 3359 text = '' |
| 3273 for i in xrange(len(column_widths)): | 3360 for i in xrange(len(column_widths)): |
| 3274 current_row_data = row_data[i].center(column_widths[i], ' ') | 3361 current_row_data = row_data[i].center(column_widths[i], ' ') |
| 3275 text += ('%%%ds' % column_widths[i]) % current_row_data | 3362 text += ('%%%ds' % column_widths[i]) % current_row_data |
| 3276 print text | 3363 print text |
| 3277 | 3364 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3311 mean = '%d' % current_data['value']['mean'] | 3398 mean = '%d' % current_data['value']['mean'] |
| 3312 self._PrintTableRow( | 3399 self._PrintTableRow( |
| 3313 [20, 70, 14, 13], | 3400 [20, 70, 14, 13], |
| 3314 [current_data['depot'], cl_link, mean, state_str]) | 3401 [current_data['depot'], cl_link, mean, state_str]) |
| 3315 | 3402 |
| 3316 def _PrintTestedCommitsTable(self, revision_data_sorted, | 3403 def _PrintTestedCommitsTable(self, revision_data_sorted, |
| 3317 first_working_revision, last_broken_revision, confidence, | 3404 first_working_revision, last_broken_revision, confidence, |
| 3318 final_step=True): | 3405 final_step=True): |
| 3319 print | 3406 print |
| 3320 if final_step: | 3407 if final_step: |
| 3321 print 'Tested commits:' | 3408 print '===== TESTED COMMITS =====' |
| 3322 else: | 3409 else: |
| 3323 print 'Partial results:' | 3410 print '===== PARTIAL RESULTS =====' |
| 3324 self._PrintTestedCommitsHeader() | 3411 self._PrintTestedCommitsHeader() |
| 3325 state = 0 | 3412 state = 0 |
| 3326 for current_id, current_data in revision_data_sorted: | 3413 for current_id, current_data in revision_data_sorted: |
| 3327 if current_data['value']: | 3414 if current_data['value']: |
| 3328 if (current_id == last_broken_revision or | 3415 if (current_id == last_broken_revision or |
| 3329 current_id == first_working_revision): | 3416 current_id == first_working_revision): |
| 3330 # If confidence is too low, don't add this empty line since it's | 3417 # If confidence is too low, don't add this empty line since it's |
| 3331 # used to put focus on a suspected CL. | 3418 # used to put focus on a suspected CL. |
| 3332 if confidence and final_step: | 3419 if confidence and final_step: |
| 3333 print | 3420 print |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 3347 state_str = '' | 3434 state_str = '' |
| 3348 state_str = state_str.center(13, ' ') | 3435 state_str = state_str.center(13, ' ') |
| 3349 | 3436 |
| 3350 cl_link = self._GetViewVCLinkFromDepotAndHash(current_id, | 3437 cl_link = self._GetViewVCLinkFromDepotAndHash(current_id, |
| 3351 current_data['depot']) | 3438 current_data['depot']) |
| 3352 if not cl_link: | 3439 if not cl_link: |
| 3353 cl_link = current_id | 3440 cl_link = current_id |
| 3354 self._PrintTestedCommitsEntry(current_data, cl_link, state_str) | 3441 self._PrintTestedCommitsEntry(current_data, cl_link, state_str) |
| 3355 | 3442 |
| 3356 def _PrintReproSteps(self): | 3443 def _PrintReproSteps(self): |
| 3357 print | 3444 command = '$ ' + self.opts.command |
| 3358 print 'To reproduce locally:' | |
| 3359 print '$ ' + self.opts.command | |
| 3360 if bisect_utils.IsTelemetryCommand(self.opts.command): | 3445 if bisect_utils.IsTelemetryCommand(self.opts.command): |
| 3361 print | 3446 command += ('\nAlso consider passing --profiler=list to see available ' |
| 3362 print 'Also consider passing --profiler=list to see available profilers.' | 3447 'profilers.') |
| 3448 print REPRO_STEPS_LOCAL % {'command': command} | |
| 3449 print REPRO_STEPS_TRYJOB % {'command': command} | |
| 3363 | 3450 |
| 3364 def _PrintOtherRegressions(self, other_regressions, revision_data): | 3451 def _PrintOtherRegressions(self, other_regressions, revision_data): |
| 3365 print | 3452 print |
| 3366 print 'Other regressions may have occurred:' | 3453 print 'Other regressions may have occurred:' |
| 3367 print ' %8s %70s %10s' % ('Depot'.center(8, ' '), | 3454 print ' %8s %70s %10s' % ('Depot'.center(8, ' '), |
| 3368 'Range'.center(70, ' '), 'Confidence'.center(10, ' ')) | 3455 'Range'.center(70, ' '), 'Confidence'.center(10, ' ')) |
| 3369 for regression in other_regressions: | 3456 for regression in other_regressions: |
| 3370 current_id, previous_id, confidence = regression | 3457 current_id, previous_id, confidence = regression |
| 3371 current_data = revision_data[current_id] | 3458 current_data = revision_data[current_id] |
| 3372 previous_data = revision_data[previous_id] | 3459 previous_data = revision_data[previous_id] |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3406 seconds=int(step_build_time_avg)) | 3493 seconds=int(step_build_time_avg)) |
| 3407 print 'Average test time : %s' % datetime.timedelta( | 3494 print 'Average test time : %s' % datetime.timedelta( |
| 3408 seconds=int(step_perf_time_avg)) | 3495 seconds=int(step_perf_time_avg)) |
| 3409 | 3496 |
| 3410 def _PrintWarnings(self): | 3497 def _PrintWarnings(self): |
| 3411 if not self.warnings: | 3498 if not self.warnings: |
| 3412 return | 3499 return |
| 3413 print | 3500 print |
| 3414 print 'WARNINGS:' | 3501 print 'WARNINGS:' |
| 3415 for w in set(self.warnings): | 3502 for w in set(self.warnings): |
| 3416 print ' !!! %s' % w | 3503 print ' !!! %s' % w |
|
shatch
2014/07/10 23:03:09
Vaguely recall from Jeff's doc that he wanted to g
prasadv
2014/07/11 00:34:54
Done.
| |
| 3417 | 3504 |
| 3418 def _FindOtherRegressions(self, revision_data_sorted, bad_greater_than_good): | 3505 def _FindOtherRegressions(self, revision_data_sorted, bad_greater_than_good): |
| 3419 other_regressions = [] | 3506 other_regressions = [] |
| 3420 previous_values = [] | 3507 previous_values = [] |
| 3421 previous_id = None | 3508 previous_id = None |
| 3422 for current_id, current_data in revision_data_sorted: | 3509 for current_id, current_data in revision_data_sorted: |
| 3423 current_values = current_data['value'] | 3510 current_values = current_data['value'] |
| 3424 if current_values: | 3511 if current_values: |
| 3425 current_values = current_values['values'] | 3512 current_values = current_values['values'] |
| 3426 if previous_values: | 3513 if previous_values: |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3607 print ' %20s %40s %s' % (current_data['depot'], | 3694 print ' %20s %40s %s' % (current_data['depot'], |
| 3608 current_id, build_status) | 3695 current_id, build_status) |
| 3609 print | 3696 print |
| 3610 | 3697 |
| 3611 if self.opts.output_buildbot_annotations: | 3698 if self.opts.output_buildbot_annotations: |
| 3612 bisect_utils.OutputAnnotationStepClosed() | 3699 bisect_utils.OutputAnnotationStepClosed() |
| 3613 # The perf dashboard scrapes the "results" step in order to comment on | 3700 # The perf dashboard scrapes the "results" step in order to comment on |
| 3614 # bugs. If you change this, please update the perf dashboard as well. | 3701 # bugs. If you change this, please update the perf dashboard as well. |
| 3615 bisect_utils.OutputAnnotationStepStart('Results') | 3702 bisect_utils.OutputAnnotationStepStart('Results') |
| 3616 | 3703 |
| 3704 self._PrintBanner(results_dict) | |
| 3705 self._PrintWarnings() | |
| 3706 | |
| 3617 if results_dict['culprit_revisions'] and results_dict['confidence']: | 3707 if results_dict['culprit_revisions'] and results_dict['confidence']: |
| 3618 self._PrintBanner(results_dict) | |
| 3619 for culprit in results_dict['culprit_revisions']: | 3708 for culprit in results_dict['culprit_revisions']: |
| 3620 cl, info, depot = culprit | 3709 cl, info, depot = culprit |
| 3621 self._PrintRevisionInfo(cl, info, depot) | 3710 self._PrintRevisionInfo(cl, info, depot) |
| 3622 self._PrintReproSteps() | |
| 3623 if results_dict['other_regressions']: | 3711 if results_dict['other_regressions']: |
| 3624 self._PrintOtherRegressions(results_dict['other_regressions'], | 3712 self._PrintOtherRegressions(results_dict['other_regressions'], |
| 3625 revision_data) | 3713 revision_data) |
| 3626 else: | |
| 3627 self._PrintFailedBanner(results_dict) | |
| 3628 self._PrintReproSteps() | |
| 3629 | |
| 3630 self._PrintTestedCommitsTable(revision_data_sorted, | 3714 self._PrintTestedCommitsTable(revision_data_sorted, |
| 3631 results_dict['first_working_revision'], | 3715 results_dict['first_working_revision'], |
| 3632 results_dict['last_broken_revision'], | 3716 results_dict['last_broken_revision'], |
| 3633 results_dict['confidence']) | 3717 results_dict['confidence']) |
| 3634 self._PrintStepTime(revision_data_sorted) | 3718 self._PrintStepTime(revision_data_sorted) |
| 3635 self._PrintWarnings() | 3719 self._PrintReproSteps() |
| 3636 | 3720 self._PrintThankYou() |
| 3637 if self.opts.output_buildbot_annotations: | 3721 if self.opts.output_buildbot_annotations: |
| 3638 bisect_utils.OutputAnnotationStepClosed() | 3722 bisect_utils.OutputAnnotationStepClosed() |
| 3639 | 3723 |
| 3640 | 3724 |
| 3641 def DetermineAndCreateSourceControl(opts): | 3725 def DetermineAndCreateSourceControl(opts): |
| 3642 """Attempts to determine the underlying source control workflow and returns | 3726 """Attempts to determine the underlying source control workflow and returns |
| 3643 a SourceControl object. | 3727 a SourceControl object. |
| 3644 | 3728 |
| 3645 Returns: | 3729 Returns: |
| 3646 An instance of a SourceControl object, or None if the current workflow | 3730 An instance of a SourceControl object, or None if the current workflow |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4036 # The perf dashboard scrapes the "results" step in order to comment on | 4120 # The perf dashboard scrapes the "results" step in order to comment on |
| 4037 # bugs. If you change this, please update the perf dashboard as well. | 4121 # bugs. If you change this, please update the perf dashboard as well. |
| 4038 bisect_utils.OutputAnnotationStepStart('Results') | 4122 bisect_utils.OutputAnnotationStepStart('Results') |
| 4039 print 'Error: %s' % e.message | 4123 print 'Error: %s' % e.message |
| 4040 if opts.output_buildbot_annotations: | 4124 if opts.output_buildbot_annotations: |
| 4041 bisect_utils.OutputAnnotationStepClosed() | 4125 bisect_utils.OutputAnnotationStepClosed() |
| 4042 return 1 | 4126 return 1 |
| 4043 | 4127 |
| 4044 if __name__ == '__main__': | 4128 if __name__ == '__main__': |
| 4045 sys.exit(main()) | 4129 sys.exit(main()) |
| OLD | NEW |