OLD | NEW |
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 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 import time | 59 import time |
60 | 60 |
61 from pylib import cmd_helper | 61 from pylib import cmd_helper |
62 from pylib import constants | 62 from pylib import constants |
63 from pylib import forwarder | 63 from pylib import forwarder |
64 from pylib.base import base_test_result | 64 from pylib.base import base_test_result |
65 from pylib.base import base_test_runner | 65 from pylib.base import base_test_runner |
66 from pylib.device import battery_utils | 66 from pylib.device import battery_utils |
67 from pylib.device import device_errors | 67 from pylib.device import device_errors |
68 | 68 |
| 69 NUM_DEVICE_AFFINITIES = 8 |
| 70 |
69 | 71 |
70 def GetPersistedResult(test_name): | 72 def GetPersistedResult(test_name): |
71 file_name = os.path.join(constants.PERF_OUTPUT_DIR, test_name) | 73 file_name = os.path.join(constants.PERF_OUTPUT_DIR, test_name) |
72 if not os.path.exists(file_name): | 74 if not os.path.exists(file_name): |
73 logging.error('File not found %s', file_name) | 75 logging.error('File not found %s', file_name) |
74 return None | 76 return None |
75 | 77 |
76 with file(file_name, 'r') as f: | 78 with file(file_name, 'r') as f: |
77 return pickle.loads(f.read()) | 79 return pickle.loads(f.read()) |
78 | 80 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 if now - self._tick >= _HeartBeatLogger._PRINT_INTERVAL: | 168 if now - self._tick >= _HeartBeatLogger._PRINT_INTERVAL: |
167 self._tick = now | 169 self._tick = now |
168 print '--single-step output length %d' % self._len | 170 print '--single-step output length %d' % self._len |
169 sys.stdout.flush() | 171 sys.stdout.flush() |
170 | 172 |
171 def stop(self): | 173 def stop(self): |
172 self._stopped.set() | 174 self._stopped.set() |
173 | 175 |
174 | 176 |
175 class TestRunner(base_test_runner.BaseTestRunner): | 177 class TestRunner(base_test_runner.BaseTestRunner): |
176 def __init__(self, test_options, device, shard_index, max_shard, tests, | 178 def __init__(self, test_options, device, affinities, tests, flaky_tests): |
177 flaky_tests): | |
178 """A TestRunner instance runs a perf test on a single device. | 179 """A TestRunner instance runs a perf test on a single device. |
179 | 180 |
180 Args: | 181 Args: |
181 test_options: A PerfOptions object. | 182 test_options: A PerfOptions object. |
182 device: Device to run the tests. | 183 device: Device to run the tests. |
183 shard_index: the index of this device. | 184 affinities: the list of affinities to run on this shard. |
184 max_shards: the maximum shard index. | |
185 tests: a dict mapping test_name to command. | 185 tests: a dict mapping test_name to command. |
186 flaky_tests: a list of flaky test_name. | 186 flaky_tests: a list of flaky test_name. |
187 """ | 187 """ |
188 super(TestRunner, self).__init__(device, None) | 188 super(TestRunner, self).__init__(device, None) |
189 self._options = test_options | 189 self._options = test_options |
190 self._shard_index = shard_index | 190 self._affinities = affinities |
191 self._max_shard = max_shard | |
192 self._tests = tests | 191 self._tests = tests |
193 self._flaky_tests = flaky_tests | 192 self._flaky_tests = flaky_tests |
194 self._output_dir = None | 193 self._output_dir = None |
195 self._device_battery = battery_utils.BatteryUtils(self.device) | 194 self._device_battery = battery_utils.BatteryUtils(self.device) |
196 | 195 |
197 @staticmethod | 196 @staticmethod |
198 def _IsBetter(result): | 197 def _IsBetter(result): |
199 if result['actual_exit_code'] == 0: | 198 if result['actual_exit_code'] == 0: |
200 return True | 199 return True |
201 pickled = os.path.join(constants.PERF_OUTPUT_DIR, | 200 pickled = os.path.join(constants.PERF_OUTPUT_DIR, |
202 result['name']) | 201 result['name']) |
203 if not os.path.exists(pickled): | 202 if not os.path.exists(pickled): |
204 return True | 203 return True |
205 with file(pickled, 'r') as f: | 204 with file(pickled, 'r') as f: |
206 previous = pickle.loads(f.read()) | 205 previous = pickle.loads(f.read()) |
207 return result['actual_exit_code'] < previous['actual_exit_code'] | 206 return result['actual_exit_code'] < previous['actual_exit_code'] |
208 | 207 |
209 @staticmethod | 208 @staticmethod |
210 def _SaveResult(result): | 209 def _SaveResult(result): |
211 if TestRunner._IsBetter(result): | 210 if TestRunner._IsBetter(result): |
212 with file(os.path.join(constants.PERF_OUTPUT_DIR, | 211 with file(os.path.join(constants.PERF_OUTPUT_DIR, |
213 result['name']), 'w') as f: | 212 result['name']), 'w') as f: |
214 f.write(pickle.dumps(result)) | 213 f.write(pickle.dumps(result)) |
215 | 214 |
216 def _CheckDeviceAffinity(self, test_name): | 215 def _CheckDeviceAffinity(self, test_name): |
217 """Returns True if test_name has affinity for this shard.""" | 216 """Returns True if test_name has affinity for this shard.""" |
218 affinity = (self._tests['steps'][test_name]['device_affinity'] % | 217 affinity = self._tests['steps'][test_name]['device_affinity'] |
219 self._max_shard) | 218 if not (0 <= affinity and affinity < NUM_DEVICE_AFFINITIES): |
220 if self._shard_index == affinity: | 219 raise Exception('Got out-of-range device affinity %s' % affinity) |
| 220 if affinity in self._affinities: |
221 return True | 221 return True |
222 logging.info('Skipping %s on %s (affinity is %s, device is %s)', | 222 logging.info( |
223 test_name, self.device_serial, affinity, self._shard_index) | 223 'Skipping %s on %s (affinity is %s, allowed affinities are %s)', |
| 224 test_name, self.device_serial, affinity, self._affinities) |
224 return False | 225 return False |
225 | 226 |
226 def _CleanupOutputDirectory(self): | 227 def _CleanupOutputDirectory(self): |
227 if self._output_dir: | 228 if self._output_dir: |
228 shutil.rmtree(self._output_dir, ignore_errors=True) | 229 shutil.rmtree(self._output_dir, ignore_errors=True) |
229 self._output_dir = None | 230 self._output_dir = None |
230 | 231 |
231 def _ReadChartjsonOutput(self): | 232 def _ReadChartjsonOutput(self): |
232 if not self._output_dir: | 233 if not self._output_dir: |
233 return '' | 234 return '' |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 Returns: | 356 Returns: |
356 A tuple of (TestRunResults, retry). | 357 A tuple of (TestRunResults, retry). |
357 """ | 358 """ |
358 _, result_type = self._LaunchPerfTest(test_name) | 359 _, result_type = self._LaunchPerfTest(test_name) |
359 results = base_test_result.TestRunResults() | 360 results = base_test_result.TestRunResults() |
360 results.AddResult(base_test_result.BaseTestResult(test_name, result_type)) | 361 results.AddResult(base_test_result.BaseTestResult(test_name, result_type)) |
361 retry = None | 362 retry = None |
362 if not results.DidRunPass(): | 363 if not results.DidRunPass(): |
363 retry = test_name | 364 retry = test_name |
364 return results, retry | 365 return results, retry |
OLD | NEW |