| 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 2219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2230 next_revision_data['passed'] = 'Build Failed' | 2230 next_revision_data['passed'] = 'Build Failed' |
| 2231 | 2231 |
| 2232 print run_results[0] | 2232 print run_results[0] |
| 2233 | 2233 |
| 2234 # If the build is broken, remove it and redo search. | 2234 # If the build is broken, remove it and redo search. |
| 2235 revision_list.pop(next_revision_index) | 2235 revision_list.pop(next_revision_index) |
| 2236 | 2236 |
| 2237 max_revision -= 1 | 2237 max_revision -= 1 |
| 2238 | 2238 |
| 2239 if self.opts.output_buildbot_annotations: | 2239 if self.opts.output_buildbot_annotations: |
| 2240 self._PrintPartialResults(results) |
| 2240 bisect_utils.OutputAnnotationStepClosed() | 2241 bisect_utils.OutputAnnotationStepClosed() |
| 2241 else: | 2242 else: |
| 2242 # Weren't able to sync and retrieve the revision range. | 2243 # Weren't able to sync and retrieve the revision range. |
| 2243 results['error'] = 'An error occurred attempting to retrieve revision '\ | 2244 results['error'] = 'An error occurred attempting to retrieve revision '\ |
| 2244 'range: [%s..%s]' % (good_revision, bad_revision) | 2245 'range: [%s..%s]' % (good_revision, bad_revision) |
| 2245 | 2246 |
| 2246 return results | 2247 return results |
| 2247 | 2248 |
| 2249 def _PrintPartialResults(self, results_dict): |
| 2250 revision_data = results_dict['revision_data'] |
| 2251 revision_data_sorted = sorted(revision_data.iteritems(), |
| 2252 key = lambda x: x[1]['sort']) |
| 2253 results_dict = self._GetResultsDict(revision_data, revision_data_sorted) |
| 2254 first_working_revision = results_dict['first_working_revision'] |
| 2255 last_broken_revision = results_dict['last_broken_revision'] |
| 2256 |
| 2257 self._PrintTestedCommitsTable(revision_data_sorted, |
| 2258 results_dict['first_working_revision'], |
| 2259 results_dict['last_broken_revision'], |
| 2260 100, final_step=False) |
| 2261 |
| 2248 def _PrintConfidence(self, results_dict): | 2262 def _PrintConfidence(self, results_dict): |
| 2249 # The perf dashboard specifically looks for the string | 2263 # The perf dashboard specifically looks for the string |
| 2250 # "Confidence in Bisection Results: 100%" to decide whether or not | 2264 # "Confidence in Bisection Results: 100%" to decide whether or not |
| 2251 # to cc the author(s). If you change this, please update the perf | 2265 # to cc the author(s). If you change this, please update the perf |
| 2252 # dashboard as well. | 2266 # dashboard as well. |
| 2253 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence'] | 2267 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence'] |
| 2254 | 2268 |
| 2255 def _PrintBanner(self, results_dict): | 2269 def _PrintBanner(self, results_dict): |
| 2256 print | 2270 print |
| 2257 print " __o_\___ Aw Snap! We hit a speed bump!" | 2271 print " __o_\___ Aw Snap! We hit a speed bump!" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 2288 except IndexError: | 2302 except IndexError: |
| 2289 print | 2303 print |
| 2290 print 'Failed to parse svn revision from body:' | 2304 print 'Failed to parse svn revision from body:' |
| 2291 print | 2305 print |
| 2292 print info['body'] | 2306 print info['body'] |
| 2293 print | 2307 print |
| 2294 print 'Commit : %s' % cl | 2308 print 'Commit : %s' % cl |
| 2295 print 'Date : %s' % info['date'] | 2309 print 'Date : %s' % info['date'] |
| 2296 | 2310 |
| 2297 def _PrintTestedCommitsTable(self, revision_data_sorted, | 2311 def _PrintTestedCommitsTable(self, revision_data_sorted, |
| 2298 first_working_revision, last_broken_revision, confidence): | 2312 first_working_revision, last_broken_revision, confidence, |
| 2313 final_step=True): |
| 2299 print | 2314 print |
| 2300 print 'Tested commits:' | 2315 if final_step: |
| 2316 print 'Tested commits:' |
| 2317 else: |
| 2318 print 'Partial results:' |
| 2301 print ' %20s %40s %12s %14s %13s' % ('Depot'.center(20, ' '), | 2319 print ' %20s %40s %12s %14s %13s' % ('Depot'.center(20, ' '), |
| 2302 'Commit SHA'.center(40, ' '), 'Mean'.center(12, ' '), | 2320 'Commit SHA'.center(40, ' '), 'Mean'.center(12, ' '), |
| 2303 'Std. Error'.center(14, ' '), 'State'.center(13, ' ')) | 2321 'Std. Error'.center(14, ' '), 'State'.center(13, ' ')) |
| 2304 state = 0 | 2322 state = 0 |
| 2305 for current_id, current_data in revision_data_sorted: | 2323 for current_id, current_data in revision_data_sorted: |
| 2306 if current_data['value']: | 2324 if current_data['value']: |
| 2307 if (current_id == last_broken_revision or | 2325 if (current_id == last_broken_revision or |
| 2308 current_id == first_working_revision): | 2326 current_id == first_working_revision): |
| 2309 # If confidence is too low, don't add this empty line since it's | 2327 # If confidence is too low, don't add this empty line since it's |
| 2310 # used to put focus on a suspected CL. | 2328 # used to put focus on a suspected CL. |
| 2311 if confidence: | 2329 if confidence and final_step: |
| 2312 print | 2330 print |
| 2313 state += 1 | 2331 state += 1 |
| 2332 if state == 2 and not final_step: |
| 2333 # Just want a separation between "bad" and "good" cl's. |
| 2334 print |
| 2314 | 2335 |
| 2315 state_str = 'Bad' | 2336 state_str = 'Bad' |
| 2316 if state == 1: | 2337 if state == 1 and final_step: |
| 2317 state_str = 'Suspected CL' | 2338 state_str = 'Suspected CL' |
| 2318 elif state == 2: | 2339 elif state == 2: |
| 2319 state_str = 'Good' | 2340 state_str = 'Good' |
| 2320 | 2341 |
| 2321 # If confidence is too low, don't bother outputting good/bad. | 2342 # If confidence is too low, don't bother outputting good/bad. |
| 2322 if not confidence: | 2343 if not confidence: |
| 2323 state_str = '' | 2344 state_str = '' |
| 2324 state_str = state_str.center(13, ' ') | 2345 state_str = state_str.center(13, ' ') |
| 2325 | 2346 |
| 2326 std_error = ('+-%.02f' % | 2347 std_error = ('+-%.02f' % |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2374 print 'Average build time : %s' % datetime.timedelta( | 2395 print 'Average build time : %s' % datetime.timedelta( |
| 2375 seconds=int(step_build_time_avg)) | 2396 seconds=int(step_build_time_avg)) |
| 2376 print 'Average test time : %s' % datetime.timedelta( | 2397 print 'Average test time : %s' % datetime.timedelta( |
| 2377 seconds=int(step_perf_time_avg)) | 2398 seconds=int(step_perf_time_avg)) |
| 2378 | 2399 |
| 2379 def _PrintWarnings(self): | 2400 def _PrintWarnings(self): |
| 2380 if not self.warnings: | 2401 if not self.warnings: |
| 2381 return | 2402 return |
| 2382 print | 2403 print |
| 2383 print 'WARNINGS:' | 2404 print 'WARNINGS:' |
| 2384 for w in self.warnings: | 2405 for w in set(self.warnings): |
| 2385 print ' !!! %s' % w | 2406 print ' !!! %s' % w |
| 2386 | 2407 |
| 2387 def _GetResultsDict(self, revision_data, revision_data_sorted): | 2408 def _GetResultsDict(self, revision_data, revision_data_sorted): |
| 2388 # Find range where it possibly broke. | 2409 # Find range where it possibly broke. |
| 2389 first_working_revision = None | 2410 first_working_revision = None |
| 2390 first_working_revision_index = -1 | 2411 first_working_revision_index = -1 |
| 2391 last_broken_revision = None | 2412 last_broken_revision = None |
| 2392 last_broken_revision_index = -1 | 2413 last_broken_revision_index = -1 |
| 2393 | 2414 |
| 2394 for i in xrange(len(revision_data_sorted)): | 2415 for i in xrange(len(revision_data_sorted)): |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2966 # The perf dashboard scrapes the "results" step in order to comment on | 2987 # The perf dashboard scrapes the "results" step in order to comment on |
| 2967 # bugs. If you change this, please update the perf dashboard as well. | 2988 # bugs. If you change this, please update the perf dashboard as well. |
| 2968 bisect_utils.OutputAnnotationStepStart('Results') | 2989 bisect_utils.OutputAnnotationStepStart('Results') |
| 2969 print 'Error: %s' % e.message | 2990 print 'Error: %s' % e.message |
| 2970 if opts.output_buildbot_annotations: | 2991 if opts.output_buildbot_annotations: |
| 2971 bisect_utils.OutputAnnotationStepClosed() | 2992 bisect_utils.OutputAnnotationStepClosed() |
| 2972 return 1 | 2993 return 1 |
| 2973 | 2994 |
| 2974 if __name__ == '__main__': | 2995 if __name__ == '__main__': |
| 2975 sys.exit(main()) | 2996 sys.exit(main()) |
| OLD | NEW |