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 |