| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Helper script to shard build bot steps and save results to disk. | 7 """Helper script to shard build bot steps and save results to disk. |
| 8 | 8 |
| 9 Our buildbot infrastructure requires each slave to run steps serially. | 9 Our buildbot infrastructure requires each slave to run steps serially. |
| 10 This is sub-optimal for android, where these steps can run independently on | 10 This is sub-optimal for android, where these steps can run independently on |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 import datetime | 38 import datetime |
| 39 import json | 39 import json |
| 40 import logging | 40 import logging |
| 41 import multiprocessing | 41 import multiprocessing |
| 42 import optparse | 42 import optparse |
| 43 import pexpect | 43 import pexpect |
| 44 import pickle | 44 import pickle |
| 45 import os | 45 import os |
| 46 import signal | 46 import signal |
| 47 import shutil | 47 import shutil |
| 48 import subprocess |
| 48 import sys | 49 import sys |
| 49 | 50 |
| 50 from pylib import android_commands | 51 from pylib import android_commands |
| 51 from pylib import cmd_helper | 52 from pylib import cmd_helper |
| 52 from pylib import constants | 53 from pylib import constants |
| 53 from pylib import ports | 54 from pylib import ports |
| 54 | 55 |
| 55 | 56 |
| 56 _OUTPUT_DIR = os.path.join(constants.CHROME_DIR, 'out', 'step_results') | 57 _OUTPUT_DIR = os.path.join(constants.CHROME_DIR, 'out', 'step_results') |
| 57 | 58 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 77 result = {'name': step['name'], | 78 result = {'name': step['name'], |
| 78 'output': output, | 79 'output': output, |
| 79 'exit_code': exit_code or 0, | 80 'exit_code': exit_code or 0, |
| 80 'total_time': (end_time - start_time).seconds, | 81 'total_time': (end_time - start_time).seconds, |
| 81 'device': step['device']} | 82 'device': step['device']} |
| 82 _SaveResult(result) | 83 _SaveResult(result) |
| 83 results += [result] | 84 results += [result] |
| 84 return results | 85 return results |
| 85 | 86 |
| 86 | 87 |
| 88 class _LogCatMonitor(object): |
| 89 def __init__(self): |
| 90 self._monitor = None |
| 91 self._logcat_dir = os.path.join(constants.CHROME_DIR, 'out', |
| 92 'sharded_steps_logcat_monitor') |
| 93 |
| 94 def __enter__(self): |
| 95 cmd = [os.path.join(constants.CHROME_DIR, 'build', 'android', |
| 96 'adb_logcat_monitor.py'), self._logcat_dir] |
| 97 self._monitor = subprocess.Popen(cmd, cwd=constants.CHROME_DIR) |
| 98 |
| 99 def __exit__(self, *args): |
| 100 if self._monitor: |
| 101 # adb_logcat_printer will kill the monitor. |
| 102 self._monitor = None |
| 103 cmd = [os.path.join(constants.CHROME_DIR, 'build', 'android', |
| 104 'adb_logcat_printer.py'), self._logcat_dir] |
| 105 cmd = subprocess.Popen(cmd, cwd=constants.CHROME_DIR) |
| 106 cmd.wait() |
| 107 |
| 108 |
| 87 def _RunShardedSteps(steps, devices): | 109 def _RunShardedSteps(steps, devices): |
| 88 assert steps | 110 assert steps |
| 89 assert devices, 'No devices connected?' | 111 assert devices, 'No devices connected?' |
| 90 if os.path.exists(_OUTPUT_DIR): | 112 if os.path.exists(_OUTPUT_DIR): |
| 91 assert '/step_results' in _OUTPUT_DIR | 113 assert '/step_results' in _OUTPUT_DIR |
| 92 shutil.rmtree(_OUTPUT_DIR) | 114 shutil.rmtree(_OUTPUT_DIR) |
| 93 if not os.path.exists(_OUTPUT_DIR): | 115 if not os.path.exists(_OUTPUT_DIR): |
| 94 os.makedirs(_OUTPUT_DIR) | 116 os.makedirs(_OUTPUT_DIR) |
| 95 step_names = sorted(steps.keys()) | 117 step_names = sorted(steps.keys()) |
| 96 all_params = [] | 118 all_params = [] |
| 97 num_devices = len(devices) | 119 num_devices = len(devices) |
| 98 shard_size = (len(steps) + num_devices - 1) / num_devices | 120 shard_size = (len(steps) + num_devices - 1) / num_devices |
| 99 for i, device in enumerate(devices): | 121 for i, device in enumerate(devices): |
| 100 steps_per_device = [] | 122 steps_per_device = [] |
| 101 for s in steps.keys()[i * shard_size:(i + 1) * shard_size]: | 123 for s in steps.keys()[i * shard_size:(i + 1) * shard_size]: |
| 102 steps_per_device += [{'name': s, | 124 steps_per_device += [{'name': s, |
| 103 'device': device, | 125 'device': device, |
| 104 'cmd': steps[s] + ' --device ' + device + | 126 'cmd': steps[s] + ' --device ' + device + |
| 105 ' --keep_test_server_ports'}] | 127 ' --keep_test_server_ports'}] |
| 106 all_params += [steps_per_device] | 128 all_params += [steps_per_device] |
| 107 print 'Start sharding (note: output is not synchronized...)' | 129 print 'Start sharding (note: output is not synchronized...)' |
| 108 print '*' * 80 | 130 print '*' * 80 |
| 109 start_time = datetime.datetime.now() | 131 start_time = datetime.datetime.now() |
| 110 pool = multiprocessing.Pool(processes=num_devices) | 132 |
| 111 async_results = pool.map_async(_RunStepsPerDevice, all_params) | 133 with _LogCatMonitor() as logcat_monitor: |
| 112 results_per_device = async_results.get(999999) | 134 pool = multiprocessing.Pool(processes=num_devices) |
| 135 async_results = pool.map_async(_RunStepsPerDevice, all_params) |
| 136 results_per_device = async_results.get(999999) |
| 137 |
| 113 end_time = datetime.datetime.now() | 138 end_time = datetime.datetime.now() |
| 114 print '*' * 80 | 139 print '*' * 80 |
| 115 print 'Finished sharding.' | 140 print 'Finished sharding.' |
| 116 print 'Summary' | 141 print 'Summary' |
| 117 total_time = 0 | 142 total_time = 0 |
| 118 for results in results_per_device: | 143 for results in results_per_device: |
| 119 for result in results: | 144 for result in results: |
| 120 print('%s : exit_code=%d in %d secs at %s' % | 145 print('%s : exit_code=%d in %d secs at %s' % |
| 121 (result['name'], result['exit_code'], result['total_time'], | 146 (result['name'], result['exit_code'], result['total_time'], |
| 122 result['device'])) | 147 result['device'])) |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 print 'You must attach a device' | 204 print 'You must attach a device' |
| 180 return 1 | 205 return 1 |
| 181 | 206 |
| 182 with file(options.steps, 'r') as f: | 207 with file(options.steps, 'r') as f: |
| 183 steps = json.load(f) | 208 steps = json.load(f) |
| 184 return _RunShardedSteps(steps, devices) | 209 return _RunShardedSteps(steps, devices) |
| 185 | 210 |
| 186 | 211 |
| 187 if __name__ == '__main__': | 212 if __name__ == '__main__': |
| 188 sys.exit(main(sys.argv)) | 213 sys.exit(main(sys.argv)) |
| OLD | NEW |