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

Side by Side Diff: tools/bisect-perf-regression.py

Issue 255943002: [bisect] - First pass bisect functional breakages. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 7 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
« no previous file with comments | « no previous file | tools/run-bisect-perf-regression.py » ('j') | 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) 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 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 # once build is produced, it reads SHA value from this file and appends it 170 # once build is produced, it reads SHA value from this file and appends it
171 # to build archive filename. 171 # to build archive filename.
172 DEPS_SHA_PATCH = """diff --git src/DEPS.sha src/DEPS.sha 172 DEPS_SHA_PATCH = """diff --git src/DEPS.sha src/DEPS.sha
173 new file mode 100644 173 new file mode 100644
174 --- /dev/null 174 --- /dev/null
175 +++ src/DEPS.sha 175 +++ src/DEPS.sha
176 @@ -0,0 +1 @@ 176 @@ -0,0 +1 @@
177 +%(deps_sha)s 177 +%(deps_sha)s
178 """ 178 """
179 179
180 BISECT_MODE_MEAN = 'mean'
181 BISECT_MODE_STD_DEV = 'std_dev'
182 BISECT_MODE_RETURN_CODE = 'return_code'
183
184
180 def _AddAdditionalDepotInfo(depot_info): 185 def _AddAdditionalDepotInfo(depot_info):
181 """Adds additional depot info to the global depot variables.""" 186 """Adds additional depot info to the global depot variables."""
182 global DEPOT_DEPS_NAME 187 global DEPOT_DEPS_NAME
183 global DEPOT_NAMES 188 global DEPOT_NAMES
184 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() + 189 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() +
185 depot_info.items()) 190 depot_info.items())
186 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() 191 DEPOT_NAMES = DEPOT_DEPS_NAME.keys()
187 192
188 193
189 def CalculateTruncatedMean(data_set, truncate_percent): 194 def CalculateTruncatedMean(data_set, truncate_percent):
(...skipping 1771 matching lines...) Expand 10 before | Expand all | Expand 10 after
1961 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') 1966 path_to_generate = os.path.join('tools', 'perf', 'generate_profile')
1962 1967
1963 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): 1968 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'):
1964 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) 1969 profile_path, profile_type = os.path.split(arg_dict['--profile-dir'])
1965 return not RunProcess(['python', path_to_generate, 1970 return not RunProcess(['python', path_to_generate,
1966 '--profile-type-to-generate', profile_type, 1971 '--profile-type-to-generate', profile_type,
1967 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) 1972 '--browser', arg_dict['--browser'], '--output-dir', profile_path])
1968 return False 1973 return False
1969 return True 1974 return True
1970 1975
1976 def _IsBisectModeUsingMetric(self):
1977 return self.opts.bisect_mode in [BISECT_MODE_MEAN, BISECT_MODE_STD_DEV]
1978
1979 def _IsBisectModeReturnCode(self):
1980 return self.opts.bisect_mode in [BISECT_MODE_RETURN_CODE]
1981
1982 def _IsBisectModeStandardDeviation(self):
1983 return self.opts.bisect_mode in [BISECT_MODE_STD_DEV]
1984
1971 def RunPerformanceTestAndParseResults( 1985 def RunPerformanceTestAndParseResults(
1972 self, command_to_run, metric, reset_on_first_run=False, 1986 self, command_to_run, metric, reset_on_first_run=False,
1973 upload_on_last_run=False, results_label=None): 1987 upload_on_last_run=False, results_label=None):
1974 """Runs a performance test on the current revision and parses the results. 1988 """Runs a performance test on the current revision and parses the results.
1975 1989
1976 Args: 1990 Args:
1977 command_to_run: The command to be run to execute the performance test. 1991 command_to_run: The command to be run to execute the performance test.
1978 metric: The metric to parse out from the results of the performance test. 1992 metric: The metric to parse out from the results of the performance test.
1979 This is the result chart name and trace name, separated by slash. 1993 This is the result chart name and trace name, separated by slash.
1980 reset_on_first_run: If True, pass the flag --reset-results on first run. 1994 reset_on_first_run: If True, pass the flag --reset-results on first run.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
2026 current_args = copy.copy(args) 2040 current_args = copy.copy(args)
2027 if is_telemetry: 2041 if is_telemetry:
2028 if i == 0 and reset_on_first_run: 2042 if i == 0 and reset_on_first_run:
2029 current_args.append('--reset-results') 2043 current_args.append('--reset-results')
2030 elif i == self.opts.repeat_test_count - 1 and upload_on_last_run: 2044 elif i == self.opts.repeat_test_count - 1 and upload_on_last_run:
2031 current_args.append('--upload-results') 2045 current_args.append('--upload-results')
2032 if results_label: 2046 if results_label:
2033 current_args.append('--results-label=%s' % results_label) 2047 current_args.append('--results-label=%s' % results_label)
2034 (output, return_code) = RunProcessAndRetrieveOutput(current_args, 2048 (output, return_code) = RunProcessAndRetrieveOutput(current_args,
2035 cwd=self.src_cwd) 2049 cwd=self.src_cwd)
2050 output_of_all_runs += output
tonyg 2014/04/25 20:38:42 I'm not following why this block moved into the tr
shatch 2014/04/25 22:46:03 Oops, leftover from earlier version. Also moved th
2051 if self.opts.output_buildbot_annotations:
2052 print output
2053
2054 if self._IsBisectModeUsingMetric():
2055 metric_values += self.ParseMetricValuesFromOutput(metric, output)
2056 # If we're bisecting on a metric (ie, changes in the mean or
2057 # standard deviation) and no metric values are produced, bail out.
2058 if not metric_values:
2059 break
2060 elif self._IsBisectModeReturnCode():
2061 metric_values.append(return_code)
2062
2063 elapsed_minutes = (time.time() - start_time) / 60.0
2064 if elapsed_minutes >= self.opts.max_time_minutes:
2065 break
2036 except OSError, e: 2066 except OSError, e:
2037 if e.errno == errno.ENOENT: 2067 if e.errno == errno.ENOENT:
2038 err_text = ('Something went wrong running the performance test. ' 2068 err_text = ('Something went wrong running the performance test. '
2039 'Please review the command line:\n\n') 2069 'Please review the command line:\n\n')
2040 if 'src/' in ' '.join(args): 2070 if 'src/' in ' '.join(args):
2041 err_text += ('Check that you haven\'t accidentally specified a ' 2071 err_text += ('Check that you haven\'t accidentally specified a '
2042 'path with src/ in the command.\n\n') 2072 'path with src/ in the command.\n\n')
2043 err_text += ' '.join(args) 2073 err_text += ' '.join(args)
2044 err_text += '\n' 2074 err_text += '\n'
2045 2075
2046 return (err_text, failure_code) 2076 return (err_text, failure_code)
2047 raise 2077 raise
2048 2078
2049 output_of_all_runs += output
2050 if self.opts.output_buildbot_annotations:
2051 print output
2052
2053 metric_values += self.ParseMetricValuesFromOutput(metric, output)
2054
2055 elapsed_minutes = (time.time() - start_time) / 60.0
2056
2057 if elapsed_minutes >= self.opts.max_time_minutes or not metric_values:
2058 break
2059
2060 if len(metric_values) == 0: 2079 if len(metric_values) == 0:
2061 err_text = 'Metric %s was not found in the test output.' % metric 2080 err_text = 'Metric %s was not found in the test output.' % metric
2062 # TODO(qyearsley): Consider also getting and displaying a list of metrics 2081 # TODO(qyearsley): Consider also getting and displaying a list of metrics
2063 # that were found in the output here. 2082 # that were found in the output here.
2064 return (err_text, failure_code, output_of_all_runs) 2083 return (err_text, failure_code, output_of_all_runs)
2065 2084
2066 # Need to get the average value if there were multiple values. 2085 # If we're bisecting on return codes, we're really just looking for zero vs
2067 truncated_mean = CalculateTruncatedMean(metric_values, 2086 # non-zero.
2068 self.opts.truncate_percent) 2087 if self._IsBisectModeReturnCode():
2069 standard_err = CalculateStandardError(metric_values) 2088 # If any of the return codes is non-zero, output 1.
2070 standard_dev = CalculateStandardDeviation(metric_values) 2089 overall_return_code = 0 if (
2090 all(current_value == 0 for current_value in metric_values)) else 1
2071 2091
2072 values = { 2092 values = {
2073 'mean': truncated_mean, 2093 'mean': overall_return_code,
2074 'std_err': standard_err, 2094 'std_err': 0.0,
2075 'std_dev': standard_dev, 2095 'std_dev': 0.0,
2076 'values': metric_values, 2096 'values': metric_values,
2077 } 2097 }
2078 2098
2079 print 'Results of performance test: %12f %12f' % ( 2099 print 'Results of performance test: Command returned with %d' % (
2080 truncated_mean, standard_err) 2100 overall_return_code)
2081 print 2101 print
2102 else:
2103 # Need to get the average value if there were multiple values.
2104 truncated_mean = CalculateTruncatedMean(metric_values,
2105 self.opts.truncate_percent)
2106 standard_err = CalculateStandardError(metric_values)
2107 standard_dev = CalculateStandardDeviation(metric_values)
2108
2109 if self._IsBisectModeStandardDeviation():
2110 metric_values = [standard_dev]
2111
2112 values = {
2113 'mean': truncated_mean,
2114 'std_err': standard_err,
2115 'std_dev': standard_dev,
2116 'values': metric_values,
2117 }
2118
2119 print 'Results of performance test: %12f %12f' % (
2120 truncated_mean, standard_err)
2121 print
2082 return (values, success_code, output_of_all_runs) 2122 return (values, success_code, output_of_all_runs)
2083 2123
2084 def FindAllRevisionsToSync(self, revision, depot): 2124 def FindAllRevisionsToSync(self, revision, depot):
2085 """Finds all dependant revisions and depots that need to be synced for a 2125 """Finds all dependant revisions and depots that need to be synced for a
2086 given revision. This is only useful in the git workflow, as an svn depot 2126 given revision. This is only useful in the git workflow, as an svn depot
2087 may be split into multiple mirrors. 2127 may be split into multiple mirrors.
2088 2128
2089 ie. skia is broken up into 3 git mirrors over skia/src, skia/gyp, and 2129 ie. skia is broken up into 3 git mirrors over skia/src, skia/gyp, and
2090 skia/include. To sync skia/src properly, one has to find the proper 2130 skia/include. To sync skia/src properly, one has to find the proper
2091 revisions in skia/gyp and skia/include. 2131 revisions in skia/gyp and skia/include.
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
2332 return results 2372 return results
2333 else: 2373 else:
2334 return ('Failed to build revision: [%s]' % (str(revision, )), 2374 return ('Failed to build revision: [%s]' % (str(revision, )),
2335 BUILD_RESULT_FAIL) 2375 BUILD_RESULT_FAIL)
2336 else: 2376 else:
2337 return ('Failed to run [gclient runhooks].', BUILD_RESULT_FAIL) 2377 return ('Failed to run [gclient runhooks].', BUILD_RESULT_FAIL)
2338 else: 2378 else:
2339 return ('Failed to sync revision: [%s]' % (str(revision, )), 2379 return ('Failed to sync revision: [%s]' % (str(revision, )),
2340 BUILD_RESULT_FAIL) 2380 BUILD_RESULT_FAIL)
2341 2381
2342 def CheckIfRunPassed(self, current_value, known_good_value, known_bad_value): 2382 def _CheckIfRunPassed(self, current_value, known_good_value, known_bad_value):
2343 """Given known good and bad values, decide if the current_value passed 2383 """Given known good and bad values, decide if the current_value passed
2344 or failed. 2384 or failed.
2345 2385
2346 Args: 2386 Args:
2347 current_value: The value of the metric being checked. 2387 current_value: The value of the metric being checked.
2348 known_bad_value: The reference value for a "failed" run. 2388 known_bad_value: The reference value for a "failed" run.
2349 known_good_value: The reference value for a "passed" run. 2389 known_good_value: The reference value for a "passed" run.
2350 2390
2351 Returns: 2391 Returns:
2352 True if the current_value is closer to the known_good_value than the 2392 True if the current_value is closer to the known_good_value than the
2353 known_bad_value. 2393 known_bad_value.
2354 """ 2394 """
2355 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) 2395 if self.opts.bisect_mode == BISECT_MODE_STD_DEV:
2356 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) 2396 dist_to_good_value = abs(current_value['std_dev'] -
2397 known_good_value['std_dev'])
2398 dist_to_bad_value = abs(current_value['std_dev'] -
2399 known_bad_value['std_dev'])
2400 else:
2401 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean'])
2402 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean'])
2357 2403
2358 return dist_to_good_value < dist_to_bad_value 2404 return dist_to_good_value < dist_to_bad_value
2359 2405
2360 def _GetDepotDirectory(self, depot_name): 2406 def _GetDepotDirectory(self, depot_name):
2361 if depot_name == 'chromium': 2407 if depot_name == 'chromium':
2362 return self.src_cwd 2408 return self.src_cwd
2363 elif depot_name == 'cros': 2409 elif depot_name == 'cros':
2364 return self.cros_cwd 2410 return self.cros_cwd
2365 elif depot_name in DEPOT_NAMES: 2411 elif depot_name in DEPOT_NAMES:
2366 return self.depot_cwd[depot_name] 2412 return self.depot_cwd[depot_name]
(...skipping 535 matching lines...) Expand 10 before | Expand all | Expand 10 after
2902 metric, skippable=True) 2948 metric, skippable=True)
2903 2949
2904 # If the build is successful, check whether or not the metric 2950 # If the build is successful, check whether or not the metric
2905 # had regressed. 2951 # had regressed.
2906 if not run_results[1]: 2952 if not run_results[1]:
2907 if len(run_results) > 2: 2953 if len(run_results) > 2:
2908 next_revision_data['external'] = run_results[2] 2954 next_revision_data['external'] = run_results[2]
2909 next_revision_data['perf_time'] = run_results[3] 2955 next_revision_data['perf_time'] = run_results[3]
2910 next_revision_data['build_time'] = run_results[4] 2956 next_revision_data['build_time'] = run_results[4]
2911 2957
2912 passed_regression = self.CheckIfRunPassed(run_results[0], 2958 passed_regression = self._CheckIfRunPassed(run_results[0],
2913 known_good_value, 2959 known_good_value,
2914 known_bad_value) 2960 known_bad_value)
2915 2961
2916 next_revision_data['passed'] = passed_regression 2962 next_revision_data['passed'] = passed_regression
2917 next_revision_data['value'] = run_results[0] 2963 next_revision_data['value'] = run_results[0]
2918 2964
2919 if passed_regression: 2965 if passed_regression:
2920 max_revision = next_revision_index 2966 max_revision = next_revision_index
2921 else: 2967 else:
2922 min_revision = next_revision_index 2968 min_revision = next_revision_index
2923 else: 2969 else:
2924 if run_results[1] == BUILD_RESULT_SKIPPED: 2970 if run_results[1] == BUILD_RESULT_SKIPPED:
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
2959 # "Confidence in Bisection Results: 100%" to decide whether or not 3005 # "Confidence in Bisection Results: 100%" to decide whether or not
2960 # to cc the author(s). If you change this, please update the perf 3006 # to cc the author(s). If you change this, please update the perf
2961 # dashboard as well. 3007 # dashboard as well.
2962 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence'] 3008 print 'Confidence in Bisection Results: %d%%' % results_dict['confidence']
2963 3009
2964 def _PrintBanner(self, results_dict): 3010 def _PrintBanner(self, results_dict):
2965 print 3011 print
2966 print " __o_\___ Aw Snap! We hit a speed bump!" 3012 print " __o_\___ Aw Snap! We hit a speed bump!"
2967 print "=-O----O-'__.~.___________________________________" 3013 print "=-O----O-'__.~.___________________________________"
2968 print 3014 print
2969 print 'Bisect reproduced a %.02f%% (+-%.02f%%) change in the %s metric.' % ( 3015 if self._IsBisectModeReturnCode():
2970 results_dict['regression_size'], results_dict['regression_std_err'], 3016 print ('Bisect reproduced a change in return codes while running the '
2971 '/'.join(self.opts.metric)) 3017 'performance test.')
3018 else:
3019 print ('Bisect reproduced a %.02f%% (+-%.02f%%) change in the '
3020 '%s metric.' % (results_dict['regression_size'],
3021 results_dict['regression_std_err'], '/'.join(self.opts.metric)))
2972 self._PrintConfidence(results_dict) 3022 self._PrintConfidence(results_dict)
2973 3023
2974 def _PrintFailedBanner(self, results_dict): 3024 def _PrintFailedBanner(self, results_dict):
2975 print 3025 print
2976 print ('Bisect could not reproduce a change in the ' 3026 if self._IsBisectModeReturnCode():
2977 '%s/%s metric.' % (self.opts.metric[0], self.opts.metric[1])) 3027 print 'Bisect could not reproduce a change in the return code.'
3028 else:
3029 print ('Bisect could not reproduce a change in the '
3030 '%s metric.' % '/'.join(self.opts.metric))
2978 print 3031 print
2979 self._PrintConfidence(results_dict) 3032 self._PrintConfidence(results_dict)
tonyg 2014/04/25 20:38:42 Looking at the output in the CL description, it se
shatch 2014/04/25 22:46:03 Done.
2980 3033
2981 def _GetViewVCLinkFromDepotAndHash(self, cl, depot): 3034 def _GetViewVCLinkFromDepotAndHash(self, cl, depot):
2982 info = self.source_control.QueryRevisionInfo(cl, 3035 info = self.source_control.QueryRevisionInfo(cl,
2983 self._GetDepotDirectory(depot)) 3036 self._GetDepotDirectory(depot))
2984 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'): 3037 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'):
2985 try: 3038 try:
2986 # Format is "git-svn-id: svn://....@123456 <other data>" 3039 # Format is "git-svn-id: svn://....@123456 <other data>"
2987 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i] 3040 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i]
2988 svn_revision = svn_line[0].split('@') 3041 svn_revision = svn_line[0].split('@')
2989 svn_revision = svn_revision[1].split(' ')[0] 3042 svn_revision = svn_revision[1].split(' ')[0]
(...skipping 16 matching lines...) Expand all
3006 print 'Link : %s' % commit_link 3059 print 'Link : %s' % commit_link
3007 else: 3060 else:
3008 print 3061 print
3009 print 'Failed to parse svn revision from body:' 3062 print 'Failed to parse svn revision from body:'
3010 print 3063 print
3011 print info['body'] 3064 print info['body']
3012 print 3065 print
3013 print 'Commit : %s' % cl 3066 print 'Commit : %s' % cl
3014 print 'Date : %s' % info['date'] 3067 print 'Date : %s' % info['date']
3015 3068
3069 def _PrintTestedCommitsHeader(self):
tonyg 2014/04/25 20:38:42 These are now fairly hard to read and a little red
shatch 2014/04/25 22:46:03 Yeah, looks a lot more readable this way.
3070 if self.opts.bisect_mode == BISECT_MODE_MEAN:
3071 print ' %20s %70s %12s %14s %13s' % ('Depot'.center(20, ' '),
3072 'Commit SHA'.center(70, ' '), 'Mean'.center(12, ' '),
3073 'Std. Error'.center(14, ' '), 'State'.center(13, ' '))
3074 elif self.opts.bisect_mode == BISECT_MODE_STD_DEV:
3075 print ' %20s %70s %14s %12s %13s' % ('Depot'.center(20, ' '),
3076 'Commit SHA'.center(70, ' '), 'Std. Dev'.center(14, ' '),
3077 'Mean'.center(12, ' '), 'State'.center(13, ' '))
3078 elif self.opts.bisect_mode == BISECT_MODE_RETURN_CODE:
3079 print ' %20s %70s %14s %13s' % ('Depot'.center(20, ' '),
3080 'Commit SHA'.center(70, ' '), 'Return Code'.center(14, ' '),
3081 'State'.center(13, ' '))
3082
3083 def _PrintTestedCommitsEntry(self, current_data, cl_link, state_str):
3084 if self.opts.bisect_mode == BISECT_MODE_MEAN:
3085 std_error = ('+-%.02f' %
3086 current_data['value']['std_err']).center(14, ' ')
3087 mean = ('%.02f' % current_data['value']['mean']).center(12, ' ')
3088 print ' %20s %70s %12s %14s %13s' % (
3089 current_data['depot'].center(20, ' '), cl_link.center(70, ' '),
3090 mean, std_error, state_str)
3091 elif self.opts.bisect_mode == BISECT_MODE_STD_DEV:
3092 std_dev = ('+-%.02f' %
3093 current_data['value']['std_dev']).center(14, ' ')
3094 mean = ('%.02f' % current_data['value']['mean']).center(12, ' ')
3095 print ' %20s %70s %14s %12s %13s' % (
3096 current_data['depot'].center(20, ' '), cl_link.center(70, ' '),
3097 std_dev, mean, state_str)
3098 elif self.opts.bisect_mode == BISECT_MODE_RETURN_CODE:
3099 mean = ('%d' % current_data['value']['mean']).center(12, ' ')
3100 print ' %20s %70s %14s %13s' % (
3101 current_data['depot'].center(20, ' '), cl_link.center(70, ' '),
3102 mean, state_str)
3103
3016 def _PrintTestedCommitsTable(self, revision_data_sorted, 3104 def _PrintTestedCommitsTable(self, revision_data_sorted,
3017 first_working_revision, last_broken_revision, confidence, 3105 first_working_revision, last_broken_revision, confidence,
3018 final_step=True): 3106 final_step=True):
3019 print 3107 print
3020 if final_step: 3108 if final_step:
3021 print 'Tested commits:' 3109 print 'Tested commits:'
3022 else: 3110 else:
3023 print 'Partial results:' 3111 print 'Partial results:'
3024 print ' %20s %70s %12s %14s %13s' % ('Depot'.center(20, ' '), 3112 self._PrintTestedCommitsHeader()
3025 'Commit SHA'.center(70, ' '), 'Mean'.center(12, ' '),
3026 'Std. Error'.center(14, ' '), 'State'.center(13, ' '))
3027 state = 0 3113 state = 0
3028 for current_id, current_data in revision_data_sorted: 3114 for current_id, current_data in revision_data_sorted:
3029 if current_data['value']: 3115 if current_data['value']:
3030 if (current_id == last_broken_revision or 3116 if (current_id == last_broken_revision or
3031 current_id == first_working_revision): 3117 current_id == first_working_revision):
3032 # If confidence is too low, don't add this empty line since it's 3118 # If confidence is too low, don't add this empty line since it's
3033 # used to put focus on a suspected CL. 3119 # used to put focus on a suspected CL.
3034 if confidence and final_step: 3120 if confidence and final_step:
3035 print 3121 print
3036 state += 1 3122 state += 1
3037 if state == 2 and not final_step: 3123 if state == 2 and not final_step:
3038 # Just want a separation between "bad" and "good" cl's. 3124 # Just want a separation between "bad" and "good" cl's.
3039 print 3125 print
3040 3126
3041 state_str = 'Bad' 3127 state_str = 'Bad'
3042 if state == 1 and final_step: 3128 if state == 1 and final_step:
3043 state_str = 'Suspected CL' 3129 state_str = 'Suspected CL'
3044 elif state == 2: 3130 elif state == 2:
3045 state_str = 'Good' 3131 state_str = 'Good'
3046 3132
3047 # If confidence is too low, don't bother outputting good/bad. 3133 # If confidence is too low, don't bother outputting good/bad.
3048 if not confidence: 3134 if not confidence:
3049 state_str = '' 3135 state_str = ''
3050 state_str = state_str.center(13, ' ') 3136 state_str = state_str.center(13, ' ')
3051 3137
3052 std_error = ('+-%.02f' %
3053 current_data['value']['std_err']).center(14, ' ')
3054 mean = ('%.02f' % current_data['value']['mean']).center(12, ' ')
3055 cl_link = self._GetViewVCLinkFromDepotAndHash(current_id, 3138 cl_link = self._GetViewVCLinkFromDepotAndHash(current_id,
3056 current_data['depot']) 3139 current_data['depot'])
3057 if not cl_link: 3140 if not cl_link:
3058 cl_link = current_id 3141 cl_link = current_id
3059 print ' %20s %70s %12s %14s %13s' % ( 3142 self._PrintTestedCommitsEntry(current_data, cl_link, state_str)
3060 current_data['depot'].center(20, ' '), cl_link.center(70, ' '),
3061 mean, std_error, state_str)
3062 3143
3063 def _PrintReproSteps(self): 3144 def _PrintReproSteps(self):
3064 print 3145 print
3065 print 'To reproduce locally:' 3146 print 'To reproduce locally:'
3066 print '$ ' + self.opts.command 3147 print '$ ' + self.opts.command
3067 if bisect_utils.IsTelemetryCommand(self.opts.command): 3148 if bisect_utils.IsTelemetryCommand(self.opts.command):
3068 print 3149 print
3069 print 'Also consider passing --profiler=list to see available profilers.' 3150 print 'Also consider passing --profiler=list to see available profilers.'
3070 3151
3071 def _PrintOtherRegressions(self, other_regressions, revision_data): 3152 def _PrintOtherRegressions(self, other_regressions, revision_data):
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after
3426 self.no_custom_deps = False 3507 self.no_custom_deps = False
3427 self.working_directory = None 3508 self.working_directory = None
3428 self.extra_src = None 3509 self.extra_src = None
3429 self.debug_ignore_build = None 3510 self.debug_ignore_build = None
3430 self.debug_ignore_sync = None 3511 self.debug_ignore_sync = None
3431 self.debug_ignore_perf_test = None 3512 self.debug_ignore_perf_test = None
3432 self.gs_bucket = None 3513 self.gs_bucket = None
3433 self.target_arch = 'ia32' 3514 self.target_arch = 'ia32'
3434 self.builder_host = None 3515 self.builder_host = None
3435 self.builder_port = None 3516 self.builder_port = None
3517 self.bisect_mode = BISECT_MODE_MEAN
3436 3518
3437 def _CreateCommandLineParser(self): 3519 def _CreateCommandLineParser(self):
3438 """Creates a parser with bisect options. 3520 """Creates a parser with bisect options.
3439 3521
3440 Returns: 3522 Returns:
3441 An instance of optparse.OptionParser. 3523 An instance of optparse.OptionParser.
3442 """ 3524 """
3443 usage = ('%prog [options] [-- chromium-options]\n' 3525 usage = ('%prog [options] [-- chromium-options]\n'
3444 'Perform binary search on revision history to find a minimal ' 3526 'Perform binary search on revision history to find a minimal '
3445 'range of revisions where a peformance metric regressed.\n') 3527 'range of revisions where a peformance metric regressed.\n')
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
3480 'doesn\'t exceed --max_time_minutes. Values will be ' 3562 'doesn\'t exceed --max_time_minutes. Values will be '
3481 'clamped to range [1, 60].' 3563 'clamped to range [1, 60].'
3482 'Default value is 20.') 3564 'Default value is 20.')
3483 group.add_option('-t', '--truncate_percent', 3565 group.add_option('-t', '--truncate_percent',
3484 type='int', 3566 type='int',
3485 default=25, 3567 default=25,
3486 help='The highest/lowest % are discarded to form a ' 3568 help='The highest/lowest % are discarded to form a '
3487 'truncated mean. Values will be clamped to range [0, ' 3569 'truncated mean. Values will be clamped to range [0, '
3488 '25]. Default value is 25 (highest/lowest 25% will be ' 3570 '25]. Default value is 25 (highest/lowest 25% will be '
3489 'discarded).') 3571 'discarded).')
3572 group.add_option('--bisect_mode',
3573 type='choice',
3574 choices=[BISECT_MODE_MEAN, BISECT_MODE_STD_DEV,
3575 BISECT_MODE_RETURN_CODE],
3576 default=BISECT_MODE_MEAN,
3577 help='The bisect mode. Choices are to bisect on the '
3578 'difference in mean, std_dev, or return_code.')
3490 parser.add_option_group(group) 3579 parser.add_option_group(group)
3491 3580
3492 group = optparse.OptionGroup(parser, 'Build options') 3581 group = optparse.OptionGroup(parser, 'Build options')
3493 group.add_option('-w', '--working_directory', 3582 group.add_option('-w', '--working_directory',
3494 type='str', 3583 type='str',
3495 help='Path to the working directory where the script ' 3584 help='Path to the working directory where the script '
3496 'will do an initial checkout of the chromium depot. The ' 3585 'will do an initial checkout of the chromium depot. The '
3497 'files will be placed in a subdirectory "bisect" under ' 3586 'files will be placed in a subdirectory "bisect" under '
3498 'working_directory and that will be used to perform the ' 3587 'working_directory and that will be used to perform the '
3499 'bisection. This parameter is optional, if it is not ' 3588 'bisection. This parameter is optional, if it is not '
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
3579 try: 3668 try:
3580 if not opts.command: 3669 if not opts.command:
3581 raise RuntimeError('missing required parameter: --command') 3670 raise RuntimeError('missing required parameter: --command')
3582 3671
3583 if not opts.good_revision: 3672 if not opts.good_revision:
3584 raise RuntimeError('missing required parameter: --good_revision') 3673 raise RuntimeError('missing required parameter: --good_revision')
3585 3674
3586 if not opts.bad_revision: 3675 if not opts.bad_revision:
3587 raise RuntimeError('missing required parameter: --bad_revision') 3676 raise RuntimeError('missing required parameter: --bad_revision')
3588 3677
3589 if not opts.metric: 3678 if not opts.metric and opts.bisect_mode != BISECT_MODE_RETURN_CODE:
3590 raise RuntimeError('missing required parameter: --metric') 3679 raise RuntimeError('missing required parameter: --metric')
3591 3680
3592 if opts.gs_bucket: 3681 if opts.gs_bucket:
3593 if not cloud_storage.List(opts.gs_bucket): 3682 if not cloud_storage.List(opts.gs_bucket):
3594 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket) 3683 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket)
3595 if not opts.builder_host: 3684 if not opts.builder_host:
3596 raise RuntimeError('Must specify try server hostname, when ' 3685 raise RuntimeError('Must specify try server hostname, when '
3597 'gs_bucket is used: --builder_host') 3686 'gs_bucket is used: --builder_host')
3598 if not opts.builder_port: 3687 if not opts.builder_port:
3599 raise RuntimeError('Must specify try server port number, when ' 3688 raise RuntimeError('Must specify try server port number, when '
3600 'gs_bucket is used: --builder_port') 3689 'gs_bucket is used: --builder_port')
3601 if opts.target_platform == 'cros': 3690 if opts.target_platform == 'cros':
3602 # Run sudo up front to make sure credentials are cached for later. 3691 # Run sudo up front to make sure credentials are cached for later.
3603 print 'Sudo is required to build cros:' 3692 print 'Sudo is required to build cros:'
3604 print 3693 print
3605 RunProcess(['sudo', 'true']) 3694 RunProcess(['sudo', 'true'])
3606 3695
3607 if not opts.cros_board: 3696 if not opts.cros_board:
3608 raise RuntimeError('missing required parameter: --cros_board') 3697 raise RuntimeError('missing required parameter: --cros_board')
3609 3698
3610 if not opts.cros_remote_ip: 3699 if not opts.cros_remote_ip:
3611 raise RuntimeError('missing required parameter: --cros_remote_ip') 3700 raise RuntimeError('missing required parameter: --cros_remote_ip')
3612 3701
3613 if not opts.working_directory: 3702 if not opts.working_directory:
3614 raise RuntimeError('missing required parameter: --working_directory') 3703 raise RuntimeError('missing required parameter: --working_directory')
3615 3704
3616 metric_values = opts.metric.split('/') 3705 metric_values = opts.metric.split('/')
3617 if len(metric_values) != 2: 3706 if (len(metric_values) != 2 and
3707 opts.bisect_mode != BISECT_MODE_RETURN_CODE):
3618 raise RuntimeError("Invalid metric specified: [%s]" % opts.metric) 3708 raise RuntimeError("Invalid metric specified: [%s]" % opts.metric)
3619 3709
3620 opts.metric = metric_values 3710 opts.metric = metric_values
3621 opts.repeat_test_count = min(max(opts.repeat_test_count, 1), 100) 3711 opts.repeat_test_count = min(max(opts.repeat_test_count, 1), 100)
3622 opts.max_time_minutes = min(max(opts.max_time_minutes, 1), 60) 3712 opts.max_time_minutes = min(max(opts.max_time_minutes, 1), 60)
3623 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25) 3713 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25)
3624 opts.truncate_percent = opts.truncate_percent / 100.0 3714 opts.truncate_percent = opts.truncate_percent / 100.0
3625 3715
3626 for k, v in opts.__dict__.iteritems(): 3716 for k, v in opts.__dict__.iteritems():
3627 assert hasattr(self, k), "Invalid %s attribute in BisectOptions." % k 3717 assert hasattr(self, k), "Invalid %s attribute in BisectOptions." % k
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
3719 # The perf dashboard scrapes the "results" step in order to comment on 3809 # The perf dashboard scrapes the "results" step in order to comment on
3720 # bugs. If you change this, please update the perf dashboard as well. 3810 # bugs. If you change this, please update the perf dashboard as well.
3721 bisect_utils.OutputAnnotationStepStart('Results') 3811 bisect_utils.OutputAnnotationStepStart('Results')
3722 print 'Error: %s' % e.message 3812 print 'Error: %s' % e.message
3723 if opts.output_buildbot_annotations: 3813 if opts.output_buildbot_annotations:
3724 bisect_utils.OutputAnnotationStepClosed() 3814 bisect_utils.OutputAnnotationStepClosed()
3725 return 1 3815 return 1
3726 3816
3727 if __name__ == '__main__': 3817 if __name__ == '__main__':
3728 sys.exit(main()) 3818 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/run-bisect-perf-regression.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698