tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tombstones.py (9164B)


      1 #!/usr/bin/env vpython3
      2 #
      3 # Copyright 2013 The Chromium Authors
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 #
      7 # Find the most recent tombstone file(s) on all connected devices
      8 # and prints their stacks.
      9 #
     10 # Assumes tombstone file was created with current symbols.
     11 
     12 import argparse
     13 import datetime
     14 import logging
     15 import os
     16 import sys
     17 
     18 from multiprocessing.pool import ThreadPool
     19 
     20 import devil_chromium
     21 
     22 from devil.android import device_denylist
     23 from devil.android import device_errors
     24 from devil.android import device_utils
     25 from devil.utils import run_tests_helper
     26 from pylib import constants
     27 from pylib.symbols import stack_symbolizer
     28 
     29 
     30 _TZ_UTC = {'TZ': 'UTC'}
     31 
     32 
     33 def _ListTombstones(device):
     34  """List the tombstone files on the device.
     35 
     36  Args:
     37    device: An instance of DeviceUtils.
     38 
     39  Yields:
     40    Tuples of (tombstone filename, date time of file on device).
     41  """
     42  try:
     43    if not device.PathExists('/data/tombstones', as_root=True):
     44      return
     45    entries = device.StatDirectory('/data/tombstones', as_root=True)
     46    for entry in entries:
     47      if 'tombstone' in entry['filename']:
     48        yield (entry['filename'],
     49               datetime.datetime.fromtimestamp(entry['st_mtime']))
     50  except device_errors.CommandFailedError:
     51    logging.exception('Could not retrieve tombstones.')
     52  except device_errors.DeviceUnreachableError:
     53    logging.exception('Device unreachable retrieving tombstones.')
     54  except device_errors.CommandTimeoutError:
     55    logging.exception('Timed out retrieving tombstones.')
     56 
     57 
     58 def _GetDeviceDateTime(device):
     59  """Determine the date time on the device.
     60 
     61  Args:
     62    device: An instance of DeviceUtils.
     63 
     64  Returns:
     65    A datetime instance.
     66  """
     67  device_now_string = device.RunShellCommand(
     68      ['date'], check_return=True, env=_TZ_UTC)
     69  return datetime.datetime.strptime(
     70      device_now_string[0], '%a %b %d %H:%M:%S %Z %Y')
     71 
     72 
     73 def _GetTombstoneData(device, tombstone_file):
     74  """Retrieve the tombstone data from the device
     75 
     76  Args:
     77    device: An instance of DeviceUtils.
     78    tombstone_file: the tombstone to retrieve
     79 
     80  Returns:
     81    A list of lines
     82  """
     83  return device.ReadFile(
     84      '/data/tombstones/' + tombstone_file, as_root=True).splitlines()
     85 
     86 
     87 def _EraseTombstone(device, tombstone_file):
     88  """Deletes a tombstone from the device.
     89 
     90  Args:
     91    device: An instance of DeviceUtils.
     92    tombstone_file: the tombstone to delete.
     93  """
     94  return device.RunShellCommand(
     95      ['rm', '/data/tombstones/' + tombstone_file],
     96      as_root=True, check_return=True)
     97 
     98 
     99 def _ResolveTombstone(args):
    100  tombstone = args[0]
    101  tombstone_symbolizer = args[1]
    102  lines = []
    103  lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
    104            ', about this long ago: ' +
    105            (str(tombstone['device_now'] - tombstone['time']) +
    106            ' Device: ' + tombstone['serial'])]
    107  logging.info('\n'.join(lines))
    108  logging.info('Resolving...')
    109  lines += tombstone_symbolizer.ExtractAndResolveNativeStackTraces(
    110      tombstone['data'],
    111      tombstone['device_abi'],
    112      tombstone['stack'])
    113  return lines
    114 
    115 
    116 def _ResolveTombstones(jobs, tombstones, tombstone_symbolizer):
    117  """Resolve a list of tombstones.
    118 
    119  Args:
    120    jobs: the number of jobs to use with multithread.
    121    tombstones: a list of tombstones.
    122  """
    123  if not tombstones:
    124    logging.warning('No tombstones to resolve.')
    125    return []
    126  if len(tombstones) == 1:
    127    data = [_ResolveTombstone([tombstones[0], tombstone_symbolizer])]
    128  else:
    129    pool = ThreadPool(jobs)
    130    data = pool.map(
    131        _ResolveTombstone,
    132        [[tombstone, tombstone_symbolizer] for tombstone in tombstones])
    133    pool.close()
    134    pool.join()
    135  resolved_tombstones = []
    136  for tombstone in data:
    137    resolved_tombstones.extend(tombstone)
    138  return resolved_tombstones
    139 
    140 
    141 def _GetTombstonesForDevice(device, resolve_all_tombstones,
    142                            include_stack_symbols,
    143                            wipe_tombstones):
    144  """Returns a list of tombstones on a given device.
    145 
    146  Args:
    147    device: An instance of DeviceUtils.
    148    resolve_all_tombstone: Whether to resolve every tombstone.
    149    include_stack_symbols: Whether to include symbols for stack data.
    150    wipe_tombstones: Whether to wipe tombstones.
    151  """
    152  ret = []
    153  all_tombstones = list(_ListTombstones(device))
    154  if not all_tombstones:
    155    logging.warning('No tombstones.')
    156    return ret
    157 
    158  # Sort the tombstones in date order, descending
    159  all_tombstones.sort(key=lambda a: a[1], reverse=True)
    160 
    161  # Only resolve the most recent unless --all-tombstones given.
    162  tombstones = all_tombstones if resolve_all_tombstones else [all_tombstones[0]]
    163 
    164  device_now = _GetDeviceDateTime(device)
    165  try:
    166    for tombstone_file, tombstone_time in tombstones:
    167      ret += [{'serial': str(device),
    168               'device_abi': device.product_cpu_abi,
    169               'device_now': device_now,
    170               'time': tombstone_time,
    171               'file': tombstone_file,
    172               'stack': include_stack_symbols,
    173               'data': _GetTombstoneData(device, tombstone_file)}]
    174  except device_errors.CommandFailedError:
    175    for entry in device.StatDirectory(
    176        '/data/tombstones', as_root=True, timeout=60):
    177      logging.info('%s: %s', str(device), entry)
    178    raise
    179 
    180  # Erase all the tombstones if desired.
    181  if wipe_tombstones:
    182    for tombstone_file, _ in all_tombstones:
    183      _EraseTombstone(device, tombstone_file)
    184 
    185  return ret
    186 
    187 
    188 def ClearAllTombstones(device):
    189  """Clear all tombstones in the device.
    190 
    191  Args:
    192    device: An instance of DeviceUtils.
    193  """
    194  all_tombstones = list(_ListTombstones(device))
    195  if not all_tombstones:
    196    logging.warning('No tombstones to clear.')
    197 
    198  for tombstone_file, _ in all_tombstones:
    199    _EraseTombstone(device, tombstone_file)
    200 
    201 
    202 def ResolveTombstones(device, resolve_all_tombstones, include_stack_symbols,
    203                      wipe_tombstones, jobs=4, apk_under_test=None,
    204                      tombstone_symbolizer=None):
    205  """Resolve tombstones in the device.
    206 
    207  Args:
    208    device: An instance of DeviceUtils.
    209    resolve_all_tombstone: Whether to resolve every tombstone.
    210    include_stack_symbols: Whether to include symbols for stack data.
    211    wipe_tombstones: Whether to wipe tombstones.
    212    jobs: Number of jobs to use when processing multiple crash stacks.
    213 
    214  Returns:
    215    A list of resolved tombstones.
    216  """
    217  return _ResolveTombstones(jobs,
    218                            _GetTombstonesForDevice(device,
    219                                                    resolve_all_tombstones,
    220                                                    include_stack_symbols,
    221                                                    wipe_tombstones),
    222                            (tombstone_symbolizer
    223                             or stack_symbolizer.Symbolizer(apk_under_test)))
    224 
    225 
    226 def main():
    227  custom_handler = logging.StreamHandler(sys.stdout)
    228  custom_handler.setFormatter(run_tests_helper.CustomFormatter())
    229  logging.getLogger().addHandler(custom_handler)
    230  logging.getLogger().setLevel(logging.INFO)
    231 
    232  parser = argparse.ArgumentParser()
    233  parser.add_argument('--device',
    234                      help='The serial number of the device. If not specified '
    235                           'will use all devices.')
    236  parser.add_argument('--denylist-file', help='Device denylist JSON file.')
    237  parser.add_argument('-a', '--all-tombstones', action='store_true',
    238                      help='Resolve symbols for all tombstones, rather than '
    239                           'just the most recent.')
    240  parser.add_argument('-s', '--stack', action='store_true',
    241                      help='Also include symbols for stack data')
    242  parser.add_argument('-w', '--wipe-tombstones', action='store_true',
    243                      help='Erase all tombstones from device after processing')
    244  parser.add_argument('-j', '--jobs', type=int,
    245                      default=4,
    246                      help='Number of jobs to use when processing multiple '
    247                           'crash stacks.')
    248  parser.add_argument('--output-directory',
    249                      help='Path to the root build directory.')
    250  parser.add_argument('--adb-path', type=os.path.abspath,
    251                      help='Path to the adb binary.')
    252  args = parser.parse_args()
    253 
    254  if args.output_directory:
    255    constants.SetOutputDirectory(args.output_directory)
    256 
    257  devil_chromium.Initialize(output_directory=constants.GetOutDirectory(),
    258                            adb_path=args.adb_path)
    259 
    260  denylist = (device_denylist.Denylist(args.denylist_file)
    261              if args.denylist_file else None)
    262 
    263  if args.device:
    264    devices = [device_utils.DeviceUtils(args.device)]
    265  else:
    266    devices = device_utils.DeviceUtils.HealthyDevices(denylist)
    267 
    268  # This must be done serially because strptime can hit a race condition if
    269  # used for the first time in a multithreaded environment.
    270  # http://bugs.python.org/issue7980
    271  for device in devices:
    272    resolved_tombstones = ResolveTombstones(
    273        device, args.all_tombstones,
    274        args.stack, args.wipe_tombstones, args.jobs)
    275    for line in resolved_tombstones:
    276      logging.info(line)
    277 
    278 
    279 if __name__ == '__main__':
    280  sys.exit(main())