Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright 2013 The Chromium Authors. All rights reserved. | 3 # Copyright 2013 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 # Find the most recent tombstone file(s) on all connected devices | 7 # Find the most recent tombstone file(s) on all connected devices |
| 8 # and prints their stacks. | 8 # and prints their stacks. |
| 9 # | 9 # |
| 10 # Assumes tombstone file was created with current symbols. | 10 # Assumes tombstone file was created with current symbols. |
| 11 | 11 |
| 12 import argparse | 12 import argparse |
| 13 import datetime | 13 import datetime |
| 14 import logging | 14 import logging |
| 15 import multiprocessing | 15 import multiprocessing |
| 16 import os | 16 import os |
| 17 import re | 17 import re |
| 18 import subprocess | 18 import subprocess |
| 19 import sys | 19 import sys |
| 20 | 20 |
| 21 import devil_chromium | 21 import devil_chromium |
| 22 | 22 |
| 23 from devil.android import device_blacklist | 23 from devil.android import device_blacklist |
| 24 from devil.android import device_errors | 24 from devil.android import device_errors |
| 25 from devil.android import device_utils | 25 from devil.android import device_utils |
| 26 from devil.utils import run_tests_helper | 26 from devil.utils import run_tests_helper |
| 27 from pylib import constants | 27 from pylib import constants |
| 28 | 28 |
| 29 sys.path.insert(0, os.path.abspath(os.path.join( | |
| 30 constants.DIR_SOURCE_ROOT, 'tools', 'swarming_client'))) | |
| 31 from libs.logdog import bootstrap # pylint: disable=import-error | |
| 32 | |
| 29 | 33 |
| 30 _TZ_UTC = {'TZ': 'UTC'} | 34 _TZ_UTC = {'TZ': 'UTC'} |
| 31 | 35 |
| 32 | 36 |
| 33 def _ListTombstones(device): | 37 def _ListTombstones(device): |
| 34 """List the tombstone files on the device. | 38 """List the tombstone files on the device. |
| 35 | 39 |
| 36 Args: | 40 Args: |
| 37 device: An instance of DeviceUtils. | 41 device: An instance of DeviceUtils. |
| 38 | 42 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 | 99 |
| 96 | 100 |
| 97 def _DeviceAbiToArch(device_abi): | 101 def _DeviceAbiToArch(device_abi): |
| 98 # The order of this list is significant to find the more specific match (e.g., | 102 # The order of this list is significant to find the more specific match (e.g., |
| 99 # arm64) before the less specific (e.g., arm). | 103 # arm64) before the less specific (e.g., arm). |
| 100 arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips'] | 104 arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips'] |
| 101 for arch in arches: | 105 for arch in arches: |
| 102 if arch in device_abi: | 106 if arch in device_abi: |
| 103 return arch | 107 return arch |
| 104 raise RuntimeError('Unknown device ABI: %s' % device_abi) | 108 raise RuntimeError('Unknown device ABI: %s' % device_abi) |
| 105 | 109 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 106 def _ResolveSymbols(tombstone_data, include_stack, device_abi): | 110 def _ResolveSymbols(tombstone_data, include_stack, device_abi): |
| 107 """Run the stack tool for given tombstone input. | 111 """Run the stack tool for given tombstone input. |
| 108 | 112 |
| 109 Args: | 113 Args: |
| 110 tombstone_data: a list of strings of tombstone data. | 114 tombstone_data: a list of strings of tombstone data. |
| 111 include_stack: boolean whether to include stack data in output. | 115 include_stack: boolean whether to include stack data in output. |
| 112 device_abi: the default ABI of the device which generated the tombstone. | 116 device_abi: the default ABI of the device which generated the tombstone. |
| 113 | 117 |
| 114 Yields: | 118 Yields: |
| 115 A string for each line of resolved stack output. | 119 A string for each line of resolved stack output. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 return [] | 166 return [] |
| 163 if len(tombstones) == 1: | 167 if len(tombstones) == 1: |
| 164 data = [_ResolveTombstone(tombstones[0])] | 168 data = [_ResolveTombstone(tombstones[0])] |
| 165 else: | 169 else: |
| 166 pool = multiprocessing.Pool(processes=jobs) | 170 pool = multiprocessing.Pool(processes=jobs) |
| 167 data = pool.map(_ResolveTombstone, tombstones) | 171 data = pool.map(_ResolveTombstone, tombstones) |
| 168 resolved_tombstones = [] | 172 resolved_tombstones = [] |
| 169 for tombstone in data: | 173 for tombstone in data: |
| 170 resolved_tombstones.extend(tombstone) | 174 resolved_tombstones.extend(tombstone) |
| 171 return resolved_tombstones | 175 return resolved_tombstones |
| 172 | 176 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 173 def _GetTombstonesForDevice(device, resolve_all_tombstones, | 177 def _GetTombstonesForDevice(device, resolve_all_tombstones, |
| 174 include_stack_symbols, | 178 include_stack_symbols, |
| 175 wipe_tombstones): | 179 wipe_tombstones): |
| 176 """Returns a list of tombstones on a given device. | 180 """Returns a list of tombstones on a given device. |
| 177 | 181 |
| 178 Args: | 182 Args: |
| 179 device: An instance of DeviceUtils. | 183 device: An instance of DeviceUtils. |
| 180 resolve_all_tombstone: Whether to resolve every tombstone. | 184 resolve_all_tombstone: Whether to resolve every tombstone. |
| 181 include_stack_symbols: Whether to include symbols for stack data. | 185 include_stack_symbols: Whether to include symbols for stack data. |
| 182 wipe_tombstones: Whether to wipe tombstones. | 186 wipe_tombstones: Whether to wipe tombstones. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 208 '/data/tombstones', as_root=True, timeout=60): | 212 '/data/tombstones', as_root=True, timeout=60): |
| 209 logging.info('%s: %s', str(device), entry) | 213 logging.info('%s: %s', str(device), entry) |
| 210 raise | 214 raise |
| 211 | 215 |
| 212 # Erase all the tombstones if desired. | 216 # Erase all the tombstones if desired. |
| 213 if wipe_tombstones: | 217 if wipe_tombstones: |
| 214 for tombstone_file, _ in all_tombstones: | 218 for tombstone_file, _ in all_tombstones: |
| 215 _EraseTombstone(device, tombstone_file) | 219 _EraseTombstone(device, tombstone_file) |
| 216 | 220 |
| 217 return ret | 221 return ret |
| 218 | 222 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 219 def ClearAllTombstones(device): | 223 def ClearAllTombstones(device): |
| 220 """Clear all tombstones in the device. | 224 """Clear all tombstones in the device. |
| 221 | 225 |
| 222 Args: | 226 Args: |
| 223 device: An instance of DeviceUtils. | 227 device: An instance of DeviceUtils. |
| 224 """ | 228 """ |
| 225 all_tombstones = list(_ListTombstones(device)) | 229 all_tombstones = list(_ListTombstones(device)) |
| 226 if not all_tombstones: | 230 if not all_tombstones: |
| 227 logging.warning('No tombstones to clear.') | 231 logging.warning('No tombstones to clear.') |
| 228 | 232 |
| 229 for tombstone_file, _ in all_tombstones: | 233 for tombstone_file, _ in all_tombstones: |
| 230 _EraseTombstone(device, tombstone_file) | 234 _EraseTombstone(device, tombstone_file) |
| 231 | 235 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 232 def ResolveTombstones(device, resolve_all_tombstones, include_stack_symbols, | 236 def ResolveTombstones(device, resolve_all_tombstones, include_stack_symbols, |
| 233 wipe_tombstones, jobs=4): | 237 wipe_tombstones, jobs=4): |
| 234 """Resolve tombstones in the device. | 238 """Resolve tombstones in the device. |
| 235 | 239 |
| 236 Args: | 240 Args: |
| 237 device: An instance of DeviceUtils. | 241 device: An instance of DeviceUtils. |
| 238 resolve_all_tombstone: Whether to resolve every tombstone. | 242 resolve_all_tombstone: Whether to resolve every tombstone. |
| 239 include_stack_symbols: Whether to include symbols for stack data. | 243 include_stack_symbols: Whether to include symbols for stack data. |
| 240 wipe_tombstones: Whether to wipe tombstones. | 244 wipe_tombstones: Whether to wipe tombstones. |
| 241 jobs: Number of jobs to use when processing multiple crash stacks. | 245 jobs: Number of jobs to use when processing multiple crash stacks. |
| 242 """ | 246 """ |
| 243 return _ResolveTombstones(jobs, | 247 return _ResolveTombstones(jobs, |
| 244 _GetTombstonesForDevice(device, | 248 _GetTombstonesForDevice(device, |
| 245 resolve_all_tombstones, | 249 resolve_all_tombstones, |
| 246 include_stack_symbols, | 250 include_stack_symbols, |
| 247 wipe_tombstones)) | 251 wipe_tombstones)) |
| 248 | 252 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
| |
| 253 def LogdogTombstones(resolved_tombstones, stream_name): | |
| 254 """Save resolved tombstones to logdog and return the url. | |
| 255 | |
| 256 Args: | |
| 257 stream_name: The name of the logdog stream that records tombstones. | |
| 258 resolved_tombstones: Resolved tombstones (output of ResolveTombstones). | |
| 259 """ | |
|
jbudorick
2016/12/14 17:17:19
nit: Add a Returns: doc here.
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 260 try: | |
| 261 tombstones_url = '' | |
| 262 stream_client = bootstrap.ButlerBootstrap.probe().stream_client() | |
| 263 logdog_stream = stream_client.open_text(stream_name) | |
|
jbudorick
2016/12/14 17:17:19
Do this with a context manager:
with stream_cli
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 264 for tombstones_line in resolved_tombstones: | |
| 265 logdog_stream.write(tombstones_line + '\n') | |
| 266 tombstones_url = logdog_stream.get_viewer_url(stream_name) | |
| 267 logdog_stream.close() | |
| 268 except bootstrap.NotBootstrappedError: | |
| 269 logging.exception('Error not bootstrapped. Failed to start logdog') | |
| 270 except (KeyError, ValueError) as e: | |
| 271 logging.exception('Error when creating stream_client/stream: %s.', e) | |
| 272 except Exception as e: # pylint: disable=broad-except | |
| 273 logging.exception('Unknown Error: %s.', e) | |
| 274 return tombstones_url | |
| 275 | |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 249 def main(): | 276 def main(): |
| 250 custom_handler = logging.StreamHandler(sys.stdout) | 277 custom_handler = logging.StreamHandler(sys.stdout) |
| 251 custom_handler.setFormatter(run_tests_helper.CustomFormatter()) | 278 custom_handler.setFormatter(run_tests_helper.CustomFormatter()) |
| 252 logging.getLogger().addHandler(custom_handler) | 279 logging.getLogger().addHandler(custom_handler) |
| 253 logging.getLogger().setLevel(logging.INFO) | 280 logging.getLogger().setLevel(logging.INFO) |
| 254 | 281 |
| 255 parser = argparse.ArgumentParser() | 282 parser = argparse.ArgumentParser() |
| 256 parser.add_argument('--device', | 283 parser.add_argument('--device', |
| 257 help='The serial number of the device. If not specified ' | 284 help='The serial number of the device. If not specified ' |
| 258 'will use all devices.') | 285 'will use all devices.') |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 | 319 |
| 293 # This must be done serially because strptime can hit a race condition if | 320 # This must be done serially because strptime can hit a race condition if |
| 294 # used for the first time in a multithreaded environment. | 321 # used for the first time in a multithreaded environment. |
| 295 # http://bugs.python.org/issue7980 | 322 # http://bugs.python.org/issue7980 |
| 296 for device in devices: | 323 for device in devices: |
| 297 resolved_tombstones = ResolveTombstones( | 324 resolved_tombstones = ResolveTombstones( |
| 298 device, args.all_tombstones, | 325 device, args.all_tombstones, |
| 299 args.stack, args.wipe_tombstones, args.jobs) | 326 args.stack, args.wipe_tombstones, args.jobs) |
| 300 for line in resolved_tombstones: | 327 for line in resolved_tombstones: |
| 301 logging.info(line) | 328 logging.info(line) |
| 302 | 329 |
|
jbudorick
2016/12/14 17:17:19
nit: +1 blank line
BigBossZhiling
2016/12/14 17:41:27
Done.
| |
| 303 if __name__ == '__main__': | 330 if __name__ == '__main__': |
| 304 sys.exit(main()) | 331 sys.exit(main()) |
| OLD | NEW |