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 17 matching lines...) Expand all Loading... | |
| 28 An example usage (using git hashes): | 28 An example usage (using git hashes): |
| 29 | 29 |
| 30 ./tools/bisect-perf-regression.py -c\ | 30 ./tools/bisect-perf-regression.py -c\ |
| 31 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ | 31 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ |
| 32 -g 1f6e67861535121c5c819c16a666f2436c207e7b\ | 32 -g 1f6e67861535121c5c819c16a666f2436c207e7b\ |
| 33 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ | 33 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ |
| 34 -m shutdown/simple-user-quit | 34 -m shutdown/simple-user-quit |
| 35 | 35 |
| 36 """ | 36 """ |
| 37 | 37 |
| 38 import copy | |
| 38 import datetime | 39 import datetime |
| 39 import errno | 40 import errno |
| 40 import imp | 41 import imp |
| 41 import math | 42 import math |
| 42 import optparse | 43 import optparse |
| 43 import os | 44 import os |
| 44 import re | 45 import re |
| 45 import shlex | 46 import shlex |
| 46 import shutil | 47 import shutil |
| 47 import subprocess | 48 import subprocess |
| (...skipping 1179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1227 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') | 1228 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') |
| 1228 | 1229 |
| 1229 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): | 1230 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): |
| 1230 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) | 1231 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) |
| 1231 return not RunProcess(['python', path_to_generate, | 1232 return not RunProcess(['python', path_to_generate, |
| 1232 '--profile-type-to-generate', profile_type, | 1233 '--profile-type-to-generate', profile_type, |
| 1233 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) | 1234 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) |
| 1234 return False | 1235 return False |
| 1235 return True | 1236 return True |
| 1236 | 1237 |
| 1237 def RunPerformanceTestAndParseResults(self, command_to_run, metric): | 1238 def RunPerformanceTestAndParseResults(self, command_to_run, metric, |
| 1239 reset_on_first_run=False, upload_on_last_run=False): | |
| 1238 """Runs a performance test on the current revision by executing the | 1240 """Runs a performance test on the current revision by executing the |
| 1239 'command_to_run' and parses the results. | 1241 'command_to_run' and parses the results. |
| 1240 | 1242 |
| 1241 Args: | 1243 Args: |
| 1242 command_to_run: The command to be run to execute the performance test. | 1244 command_to_run: The command to be run to execute the performance test. |
| 1243 metric: The metric to parse out from the results of the performance test. | 1245 metric: The metric to parse out from the results of the performance test. |
| 1244 | 1246 |
| 1245 Returns: | 1247 Returns: |
| 1246 On success, it will return a tuple of the average value of the metric, | 1248 On success, it will return a tuple of the average value of the metric, |
| 1247 and a success code of 0. | 1249 and a success code of 0. |
| 1248 """ | 1250 """ |
| 1249 | 1251 |
| 1250 if self.opts.debug_ignore_perf_test: | 1252 if self.opts.debug_ignore_perf_test: |
| 1251 return ({'mean': 0.0, 'std_err': 0.0, 'std_dev': 0.0, 'values': [0.0]}, 0) | 1253 return ({'mean': 0.0, 'std_err': 0.0, 'std_dev': 0.0, 'values': [0.0]}, 0) |
| 1252 | 1254 |
| 1253 if IsWindows(): | 1255 if IsWindows(): |
| 1254 command_to_run = command_to_run.replace('/', r'\\') | 1256 command_to_run = command_to_run.replace('/', r'\\') |
| 1255 | 1257 |
| 1256 args = shlex.split(command_to_run) | 1258 args = shlex.split(command_to_run) |
| 1257 | 1259 |
| 1258 if not self._GenerateProfileIfNecessary(args): | 1260 if not self._GenerateProfileIfNecessary(args): |
| 1259 return ('Failed to generate profile for performance test.', -1) | 1261 return ('Failed to generate profile for performance test.', -1) |
| 1260 | 1262 |
| 1261 # If running a telemetry test for cros, insert the remote ip, and | 1263 # If running a telemetry test for cros, insert the remote ip, and |
| 1262 # identity parameters. | 1264 # identity parameters. |
| 1263 if self.opts.target_platform == 'cros': | 1265 is_telemetry = ('tools/perf/run_' in command_to_run or |
| 1264 if 'tools/perf/run_' in args[0]: | 1266 r'tools\\perf\\run_' in command_to_run) |
|
tonyg
2013/10/17 01:01:32
I believe you want the r'' OR the \\s, but not bot
shatch
2013/10/17 21:59:08
Done.
| |
| 1265 args.append('--remote=%s' % self.opts.cros_remote_ip) | 1267 if self.opts.target_platform == 'cros' and is_telemetry: |
| 1266 args.append('--identity=%s' % CROS_TEST_KEY_PATH) | 1268 args.append('--remote=%s' % self.opts.cros_remote_ip) |
| 1269 args.append('--identity=%s' % CROS_TEST_KEY_PATH) | |
| 1270 | |
| 1271 # These arguments only work with telemetry. | |
| 1272 reset_on_first_run = reset_on_first_run and is_telemetry | |
| 1273 upload_on_last_run = upload_on_last_run and is_telemetry | |
| 1267 | 1274 |
| 1268 cwd = os.getcwd() | 1275 cwd = os.getcwd() |
| 1269 os.chdir(self.src_cwd) | 1276 os.chdir(self.src_cwd) |
| 1270 | 1277 |
| 1271 start_time = time.time() | 1278 start_time = time.time() |
| 1272 | 1279 |
| 1273 metric_values = [] | 1280 metric_values = [] |
| 1281 output_of_all_runs = '' | |
| 1274 for i in xrange(self.opts.repeat_test_count): | 1282 for i in xrange(self.opts.repeat_test_count): |
| 1275 # Can ignore the return code since if the tests fail, it won't return 0. | 1283 # Can ignore the return code since if the tests fail, it won't return 0. |
| 1276 try: | 1284 try: |
| 1277 (output, return_code) = RunProcessAndRetrieveOutput(args) | 1285 current_args = copy.copy(args) |
| 1286 if i == 0 and reset_on_first_run: | |
| 1287 current_args.append('--reset-results') | |
| 1288 elif i == self.opts.repeat_test_count - 1 and upload_on_last_run: | |
| 1289 current_args.append('--upload-results') | |
| 1290 (output, return_code) = RunProcessAndRetrieveOutput(current_args) | |
| 1278 except OSError, e: | 1291 except OSError, e: |
| 1279 if e.errno == errno.ENOENT: | 1292 if e.errno == errno.ENOENT: |
| 1280 err_text = ("Something went wrong running the performance test. " | 1293 err_text = ("Something went wrong running the performance test. " |
| 1281 "Please review the command line:\n\n") | 1294 "Please review the command line:\n\n") |
| 1282 if 'src/' in ' '.join(args): | 1295 if 'src/' in ' '.join(args): |
| 1283 err_text += ("Check that you haven't accidentally specified a path " | 1296 err_text += ("Check that you haven't accidentally specified a path " |
| 1284 "with src/ in the command.\n\n") | 1297 "with src/ in the command.\n\n") |
| 1285 err_text += ' '.join(args) | 1298 err_text += ' '.join(args) |
| 1286 err_text += '\n' | 1299 err_text += '\n' |
| 1287 | 1300 |
| 1288 return (err_text, -1) | 1301 return (err_text, -1) |
| 1289 raise | 1302 raise |
| 1290 | 1303 |
| 1304 output_of_all_runs += output | |
| 1291 if self.opts.output_buildbot_annotations: | 1305 if self.opts.output_buildbot_annotations: |
| 1292 print output | 1306 print output |
| 1293 | 1307 |
| 1294 metric_values += self.ParseMetricValuesFromOutput(metric, output) | 1308 metric_values += self.ParseMetricValuesFromOutput(metric, output) |
| 1295 | 1309 |
| 1296 elapsed_minutes = (time.time() - start_time) / 60.0 | 1310 elapsed_minutes = (time.time() - start_time) / 60.0 |
| 1297 | 1311 |
| 1298 if elapsed_minutes >= self.opts.max_time_minutes or not metric_values: | 1312 if elapsed_minutes >= self.opts.max_time_minutes or not metric_values: |
| 1299 break | 1313 break |
| 1300 | 1314 |
| 1301 os.chdir(cwd) | 1315 os.chdir(cwd) |
| 1302 | 1316 |
| 1303 # Need to get the average value if there were multiple values. | 1317 # Need to get the average value if there were multiple values. |
| 1304 if metric_values: | 1318 if metric_values: |
| 1305 truncated_mean = CalculateTruncatedMean(metric_values, | 1319 truncated_mean = CalculateTruncatedMean(metric_values, |
| 1306 self.opts.truncate_percent) | 1320 self.opts.truncate_percent) |
| 1307 standard_err = CalculateStandardError(metric_values) | 1321 standard_err = CalculateStandardError(metric_values) |
| 1308 standard_dev = CalculateStandardDeviation(metric_values) | 1322 standard_dev = CalculateStandardDeviation(metric_values) |
| 1309 | 1323 |
| 1310 values = { | 1324 values = { |
| 1311 'mean': truncated_mean, | 1325 'mean': truncated_mean, |
| 1312 'std_err': standard_err, | 1326 'std_err': standard_err, |
| 1313 'std_dev': standard_dev, | 1327 'std_dev': standard_dev, |
| 1314 'values': metric_values, | 1328 'values': metric_values, |
| 1315 } | 1329 } |
| 1316 | 1330 |
| 1317 print 'Results of performance test: %12f %12f' % ( | 1331 print 'Results of performance test: %12f %12f' % ( |
| 1318 truncated_mean, standard_err) | 1332 truncated_mean, standard_err) |
| 1319 print | 1333 print |
| 1320 return (values, 0) | 1334 return (values, 0, output_of_all_runs) |
| 1321 else: | 1335 else: |
| 1322 return ('Invalid metric specified, or no values returned from ' | 1336 return ('Invalid metric specified, or no values returned from ' |
| 1323 'performance test.', -1) | 1337 'performance test.', -1, output_of_all_runs) |
| 1324 | 1338 |
| 1325 def FindAllRevisionsToSync(self, revision, depot): | 1339 def FindAllRevisionsToSync(self, revision, depot): |
| 1326 """Finds all dependant revisions and depots that need to be synced for a | 1340 """Finds all dependant revisions and depots that need to be synced for a |
| 1327 given revision. This is only useful in the git workflow, as an svn depot | 1341 given revision. This is only useful in the git workflow, as an svn depot |
| 1328 may be split into multiple mirrors. | 1342 may be split into multiple mirrors. |
| 1329 | 1343 |
| 1330 ie. skia is broken up into 3 git mirrors over skia/src, skia/gyp, and | 1344 ie. skia is broken up into 3 git mirrors over skia/src, skia/gyp, and |
| 1331 skia/include. To sync skia/src properly, one has to find the proper | 1345 skia/include. To sync skia/src properly, one has to find the proper |
| 1332 revisions in skia/gyp and skia/include. | 1346 revisions in skia/gyp and skia/include. |
| 1333 | 1347 |
| (...skipping 1403 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2737 | 2751 |
| 2738 Args: | 2752 Args: |
| 2739 values: a dict containing options to set. | 2753 values: a dict containing options to set. |
| 2740 | 2754 |
| 2741 Returns: | 2755 Returns: |
| 2742 An instance of BisectOptions. | 2756 An instance of BisectOptions. |
| 2743 """ | 2757 """ |
| 2744 opts = BisectOptions() | 2758 opts = BisectOptions() |
| 2745 | 2759 |
| 2746 for k, v in values.iteritems(): | 2760 for k, v in values.iteritems(): |
| 2747 assert hasattr(opts, name_to_attr[k]), 'Invalid %s attribute in '\ | 2761 assert hasattr(opts, k), 'Invalid %s attribute in '\ |
| 2748 'BisectOptions.' % name_to_attr[k] | 2762 'BisectOptions.' % k |
| 2749 setattr(opts, name_to_attr[k], v) | 2763 setattr(opts, k, v) |
| 2750 | 2764 |
| 2751 metric_values = opts.metric.split('/') | 2765 metric_values = opts.metric.split('/') |
| 2752 if len(metric_values) != 2: | 2766 if len(metric_values) != 2: |
| 2753 raise RuntimeError("Invalid metric specified: [%s]" % opts.metric) | 2767 raise RuntimeError("Invalid metric specified: [%s]" % opts.metric) |
| 2754 | 2768 |
| 2755 opts.metric = metric_values | 2769 opts.metric = metric_values |
| 2756 opts.repeat_test_count = min(max(opts.repeat_test_count, 1), 100) | 2770 opts.repeat_test_count = min(max(opts.repeat_test_count, 1), 100) |
| 2757 opts.max_time_minutes = min(max(opts.max_time_minutes, 1), 60) | 2771 opts.max_time_minutes = min(max(opts.max_time_minutes, 1), 60) |
| 2758 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25) | 2772 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25) |
| 2759 opts.truncate_percent = opts.truncate_percent / 100.0 | 2773 opts.truncate_percent = opts.truncate_percent / 100.0 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2815 return 0 | 2829 return 0 |
| 2816 finally: | 2830 finally: |
| 2817 bisect_test.PerformCleanup() | 2831 bisect_test.PerformCleanup() |
| 2818 except RuntimeError, e: | 2832 except RuntimeError, e: |
| 2819 print 'Error: %s' % e.message | 2833 print 'Error: %s' % e.message |
| 2820 print | 2834 print |
| 2821 return 1 | 2835 return 1 |
| 2822 | 2836 |
| 2823 if __name__ == '__main__': | 2837 if __name__ == '__main__': |
| 2824 sys.exit(main()) | 2838 sys.exit(main()) |
| OLD | NEW |