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

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

Issue 27165006: First pass performance try bot. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add --results-label. Created 7 years, 2 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 | « tools/bisect_utils.py ('k') | tools/run-perf-test.cfg » ('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 """Run Performance Test Bisect Tool 6 """Run Performance Test Bisect Tool
7 7
8 This script is used by a trybot to run the src/tools/bisect-perf-regression.py 8 This script is used by a trybot to run the src/tools/bisect-perf-regression.py
9 script with the parameters specified in run-bisect-perf-regression.cfg. It will 9 script with the parameters specified in run-bisect-perf-regression.cfg. It will
10 check out a copy of the depot in a subdirectory 'bisect' of the working 10 check out a copy of the depot in a subdirectory 'bisect' of the working
11 directory provided, and run the bisect-perf-regression.py script there. 11 directory provided, and run the bisect-perf-regression.py script there.
12 12
13 """ 13 """
14 14
15 import imp 15 import imp
16 import optparse 16 import optparse
17 import os 17 import os
18 import subprocess 18 import subprocess
19 import sys 19 import sys
20 import traceback 20 import traceback
21 21
22 import bisect_utils
23 bisect = imp.load_source('bisect-perf-regression',
24 os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])),
25 'bisect-perf-regression.py'))
26
27
22 CROS_BOARD_ENV = 'BISECT_CROS_BOARD' 28 CROS_BOARD_ENV = 'BISECT_CROS_BOARD'
23 CROS_IP_ENV = 'BISECT_CROS_IP' 29 CROS_IP_ENV = 'BISECT_CROS_IP'
24 30
25 def LoadConfigFile(path_to_file): 31
26 """Attempts to load the file 'run-bisect-perf-regression.cfg' as a module 32 class Goma(object):
tonyg 2013/10/18 01:00:30 Thanks, looks really clean :)
33
34 def __init__(self, path_to_goma):
35 if path_to_goma:
36 self._abs_path_to_goma = os.path.abspath(path_to_goma)
37 self._abs_path_to_goma_file = self._GetExecutablePath(
38 self._abs_path_to_goma)
39
40 def __enter__(self):
41 if self._HasGOMAPath():
42 self._SetupAndStart()
43 return self
44
45 def __exit__(self, *_):
46 if self._HasGOMAPath():
47 self._Stop()
48
49 def _HasGOMAPath(self):
50 return hasattr(self, '_abs_path_to_goma')
tonyg 2013/10/18 01:00:30 Probably cleaner to set these to None in __init__
shatch 2013/10/18 17:09:50 Done.
51
52 def _GetExecutablePath(self, path_to_goma):
53 if os.name == 'nt':
54 return os.path.join(path_to_goma, 'goma_ctl.bat')
55 else:
56 return os.path.join(path_to_goma, 'goma_ctl.sh')
57
58 def _SetupEnvVars(self):
59 if os.name == 'nt':
60 os.environ['CC'] = (os.path.join(self._abs_path_to_goma, 'gomacc.exe') +
61 ' cl.exe')
62 os.environ['CXX'] = (os.path.join(self._abs_path_to_goma, 'gomacc.exe') +
63 ' cl.exe')
64 else:
65 os.environ['PATH'] = os.pathsep.join([self._abs_path_to_goma,
66 os.environ['PATH']])
67
68 def _SetupAndStart(self):
69 """Sets up GOMA and launches it.
70
71 Args:
72 path_to_goma: Path to goma directory.
73
74 Returns:
75 True if successful."""
76 self._SetupEnvVars()
77
78 # Sometimes goma is lingering around if something went bad on a previous
79 # run. Stop it before starting a new process. Can ignore the return code
80 # since it will return an error if it wasn't running.
81 self._Stop()
82
83 if subprocess.call([self._abs_path_to_goma_file, 'start']):
84 raise RuntimeError('GOMA failed to start.')
85
86 def _Stop(self):
87 subprocess.call([self._abs_path_to_goma_file, 'stop'])
88
89
90
91 def _LoadConfigFile(path_to_file):
92 """Attempts to load the specified config file as a module
27 and grab the global config dict. 93 and grab the global config dict.
28 94
29 Args: 95 Args:
30 path_to_file: Path to the run-bisect-perf-regression.cfg file. 96 path_to_file: Path to the file.
31 97
32 Returns: 98 Returns:
33 The config dict which should be formatted as follows: 99 The config dict which should be formatted as follows:
34 {'command': string, 'good_revision': string, 'bad_revision': string 100 {'command': string, 'good_revision': string, 'bad_revision': string
35 'metric': string}. 101 'metric': string, etc...}.
36 Returns None on failure. 102 Returns None on failure.
37 """ 103 """
38 try: 104 try:
39 local_vars = {} 105 local_vars = {}
40 execfile(os.path.join(path_to_file, 'run-bisect-perf-regression.cfg'), 106 execfile(path_to_file, local_vars)
41 local_vars)
42 107
43 return local_vars['config'] 108 return local_vars['config']
44 except: 109 except:
45 print 110 print
46 traceback.print_exc() 111 traceback.print_exc()
47 print 112 print
48 return None 113 return None
49 114
50 115
51 def RunBisectionScript(config, working_directory, path_to_file, path_to_goma): 116 def _OutputFailedResults(text_to_print):
117 bisect_utils.OutputAnnotationStepStart('Results - Failed')
118 print
119 print text_to_print
120 print
121 bisect_utils.OutputAnnotationStepClosed()
122
123
124 def _CreateBisectOptionsFromConfig(config):
125 opts_dict = {}
126 opts_dict['command'] = config['command']
127 opts_dict['metric'] = config['metric']
128
129 if config['repeat_count']:
130 opts_dict['repeat_test_count'] = int(config['repeat_count'])
131
132 if config['truncate_percent']:
133 opts_dict['truncate_percent'] = int(config['truncate_percent'])
134
135 if config['max_time_minutes']:
136 opts_dict['max_time_minutes'] = int(config['max_time_minutes'])
137
138 if config.has_key('use_goma'):
139 opts_dict['use_goma'] = config['use_goma']
140
141 opts_dict['build_preference'] = 'ninja'
142 opts_dict['output_buildbot_annotations'] = True
143
144 if '--browser=cros' in config['command']:
145 opts_dict['target_platform'] = 'cros'
146
147 if os.environ[CROS_BOARD_ENV] and os.environ[CROS_IP_ENV]:
148 opts_dict['cros_board'] = os.environ[CROS_BOARD_ENV]
149 opts_dict['cros_remote_ip'] = os.environ[CROS_IP_ENV]
150 else:
151 raise RuntimeError('Cros build selected, but BISECT_CROS_IP or'
152 'BISECT_CROS_BOARD undefined.')
153 elif 'android' in config['command']:
154 opts_dict['target_platform'] = 'android'
155
156 return bisect.BisectOptions.FromDict(opts_dict)
157
158
159 def _RunPerformanceTest(config, path_to_file):
160 # Bisect script expects to be run from src
161 os.chdir(os.path.join(path_to_file, '..'))
162
163 bisect_utils.OutputAnnotationStepStart('Building With Patch')
164
165 opts = _CreateBisectOptionsFromConfig(config)
166 b = bisect.BisectPerformanceMetrics(None, opts)
167
168 if bisect_utils.RunGClient(['runhooks']):
169 raise RuntimeError('Failed to run gclient runhooks')
170
171 if not b.BuildCurrentRevision('chromium'):
172 raise RuntimeError('Patched version failed to build.')
173
174 bisect_utils.OutputAnnotationStepClosed()
175 bisect_utils.OutputAnnotationStepStart('Running With Patch')
176
177 results_with_patch = b.RunPerformanceTestAndParseResults(
178 opts.command, opts.metric, reset_on_first_run=True, results_label='Patch')
179
180 if results_with_patch[1]:
181 raise RuntimeError('Patched version failed to run performance test.')
182
183 bisect_utils.OutputAnnotationStepClosed()
184
185 bisect_utils.OutputAnnotationStepStart('Reverting Patch')
186 if bisect_utils.RunGClient(['revert']):
187 raise RuntimeError('Failed to run gclient runhooks')
188 bisect_utils.OutputAnnotationStepClosed()
189
190 bisect_utils.OutputAnnotationStepStart('Building Without Patch')
191
192 if bisect_utils.RunGClient(['runhooks']):
193 raise RuntimeError('Failed to run gclient runhooks')
194
195 if not b.BuildCurrentRevision('chromium'):
196 raise RuntimeError('Unpatched version failed to build.')
197
198 bisect_utils.OutputAnnotationStepClosed()
199 bisect_utils.OutputAnnotationStepStart('Running Without Patch')
200
201 results_without_patch = b.RunPerformanceTestAndParseResults(
202 opts.command, opts.metric, upload_on_last_run=True, results_label='ToT')
203
204 if results_without_patch[1]:
205 raise RuntimeError('Unpatched version failed to run performance test.')
206
207 # Find the link to the cloud stored results file.
208 output = results_without_patch[2]
209 cloud_file_link = [t for t in output.splitlines() if 'View online at' in t]
tonyg 2013/10/18 01:00:30 This is a little brittle. Is there a better searc
shatch 2013/10/18 17:09:50 Changed it to search for storage.googleapis.com/ch
210 if cloud_file_link:
211 cloud_file_link = cloud_file_link[0]
212 else:
213 cloud_file_link = ''
214
215 # Calculate the % difference in the means of the 2 runs.
216 percent_diff_in_means = (results_with_patch[0]['mean'] /
217 max(0.0001, results_without_patch[0]['mean'])) * 100.0 - 100.0
218 std_err = bisect.CalculatePooledStandardError(
219 [results_with_patch[0]['values'], results_without_patch[0]['values']])
220
221 bisect_utils.OutputAnnotationStepClosed()
222 bisect_utils.OutputAnnotationStepStart('Results - %.02f +- %0.02f delta' %
223 percent_diff_in_means, std_err)
224 print ' %s %s %s' % (''.center(10, ' '), 'Mean'.center(20, ' '),
225 'Std. Error'.center(20, ' '))
226 print ' %s %s %s' % ('Patch'.center(10, ' '),
227 ('%.02f' % results_with_patch[0]['mean']).center(20, ' '),
228 ('%.02f' % results_with_patch[0]['std_err']).center(20, ' '))
229 print ' %s %s %s' % ('No Patch'.center(10, ' '),
230 ('%.02f' % results_without_patch[0]['mean']).center(20, ' '),
231 ('%.02f' % results_without_patch[0]['std_err']).center(20, ' '))
232 if cloud_file_link:
233 bisect_utils.OutputAnnotationStepLink('HTML Results', cloud_file_link)
234 bisect_utils.OutputAnnotationStepClosed()
235
236
237 def _SetupAndRunPerformanceTest(config, path_to_file, path_to_goma):
238 """Attempts to build and run the current revision with and without the
239 current patch, with the parameters passed in.
240
241 Args:
242 config: The config read from run-perf-test.cfg.
243 path_to_file: Path to the bisect-perf-regression.py script.
244 path_to_goma: Path to goma directory.
245
246 Returns:
247 0 on success, otherwise 1.
248 """
249 try:
250 with Goma(path_to_goma) as goma:
251 config['use_goma'] = bool(path_to_goma)
252 _RunPerformanceTest(config, path_to_file)
253 return 0
254 except RuntimeError, e:
255 bisect_utils.OutputAnnotationStepClosed()
256 _OutputFailedResults('Error: %s' % e.message)
257 return 1
258
259
260 def _RunBisectionScript(config, working_directory, path_to_file, path_to_goma):
52 """Attempts to execute src/tools/bisect-perf-regression.py with the parameters 261 """Attempts to execute src/tools/bisect-perf-regression.py with the parameters
53 passed in. 262 passed in.
54 263
55 Args: 264 Args:
56 config: A dict containing the parameters to pass to the script. 265 config: A dict containing the parameters to pass to the script.
57 working_directory: A working directory to provide to the 266 working_directory: A working directory to provide to the
58 bisect-perf-regression.py script, where it will store it's own copy of 267 bisect-perf-regression.py script, where it will store it's own copy of
59 the depot. 268 the depot.
60 path_to_file: Path to the bisect-perf-regression.py script. 269 path_to_file: Path to the bisect-perf-regression.py script.
61 path_to_goma: Path to goma directory. 270 path_to_goma: Path to goma directory.
(...skipping 29 matching lines...) Expand all
91 cmd.extend(['--cros_remote_ip', os.environ[CROS_IP_ENV]]) 300 cmd.extend(['--cros_remote_ip', os.environ[CROS_IP_ENV]])
92 else: 301 else:
93 print 'Error: Cros build selected, but BISECT_CROS_IP or'\ 302 print 'Error: Cros build selected, but BISECT_CROS_IP or'\
94 'BISECT_CROS_BOARD undefined.' 303 'BISECT_CROS_BOARD undefined.'
95 print 304 print
96 return 1 305 return 1
97 306
98 if 'android' in config['command']: 307 if 'android' in config['command']:
99 cmd.extend(['--target_platform', 'android']) 308 cmd.extend(['--target_platform', 'android'])
100 309
101 goma_file = ''
102 if path_to_goma: 310 if path_to_goma:
103 path_to_goma = os.path.abspath(path_to_goma)
104
105 if os.name == 'nt':
106 os.environ['CC'] = os.path.join(path_to_goma, 'gomacc.exe') + ' cl.exe'
107 os.environ['CXX'] = os.path.join(path_to_goma, 'gomacc.exe') + ' cl.exe'
108 goma_file = os.path.join(path_to_goma, 'goma_ctl.bat')
109 else:
110 os.environ['PATH'] = os.pathsep.join([path_to_goma, os.environ['PATH']])
111 goma_file = os.path.join(path_to_goma, 'goma_ctl.sh')
112
113 cmd.append('--use_goma') 311 cmd.append('--use_goma')
114 312
115 # Sometimes goma is lingering around if something went bad on a previous
116 # run. Stop it before starting a new process. Can ignore the return code
117 # since it will return an error if it wasn't running.
118 subprocess.call([goma_file, 'stop'])
119
120 return_code = subprocess.call([goma_file, 'start'])
121 if return_code:
122 print 'Error: goma failed to start.'
123 print
124 return return_code
125
126 cmd = [str(c) for c in cmd] 313 cmd = [str(c) for c in cmd]
127 314
128 return_code = subprocess.call(cmd) 315 with Goma(path_to_goma) as goma:
129 316 return_code = subprocess.call(cmd)
130 if path_to_goma:
131 subprocess.call([goma_file, 'stop'])
132 317
133 if return_code: 318 if return_code:
134 print 'Error: bisect-perf-regression.py returned with error %d' %\ 319 print 'Error: bisect-perf-regression.py returned with error %d' %\
135 return_code 320 return_code
136 print 321 print
137 322
138 return return_code 323 return return_code
139 324
140 325
141 def main(): 326 def main():
142 327
143 usage = ('%prog [options] [-- chromium-options]\n' 328 usage = ('%prog [options] [-- chromium-options]\n'
144 'Used by a trybot to run the bisection script using the parameters' 329 'Used by a trybot to run the bisection script using the parameters'
145 ' provided in the run-bisect-perf-regression.cfg file.') 330 ' provided in the run-bisect-perf-regression.cfg file.')
146 331
147 parser = optparse.OptionParser(usage=usage) 332 parser = optparse.OptionParser(usage=usage)
148 parser.add_option('-w', '--working_directory', 333 parser.add_option('-w', '--working_directory',
149 type='str', 334 type='str',
150 help='A working directory to supply to the bisection ' 335 help='A working directory to supply to the bisection '
151 'script, which will use it as the location to checkout ' 336 'script, which will use it as the location to checkout '
152 'a copy of the chromium depot.') 337 'a copy of the chromium depot.')
153 parser.add_option('-p', '--path_to_goma', 338 parser.add_option('-p', '--path_to_goma',
154 type='str', 339 type='str',
155 help='Path to goma directory. If this is supplied, goma ' 340 help='Path to goma directory. If this is supplied, goma '
156 'builds will be enabled.') 341 'builds will be enabled.')
157 (opts, args) = parser.parse_args() 342 (opts, args) = parser.parse_args()
158 343
159 if not opts.working_directory: 344 path_to_current_directory = os.path.abspath(os.path.dirname(sys.argv[0]))
160 print 'Error: missing required parameter: --working_directory' 345 path_to_bisect_cfg = os.path.join(path_to_current_directory,
161 print 346 'run-bisect-perf-regression.cfg')
162 parser.print_help()
163 return 1
164 347
165 path_to_file = os.path.abspath(os.path.dirname(sys.argv[0])) 348 config = _LoadConfigFile(path_to_bisect_cfg)
166 349
167 config = LoadConfigFile(path_to_file) 350 # Check if the config is empty
168 if not config: 351 config_has_values = [v for v in config.values() if v]
169 print 'Error: Could not load config file. Double check your changes to '\
170 'run-bisect-perf-regression.cfg for syntax errors.'
171 print
172 return 1
173 352
174 return RunBisectionScript(config, opts.working_directory, path_to_file, 353 if config and config_has_values:
175 opts.path_to_goma) 354 if not opts.working_directory:
355 print 'Error: missing required parameter: --working_directory'
356 print
357 parser.print_help()
358 return 1
359
360 return _RunBisectionScript(config, opts.working_directory,
361 path_to_current_directory, opts.path_to_goma)
362 else:
363 path_to_perf_cfg = os.path.join(
364 os.path.abspath(os.path.dirname(sys.argv[0])), 'run-perf-test.cfg')
365
366 config = _LoadConfigFile(path_to_perf_cfg)
367
368 if config:
369 return _SetupAndRunPerformanceTest(config, path_to_current_directory,
370 opts.path_to_goma)
371 else:
372 print 'Error: Could not load config file. Double check your changes to '\
373 'run-bisect-perf-regression.cfg for syntax errors.'
374 print
375 return 1
176 376
177 377
178 if __name__ == '__main__': 378 if __name__ == '__main__':
179 sys.exit(main()) 379 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/bisect_utils.py ('k') | tools/run-perf-test.cfg » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698