17dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#!/usr/bin/env python
27dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#
37dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Copyright 2013 The Chromium Authors. All rights reserved.
47dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Use of this source code is governed by a BSD-style license that can be
57dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# found in the LICENSE file.
67dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#
77dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Find the most recent tombstone file(s) on all connected devices
87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# and prints their stacks.
97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Assumes tombstone file was created with current symbols.
117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport datetime
137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport logging
147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport multiprocessing
157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport os
167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport subprocess
177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport sys
187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport optparse
197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom pylib import android_commands
217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _ListTombstones(adb):
247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """List the tombstone files on the device.
257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    adb: An instance of AndroidCommands.
287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Yields:
307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    Tuples of (tombstone filename, date time of file on device).
317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  lines = adb.RunShellCommand('TZ=UTC su -c ls -a -l /data/tombstones')
337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  for line in lines:
347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if 'tombstone' in line and not 'No such file or directory' in line:
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      details = line.split()
367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      t = datetime.datetime.strptime(details[-3] + ' ' + details[-2],
377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                     '%Y-%m-%d %H:%M')
387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      yield details[-1], t
397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _GetDeviceDateTime(adb):
427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Determine the date time on the device.
437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    adb: An instance of AndroidCommands.
467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Returns:
487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    A datetime instance.
497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  device_now_string = adb.RunShellCommand('TZ=UTC date')
517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return datetime.datetime.strptime(
527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      device_now_string[0], '%a %b %d %H:%M:%S %Z %Y')
537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _GetTombstoneData(adb, tombstone_file):
567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Retrieve the tombstone data from the device
577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    tombstone_file: the tombstone to retrieve
607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Returns:
627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    A list of lines
637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return adb.GetProtectedFileContents('/data/tombstones/' + tombstone_file)
657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _EraseTombstone(adb, tombstone_file):
687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Deletes a tombstone from the device.
697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    tombstone_file: the tombstone to delete.
727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return adb.RunShellCommand('su -c rm /data/tombstones/' + tombstone_file)
747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _ResolveSymbols(tombstone_data, include_stack):
777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Run the stack tool for given tombstone input.
787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    tombstone_data: a list of strings of tombstone data.
817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    include_stack: boolean whether to include stack data in output.
827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Yields:
847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    A string for each line of resolved stack output.
857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                            'third_party', 'android_platform', 'development',
887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                            'scripts', 'stack')
897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  proc = subprocess.Popen(stack_tool, stdin=subprocess.PIPE,
907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                          stdout=subprocess.PIPE)
917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  output = proc.communicate(input='\n'.join(tombstone_data))[0]
927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  for line in output.split('\n'):
937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if not include_stack and 'Stack Data:' in line:
947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      break
957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    yield line
967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _ResolveTombstone(tombstone):
997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  lines = []
1007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
1017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            ', about this long ago: ' +
1027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            (str(tombstone['device_now'] - tombstone['time']) +
1037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            ' Device: ' + tombstone['serial'])]
1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  print '\n'.join(lines)
1057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  print 'Resolving...'
1067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  lines += _ResolveSymbols(tombstone['data'], tombstone['stack'])
1077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return lines
1087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _ResolveTombstones(jobs, tombstones):
1117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Resolve a list of tombstones.
1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
1147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    jobs: the number of jobs to use with multiprocess.
1157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    tombstones: a list of tombstones.
1167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
1177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if not tombstones:
1187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    print 'No device attached?  Or no tombstones?'
1197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return
1207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if len(tombstones) == 1:
1217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    data = _ResolveTombstone(tombstones[0])
1227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  else:
1237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    pool = multiprocessing.Pool(processes=jobs)
1247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    data = pool.map(_ResolveTombstone, tombstones)
1257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    data = ['\n'.join(d) for d in data]
1267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  print '\n'.join(data)
1277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef _GetTombstonesForDevice(adb, options):
1307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """Returns a list of tombstones on a given adb connection.
1317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  Args:
1337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    adb: An instance of Androidcommands.
1347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    options: command line arguments from OptParse
1357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  """
1367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  ret = []
1377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  all_tombstones = list(_ListTombstones(adb))
1387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if not all_tombstones:
1397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    print 'No device attached?  Or no tombstones?'
1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return ret
1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  # Sort the tombstones in date order, descending
1437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  all_tombstones.sort(cmp=lambda a, b: cmp(b[1], a[1]))
1447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  # Only resolve the most recent unless --all-tombstones given.
1467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  tombstones = all_tombstones if options.all_tombstones else [all_tombstones[0]]
1477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  device_now = _GetDeviceDateTime(adb)
1497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  for tombstone_file, tombstone_time in tombstones:
1507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    ret += [{'serial': adb.Adb().GetSerialNumber(),
1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch             'device_now': device_now,
1527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch             'time': tombstone_time,
1537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch             'file': tombstone_file,
1547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch             'stack': options.stack,
1557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch             'data': _GetTombstoneData(adb, tombstone_file)}]
1567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  # Erase all the tombstones if desired.
1587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if options.wipe_tombstones:
1597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    for tombstone_file, _ in all_tombstones:
1607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      _EraseTombstone(adb, tombstone_file)
1617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return ret
1637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochdef main():
1657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser = optparse.OptionParser()
1667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser.add_option('--device',
1677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    help='The serial number of the device. If not specified '
1687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                         'will use all devices.')
1697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser.add_option('-a', '--all-tombstones', action='store_true',
1707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    help="""Resolve symbols for all tombstones, rather than just
1717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                         the most recent""")
1727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser.add_option('-s', '--stack', action='store_true',
1737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    help='Also include symbols for stack data')
1747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser.add_option('-w', '--wipe-tombstones', action='store_true',
1757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    help='Erase all tombstones from device after processing')
1767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  parser.add_option('-j', '--jobs', type='int',
1777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    default=4,
1787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                    help='Number of jobs to use when processing multiple '
1797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                         'crash stacks.')
1807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  options, args = parser.parse_args()
1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if options.device:
1837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    devices = [options.device]
1847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  else:
1857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    devices = android_commands.GetAttachedDevices()
1867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  tombstones = []
1887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  for device in devices:
1897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    adb = android_commands.AndroidCommands(device)
1907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    tombstones += _GetTombstonesForDevice(adb, options)
1917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  _ResolveTombstones(options.jobs, tombstones)
1937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochif __name__ == '__main__':
1957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  sys.exit(main())
196