123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #!/usr/bin/env vpython3
- #
- # Copyright 2013 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- #
- # Find the most recent tombstone file(s) on all connected devices
- # and prints their stacks.
- #
- # Assumes tombstone file was created with current symbols.
- import argparse
- import datetime
- import logging
- import os
- import sys
- from multiprocessing.pool import ThreadPool
- import devil_chromium
- from devil.android import device_denylist
- from devil.android import device_errors
- from devil.android import device_utils
- from devil.utils import run_tests_helper
- from pylib import constants
- from pylib.symbols import stack_symbolizer
- _TZ_UTC = {'TZ': 'UTC'}
- def _ListTombstones(device):
- """List the tombstone files on the device.
- Args:
- device: An instance of DeviceUtils.
- Yields:
- Tuples of (tombstone filename, date time of file on device).
- """
- try:
- if not device.PathExists('/data/tombstones', as_root=True):
- return
- entries = device.StatDirectory('/data/tombstones', as_root=True)
- for entry in entries:
- if 'tombstone' in entry['filename']:
- yield (entry['filename'],
- datetime.datetime.fromtimestamp(entry['st_mtime']))
- except device_errors.CommandFailedError:
- logging.exception('Could not retrieve tombstones.')
- except device_errors.DeviceUnreachableError:
- logging.exception('Device unreachable retrieving tombstones.')
- except device_errors.CommandTimeoutError:
- logging.exception('Timed out retrieving tombstones.')
- def _GetDeviceDateTime(device):
- """Determine the date time on the device.
- Args:
- device: An instance of DeviceUtils.
- Returns:
- A datetime instance.
- """
- device_now_string = device.RunShellCommand(
- ['date'], check_return=True, env=_TZ_UTC)
- return datetime.datetime.strptime(
- device_now_string[0], '%a %b %d %H:%M:%S %Z %Y')
- def _GetTombstoneData(device, tombstone_file):
- """Retrieve the tombstone data from the device
- Args:
- device: An instance of DeviceUtils.
- tombstone_file: the tombstone to retrieve
- Returns:
- A list of lines
- """
- return device.ReadFile(
- '/data/tombstones/' + tombstone_file, as_root=True).splitlines()
- def _EraseTombstone(device, tombstone_file):
- """Deletes a tombstone from the device.
- Args:
- device: An instance of DeviceUtils.
- tombstone_file: the tombstone to delete.
- """
- return device.RunShellCommand(
- ['rm', '/data/tombstones/' + tombstone_file],
- as_root=True, check_return=True)
- def _ResolveTombstone(args):
- tombstone = args[0]
- tombstone_symbolizer = args[1]
- lines = []
- lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
- ', about this long ago: ' +
- (str(tombstone['device_now'] - tombstone['time']) +
- ' Device: ' + tombstone['serial'])]
- logging.info('\n'.join(lines))
- logging.info('Resolving...')
- lines += tombstone_symbolizer.ExtractAndResolveNativeStackTraces(
- tombstone['data'],
- tombstone['device_abi'],
- tombstone['stack'])
- return lines
- def _ResolveTombstones(jobs, tombstones, tombstone_symbolizer):
- """Resolve a list of tombstones.
- Args:
- jobs: the number of jobs to use with multithread.
- tombstones: a list of tombstones.
- """
- if not tombstones:
- logging.warning('No tombstones to resolve.')
- return []
- if len(tombstones) == 1:
- data = [_ResolveTombstone([tombstones[0], tombstone_symbolizer])]
- else:
- pool = ThreadPool(jobs)
- data = pool.map(
- _ResolveTombstone,
- [[tombstone, tombstone_symbolizer] for tombstone in tombstones])
- pool.close()
- pool.join()
- resolved_tombstones = []
- for tombstone in data:
- resolved_tombstones.extend(tombstone)
- return resolved_tombstones
- def _GetTombstonesForDevice(device, resolve_all_tombstones,
- include_stack_symbols,
- wipe_tombstones):
- """Returns a list of tombstones on a given device.
- Args:
- device: An instance of DeviceUtils.
- resolve_all_tombstone: Whether to resolve every tombstone.
- include_stack_symbols: Whether to include symbols for stack data.
- wipe_tombstones: Whether to wipe tombstones.
- """
- ret = []
- all_tombstones = list(_ListTombstones(device))
- if not all_tombstones:
- logging.warning('No tombstones.')
- return ret
- # Sort the tombstones in date order, descending
- all_tombstones.sort(key=lambda a: a[1], reverse=True)
- # Only resolve the most recent unless --all-tombstones given.
- tombstones = all_tombstones if resolve_all_tombstones else [all_tombstones[0]]
- device_now = _GetDeviceDateTime(device)
- try:
- for tombstone_file, tombstone_time in tombstones:
- ret += [{'serial': str(device),
- 'device_abi': device.product_cpu_abi,
- 'device_now': device_now,
- 'time': tombstone_time,
- 'file': tombstone_file,
- 'stack': include_stack_symbols,
- 'data': _GetTombstoneData(device, tombstone_file)}]
- except device_errors.CommandFailedError:
- for entry in device.StatDirectory(
- '/data/tombstones', as_root=True, timeout=60):
- logging.info('%s: %s', str(device), entry)
- raise
- # Erase all the tombstones if desired.
- if wipe_tombstones:
- for tombstone_file, _ in all_tombstones:
- _EraseTombstone(device, tombstone_file)
- return ret
- def ClearAllTombstones(device):
- """Clear all tombstones in the device.
- Args:
- device: An instance of DeviceUtils.
- """
- all_tombstones = list(_ListTombstones(device))
- if not all_tombstones:
- logging.warning('No tombstones to clear.')
- for tombstone_file, _ in all_tombstones:
- _EraseTombstone(device, tombstone_file)
- def ResolveTombstones(device, resolve_all_tombstones, include_stack_symbols,
- wipe_tombstones, jobs=4, apk_under_test=None,
- tombstone_symbolizer=None):
- """Resolve tombstones in the device.
- Args:
- device: An instance of DeviceUtils.
- resolve_all_tombstone: Whether to resolve every tombstone.
- include_stack_symbols: Whether to include symbols for stack data.
- wipe_tombstones: Whether to wipe tombstones.
- jobs: Number of jobs to use when processing multiple crash stacks.
- Returns:
- A list of resolved tombstones.
- """
- return _ResolveTombstones(jobs,
- _GetTombstonesForDevice(device,
- resolve_all_tombstones,
- include_stack_symbols,
- wipe_tombstones),
- (tombstone_symbolizer
- or stack_symbolizer.Symbolizer(apk_under_test)))
- def main():
- custom_handler = logging.StreamHandler(sys.stdout)
- custom_handler.setFormatter(run_tests_helper.CustomFormatter())
- logging.getLogger().addHandler(custom_handler)
- logging.getLogger().setLevel(logging.INFO)
- parser = argparse.ArgumentParser()
- parser.add_argument('--device',
- help='The serial number of the device. If not specified '
- 'will use all devices.')
- parser.add_argument('--denylist-file', help='Device denylist JSON file.')
- parser.add_argument('-a', '--all-tombstones', action='store_true',
- help='Resolve symbols for all tombstones, rather than '
- 'just the most recent.')
- parser.add_argument('-s', '--stack', action='store_true',
- help='Also include symbols for stack data')
- parser.add_argument('-w', '--wipe-tombstones', action='store_true',
- help='Erase all tombstones from device after processing')
- parser.add_argument('-j', '--jobs', type=int,
- default=4,
- help='Number of jobs to use when processing multiple '
- 'crash stacks.')
- parser.add_argument('--output-directory',
- help='Path to the root build directory.')
- parser.add_argument('--adb-path', type=os.path.abspath,
- help='Path to the adb binary.')
- args = parser.parse_args()
- if args.output_directory:
- constants.SetOutputDirectory(args.output_directory)
- devil_chromium.Initialize(output_directory=constants.GetOutDirectory(),
- adb_path=args.adb_path)
- denylist = (device_denylist.Denylist(args.denylist_file)
- if args.denylist_file else None)
- if args.device:
- devices = [device_utils.DeviceUtils(args.device)]
- else:
- devices = device_utils.DeviceUtils.HealthyDevices(denylist)
- # This must be done serially because strptime can hit a race condition if
- # used for the first time in a multithreaded environment.
- # http://bugs.python.org/issue7980
- for device in devices:
- resolved_tombstones = ResolveTombstones(
- device, args.all_tombstones,
- args.stack, args.wipe_tombstones, args.jobs)
- for line in resolved_tombstones:
- logging.info(line)
- if __name__ == '__main__':
- sys.exit(main())
|