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

Side by Side Diff: build/android/pylib/perf/test_runner.py

Issue 301183004: Android: adds device affinity for perf tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Runs perf tests. 5 """Runs perf tests.
6 6
7 Our buildbot infrastructure requires each slave to run steps serially. 7 Our buildbot infrastructure requires each slave to run steps serially.
8 This is sub-optimal for android, where these steps can run independently on 8 This is sub-optimal for android, where these steps can run independently on
9 multiple connected devices. 9 multiple connected devices.
10 10
11 The buildbots will run this script multiple times per cycle: 11 The buildbots will run this script multiple times per cycle:
12 - First: all steps listed in --steps in will be executed in parallel using all 12 - First: all steps listed in --steps in will be executed in parallel using all
13 connected devices. Step results will be pickled to disk. Each step has a unique 13 connected devices. Step results will be pickled to disk. Each step has a unique
14 name. The result code will be ignored if the step name is listed in 14 name. The result code will be ignored if the step name is listed in
15 --flaky-steps. 15 --flaky-steps.
16 The buildbot will treat this step as a regular step, and will not process any 16 The buildbot will treat this step as a regular step, and will not process any
17 graph data. 17 graph data.
18 18
19 - Then, with -print-step STEP_NAME: at this stage, we'll simply print the file 19 - Then, with -print-step STEP_NAME: at this stage, we'll simply print the file
20 with the step results previously saved. The buildbot will then process the graph 20 with the step results previously saved. The buildbot will then process the graph
21 data accordingly. 21 data accordingly.
22 22
23 The JSON steps file contains a dictionary in the format:
24 { "version": int,
25 "steps": {
26 "foo": {
27 "device_affinity": int,
jbudorick 2014/05/29 19:45:25 This might get into how we generate the json, and
bulach 2014/05/30 09:21:26 it's complicated :) as above, we do not want to ru
28 "cmd": "script_to_execute foo"
29 },
30 "bar": {
31 "device_affinity": int,
32 "cmd": "script_to_execute bar"
33 }
34 }
35 }
bulach 2014/05/29 18:43:32 tony: this is the information this script needs. i
23 36
24 The JSON steps file contains a dictionary in the format: 37 # TODO(bulach): remove once it rolls downstream, crbug.com/378862.
38 The OLD JSON steps file contains a dictionary in the format:
25 [ 39 [
26 ["step_name_foo", "script_to_execute foo"], 40 ["step_name_foo", "script_to_execute foo"],
27 ["step_name_bar", "script_to_execute bar"] 41 ["step_name_bar", "script_to_execute bar"]
28 ] 42 ]
29 43
30 This preserves the order in which the steps are executed. 44 This preserves the order in which the steps are executed.
31 45
32 The JSON flaky steps file contains a list with step names which results should 46 The JSON flaky steps file contains a list with step names which results should
33 be ignored: 47 be ignored:
34 [ 48 [
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 if now - self._tick >= _HeartBeatLogger._PRINT_INTERVAL: 138 if now - self._tick >= _HeartBeatLogger._PRINT_INTERVAL:
125 self._tick = now 139 self._tick = now
126 print '--single-step output length %d' % self._len 140 print '--single-step output length %d' % self._len
127 sys.stdout.flush() 141 sys.stdout.flush()
128 142
129 def stop(self): 143 def stop(self):
130 self._stopped.set() 144 self._stopped.set()
131 145
132 146
133 class TestRunner(base_test_runner.BaseTestRunner): 147 class TestRunner(base_test_runner.BaseTestRunner):
134 def __init__(self, test_options, device, tests, flaky_tests): 148 def __init__(self, test_options, device, shard_index, max_shard, tests,
149 flaky_tests):
135 """A TestRunner instance runs a perf test on a single device. 150 """A TestRunner instance runs a perf test on a single device.
136 151
137 Args: 152 Args:
138 test_options: A PerfOptions object. 153 test_options: A PerfOptions object.
139 device: Device to run the tests. 154 device: Device to run the tests.
155 shard_index: the index of this device.
156 max_shards: the maximum shard index.
140 tests: a dict mapping test_name to command. 157 tests: a dict mapping test_name to command.
141 flaky_tests: a list of flaky test_name. 158 flaky_tests: a list of flaky test_name.
142 """ 159 """
143 super(TestRunner, self).__init__(device, None, 'Release') 160 super(TestRunner, self).__init__(device, None, 'Release')
144 self._options = test_options 161 self._options = test_options
162 self._shard_index = shard_index
163 self._max_shard = max_shard
145 self._tests = tests 164 self._tests = tests
146 self._flaky_tests = flaky_tests 165 self._flaky_tests = flaky_tests
147 166
148 @staticmethod 167 @staticmethod
149 def _IsBetter(result): 168 def _IsBetter(result):
150 if result['actual_exit_code'] == 0: 169 if result['actual_exit_code'] == 0:
151 return True 170 return True
152 pickled = os.path.join(constants.PERF_OUTPUT_DIR, 171 pickled = os.path.join(constants.PERF_OUTPUT_DIR,
153 result['name']) 172 result['name'])
154 if not os.path.exists(pickled): 173 if not os.path.exists(pickled):
155 return True 174 return True
156 with file(pickled, 'r') as f: 175 with file(pickled, 'r') as f:
157 previous = pickle.loads(f.read()) 176 previous = pickle.loads(f.read())
158 return result['actual_exit_code'] < previous['actual_exit_code'] 177 return result['actual_exit_code'] < previous['actual_exit_code']
159 178
160 @staticmethod 179 @staticmethod
161 def _SaveResult(result): 180 def _SaveResult(result):
162 if TestRunner._IsBetter(result): 181 if TestRunner._IsBetter(result):
163 with file(os.path.join(constants.PERF_OUTPUT_DIR, 182 with file(os.path.join(constants.PERF_OUTPUT_DIR,
164 result['name']), 'w') as f: 183 result['name']), 'w') as f:
165 f.write(pickle.dumps(result)) 184 f.write(pickle.dumps(result))
166 185
186 def _CheckDeviceAffinity(self, test_name):
187 """Returns True if test_name has affinity for this shard."""
188 affinity = (self._tests['steps'][test_name]['device_affinity'] %
189 self._max_shard)
190 if self._shard_index == affinity:
191 return True
192 logging.info('Skipping %s on %s (affinity is %s, device is %s)',
193 test_name, self.device_serial, affinity, self._shard_index)
194 return False
195
167 def _LaunchPerfTest(self, test_name): 196 def _LaunchPerfTest(self, test_name):
168 """Runs a perf test. 197 """Runs a perf test.
169 198
170 Args: 199 Args:
171 test_name: the name of the test to be executed. 200 test_name: the name of the test to be executed.
172 201
173 Returns: 202 Returns:
174 A tuple containing (Output, base_test_result.ResultType) 203 A tuple containing (Output, base_test_result.ResultType)
175 """ 204 """
205 if not self._CheckDeviceAffinity(test_name):
206 return '', base_test_result.ResultType.PASS
207
176 try: 208 try:
177 logging.warning('Unmapping device ports') 209 logging.warning('Unmapping device ports')
178 forwarder.Forwarder.UnmapAllDevicePorts(self.device) 210 forwarder.Forwarder.UnmapAllDevicePorts(self.device)
179 self.device.old_interface.RestartAdbdOnDevice() 211 self.device.old_interface.RestartAdbdOnDevice()
180 except Exception as e: 212 except Exception as e:
181 logging.error('Exception when tearing down device %s', e) 213 logging.error('Exception when tearing down device %s', e)
182 214
183 cmd = ('%s --device %s' % 215 cmd = ('%s --device %s' %
184 (self._tests[test_name], self.device.old_interface.GetDevice())) 216 (self._tests['steps'][test_name]['cmd'],
217 self.device_serial))
185 logging.info('%s : %s', test_name, cmd) 218 logging.info('%s : %s', test_name, cmd)
186 start_time = datetime.datetime.now() 219 start_time = datetime.datetime.now()
187 220
188 timeout = 5400 221 timeout = 5400
189 if self._options.no_timeout: 222 if self._options.no_timeout:
190 timeout = None 223 timeout = None
191 full_cmd = cmd 224 full_cmd = cmd
192 if self._options.dry_run: 225 if self._options.dry_run:
193 full_cmd = 'echo %s' % cmd 226 full_cmd = 'echo %s' % cmd
194 227
(...skipping 10 matching lines...) Expand all
205 withexitstatus=True, logfile=logfile, timeout=timeout, 238 withexitstatus=True, logfile=logfile, timeout=timeout,
206 env=os.environ) 239 env=os.environ)
207 if self._options.single_step: 240 if self._options.single_step:
208 # Stop the logger. 241 # Stop the logger.
209 logfile.stop() 242 logfile.stop()
210 end_time = datetime.datetime.now() 243 end_time = datetime.datetime.now()
211 if exit_code is None: 244 if exit_code is None:
212 exit_code = -1 245 exit_code = -1
213 logging.info('%s : exit_code=%d in %d secs at %s', 246 logging.info('%s : exit_code=%d in %d secs at %s',
214 test_name, exit_code, (end_time - start_time).seconds, 247 test_name, exit_code, (end_time - start_time).seconds,
215 self.device.old_interface.GetDevice()) 248 self.device_serial)
216 result_type = base_test_result.ResultType.FAIL 249 result_type = base_test_result.ResultType.FAIL
217 if exit_code == 0: 250 if exit_code == 0:
218 result_type = base_test_result.ResultType.PASS 251 result_type = base_test_result.ResultType.PASS
219 actual_exit_code = exit_code 252 actual_exit_code = exit_code
220 if test_name in self._flaky_tests: 253 if test_name in self._flaky_tests:
221 # The exit_code is used at the second stage when printing the 254 # The exit_code is used at the second stage when printing the
222 # test output. If the test is flaky, force to "0" to get that step green 255 # test output. If the test is flaky, force to "0" to get that step green
223 # whilst still gathering data to the perf dashboards. 256 # whilst still gathering data to the perf dashboards.
224 # The result_type is used by the test_dispatcher to retry the test. 257 # The result_type is used by the test_dispatcher to retry the test.
225 exit_code = 0 258 exit_code = 0
226 259
227 persisted_result = { 260 persisted_result = {
228 'name': test_name, 261 'name': test_name,
229 'output': output, 262 'output': output,
230 'exit_code': exit_code, 263 'exit_code': exit_code,
231 'actual_exit_code': actual_exit_code, 264 'actual_exit_code': actual_exit_code,
232 'result_type': result_type, 265 'result_type': result_type,
233 'total_time': (end_time - start_time).seconds, 266 'total_time': (end_time - start_time).seconds,
234 'device': self.device.old_interface.GetDevice(), 267 'device': self.device_serial,
235 'cmd': cmd, 268 'cmd': cmd,
236 } 269 }
237 self._SaveResult(persisted_result) 270 self._SaveResult(persisted_result)
238 271
239 return (output, result_type) 272 return (output, result_type)
240 273
241 def RunTest(self, test_name): 274 def RunTest(self, test_name):
242 """Run a perf test on the device. 275 """Run a perf test on the device.
243 276
244 Args: 277 Args:
245 test_name: String to use for logging the test result. 278 test_name: String to use for logging the test result.
246 279
247 Returns: 280 Returns:
248 A tuple of (TestRunResults, retry). 281 A tuple of (TestRunResults, retry).
249 """ 282 """
250 _, result_type = self._LaunchPerfTest(test_name) 283 _, result_type = self._LaunchPerfTest(test_name)
251 results = base_test_result.TestRunResults() 284 results = base_test_result.TestRunResults()
252 results.AddResult(base_test_result.BaseTestResult(test_name, result_type)) 285 results.AddResult(base_test_result.BaseTestResult(test_name, result_type))
253 retry = None 286 retry = None
254 if not results.DidRunPass(): 287 if not results.DidRunPass():
255 retry = test_name 288 retry = test_name
256 return results, retry 289 return results, retry
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698