| 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 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 Returns: | 349 Returns: |
| 349 A tuple of (TestRunResults, retry). | 350 A tuple of (TestRunResults, retry). |
| 350 """ | 351 """ |
| 351 _, result_type = self._LaunchPerfTest(test_name) | 352 _, result_type = self._LaunchPerfTest(test_name) |
| 352 results = base_test_result.TestRunResults() | 353 results = base_test_result.TestRunResults() |
| 353 results.AddResult(base_test_result.BaseTestResult(test_name, result_type)) | 354 results.AddResult(base_test_result.BaseTestResult(test_name, result_type)) |
| 354 retry = None | 355 retry = None |
| 355 if not results.DidRunPass(): | 356 if not results.DidRunPass(): |
| 356 retry = test_name | 357 retry = test_name |
| 357 return results, retry | 358 return results, retry |
| OLD | NEW |