blob: dbfe3f76c2887270eeeb90fd02ea31fead5b574c [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001#!/usr/bin/env python
2#
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
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
12import datetime
Alhaad Gokhale4f513072015-03-24 10:49:34 -070013import itertools
14import logging
James Robinson646469d2014-10-03 15:33:28 -070015import multiprocessing
16import os
17import re
18import subprocess
19import sys
20import optparse
21
James Robinson0fae0002015-05-05 16:31:51 -070022from pylib.device import adb_wrapper
Dave Moore0ae79f42015-03-17 12:56:46 -070023from pylib.device import device_errors
James Robinson646469d2014-10-03 15:33:28 -070024from pylib.device import device_utils
Alhaad Gokhale4f513072015-03-24 10:49:34 -070025from pylib.utils import run_tests_helper
James Robinson646469d2014-10-03 15:33:28 -070026
27
Dave Moore0ae79f42015-03-17 12:56:46 -070028_TZ_UTC = {'TZ': 'UTC'}
29
James Robinson646469d2014-10-03 15:33:28 -070030def _ListTombstones(device):
31 """List the tombstone files on the device.
32
33 Args:
34 device: An instance of DeviceUtils.
35
36 Yields:
37 Tuples of (tombstone filename, date time of file on device).
38 """
Alhaad Gokhale4f513072015-03-24 10:49:34 -070039 try:
40 lines = device.RunShellCommand(
41 ['ls', '-a', '-l', '/data/tombstones'],
42 as_root=True, check_return=True, env=_TZ_UTC, timeout=60)
43 for line in lines:
44 if 'tombstone' in line and not 'No such file or directory' in line:
45 details = line.split()
46 t = datetime.datetime.strptime(details[-3] + ' ' + details[-2],
47 '%Y-%m-%d %H:%M')
48 yield details[-1], t
49 except device_errors.CommandFailedError:
50 logging.exception('Could not retrieve tombstones.')
James Robinson646469d2014-10-03 15:33:28 -070051
52
53def _GetDeviceDateTime(device):
54 """Determine the date time on the device.
55
56 Args:
57 device: An instance of DeviceUtils.
58
59 Returns:
60 A datetime instance.
61 """
Dave Moore0ae79f42015-03-17 12:56:46 -070062 device_now_string = device.RunShellCommand(
63 ['date'], check_return=True, env=_TZ_UTC)
James Robinson646469d2014-10-03 15:33:28 -070064 return datetime.datetime.strptime(
65 device_now_string[0], '%a %b %d %H:%M:%S %Z %Y')
66
67
68def _GetTombstoneData(device, tombstone_file):
69 """Retrieve the tombstone data from the device
70
71 Args:
72 device: An instance of DeviceUtils.
73 tombstone_file: the tombstone to retrieve
74
75 Returns:
76 A list of lines
77 """
Elliot Glayshereae49292015-01-28 10:47:32 -080078 return device.ReadFile(
79 '/data/tombstones/' + tombstone_file, as_root=True).splitlines()
James Robinson646469d2014-10-03 15:33:28 -070080
81
82def _EraseTombstone(device, tombstone_file):
83 """Deletes a tombstone from the device.
84
85 Args:
86 device: An instance of DeviceUtils.
87 tombstone_file: the tombstone to delete.
88 """
89 return device.RunShellCommand(
Dave Moore0ae79f42015-03-17 12:56:46 -070090 ['rm', '/data/tombstones/' + tombstone_file],
91 as_root=True, check_return=True)
James Robinson646469d2014-10-03 15:33:28 -070092
93
94def _DeviceAbiToArch(device_abi):
95 # The order of this list is significant to find the more specific match (e.g.,
96 # arm64) before the less specific (e.g., arm).
97 arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips']
98 for arch in arches:
99 if arch in device_abi:
100 return arch
101 raise RuntimeError('Unknown device ABI: %s' % device_abi)
102
103def _ResolveSymbols(tombstone_data, include_stack, device_abi):
104 """Run the stack tool for given tombstone input.
105
106 Args:
107 tombstone_data: a list of strings of tombstone data.
108 include_stack: boolean whether to include stack data in output.
109 device_abi: the default ABI of the device which generated the tombstone.
110
111 Yields:
112 A string for each line of resolved stack output.
113 """
114 # Check if the tombstone data has an ABI listed, if so use this in preference
115 # to the device's default ABI.
116 for line in tombstone_data:
117 found_abi = re.search('ABI: \'(.+?)\'', line)
118 if found_abi:
119 device_abi = found_abi.group(1)
120 arch = _DeviceAbiToArch(device_abi)
121 if not arch:
122 return
123
124 stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
125 'third_party', 'android_platform', 'development',
126 'scripts', 'stack')
127 proc = subprocess.Popen([stack_tool, '--arch', arch], stdin=subprocess.PIPE,
128 stdout=subprocess.PIPE)
129 output = proc.communicate(input='\n'.join(tombstone_data))[0]
130 for line in output.split('\n'):
131 if not include_stack and 'Stack Data:' in line:
132 break
133 yield line
134
135
136def _ResolveTombstone(tombstone):
137 lines = []
138 lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
139 ', about this long ago: ' +
140 (str(tombstone['device_now'] - tombstone['time']) +
141 ' Device: ' + tombstone['serial'])]
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700142 logging.info('\n'.join(lines))
143 logging.info('Resolving...')
James Robinson646469d2014-10-03 15:33:28 -0700144 lines += _ResolveSymbols(tombstone['data'], tombstone['stack'],
145 tombstone['device_abi'])
146 return lines
147
148
149def _ResolveTombstones(jobs, tombstones):
150 """Resolve a list of tombstones.
151
152 Args:
153 jobs: the number of jobs to use with multiprocess.
154 tombstones: a list of tombstones.
155 """
156 if not tombstones:
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700157 logging.warning('No tombstones to resolve.')
James Robinson646469d2014-10-03 15:33:28 -0700158 return
159 if len(tombstones) == 1:
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700160 data = [_ResolveTombstone(tombstones[0])]
James Robinson646469d2014-10-03 15:33:28 -0700161 else:
162 pool = multiprocessing.Pool(processes=jobs)
163 data = pool.map(_ResolveTombstone, tombstones)
Viet-Trung Luu235cf3d2015-06-11 10:01:25 -0700164 for tombstone in data:
165 for line in tombstone:
166 logging.info(line)
James Robinson646469d2014-10-03 15:33:28 -0700167
168
169def _GetTombstonesForDevice(device, options):
170 """Returns a list of tombstones on a given device.
171
172 Args:
173 device: An instance of DeviceUtils.
174 options: command line arguments from OptParse
175 """
176 ret = []
177 all_tombstones = list(_ListTombstones(device))
178 if not all_tombstones:
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700179 logging.warning('No tombstones.')
James Robinson646469d2014-10-03 15:33:28 -0700180 return ret
181
182 # Sort the tombstones in date order, descending
183 all_tombstones.sort(cmp=lambda a, b: cmp(b[1], a[1]))
184
185 # Only resolve the most recent unless --all-tombstones given.
186 tombstones = all_tombstones if options.all_tombstones else [all_tombstones[0]]
187
188 device_now = _GetDeviceDateTime(device)
Dave Moore0ae79f42015-03-17 12:56:46 -0700189 try:
190 for tombstone_file, tombstone_time in tombstones:
191 ret += [{'serial': str(device),
192 'device_abi': device.product_cpu_abi,
193 'device_now': device_now,
194 'time': tombstone_time,
195 'file': tombstone_file,
196 'stack': options.stack,
197 'data': _GetTombstoneData(device, tombstone_file)}]
198 except device_errors.CommandFailedError:
199 for line in device.RunShellCommand(
200 ['ls', '-a', '-l', '/data/tombstones'],
201 as_root=True, check_return=True, env=_TZ_UTC, timeout=60):
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700202 logging.info('%s: %s', str(device), line)
Dave Moore0ae79f42015-03-17 12:56:46 -0700203 raise
James Robinson646469d2014-10-03 15:33:28 -0700204
205 # Erase all the tombstones if desired.
206 if options.wipe_tombstones:
207 for tombstone_file, _ in all_tombstones:
208 _EraseTombstone(device, tombstone_file)
209
210 return ret
211
212
213def main():
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700214 custom_handler = logging.StreamHandler(sys.stdout)
215 custom_handler.setFormatter(run_tests_helper.CustomFormatter())
216 logging.getLogger().addHandler(custom_handler)
217 logging.getLogger().setLevel(logging.INFO)
218
James Robinson646469d2014-10-03 15:33:28 -0700219 parser = optparse.OptionParser()
220 parser.add_option('--device',
221 help='The serial number of the device. If not specified '
222 'will use all devices.')
223 parser.add_option('-a', '--all-tombstones', action='store_true',
224 help="""Resolve symbols for all tombstones, rather than just
225 the most recent""")
226 parser.add_option('-s', '--stack', action='store_true',
227 help='Also include symbols for stack data')
228 parser.add_option('-w', '--wipe-tombstones', action='store_true',
229 help='Erase all tombstones from device after processing')
230 parser.add_option('-j', '--jobs', type='int',
231 default=4,
232 help='Number of jobs to use when processing multiple '
233 'crash stacks.')
234 options, _ = parser.parse_args()
235
236 if options.device:
James Robinson0fae0002015-05-05 16:31:51 -0700237 devices = [device_utils.DeviceUtils(options.device)]
James Robinson646469d2014-10-03 15:33:28 -0700238 else:
James Robinson0fae0002015-05-05 16:31:51 -0700239 devices = device_utils.DeviceUtils.HealthyDevices()
James Robinson646469d2014-10-03 15:33:28 -0700240
Alhaad Gokhale4f513072015-03-24 10:49:34 -0700241 # This must be done serially because strptime can hit a race condition if
242 # used for the first time in a multithreaded environment.
243 # http://bugs.python.org/issue7980
James Robinson646469d2014-10-03 15:33:28 -0700244 tombstones = []
James Robinson0fae0002015-05-05 16:31:51 -0700245 for device in devices:
James Robinson646469d2014-10-03 15:33:28 -0700246 tombstones += _GetTombstonesForDevice(device, options)
247
248 _ResolveTombstones(options.jobs, tombstones)
249
James Robinson0fae0002015-05-05 16:31:51 -0700250
James Robinson646469d2014-10-03 15:33:28 -0700251if __name__ == '__main__':
252 sys.exit(main())