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.
7 # Find the most recent tombstone file(s) on all connected devices
8 # and prints their stacks.
10 # Assumes tombstone file was created with current symbols.
15 import multiprocessing
22 from pylib
import android_commands
23 from pylib
.device
import device_errors
24 from pylib
.device
import device_utils
25 from pylib
.utils
import run_tests_helper
28 _TZ_UTC
= {'TZ': 'UTC'}
30 def _ListTombstones(device
):
31 """List the tombstone files on the device.
34 device: An instance of DeviceUtils.
37 Tuples of (tombstone filename, date time of file on device).
40 lines
= device
.RunShellCommand(
41 ['ls', '-a', '-l', '/data/tombstones'],
42 as_root
=True, check_return
=True, env
=_TZ_UTC
, timeout
=60)
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],
49 except device_errors
.CommandFailedError
:
50 logging
.exception('Could not retrieve tombstones.')
53 def _GetDeviceDateTime(device
):
54 """Determine the date time on the device.
57 device: An instance of DeviceUtils.
62 device_now_string
= device
.RunShellCommand(
63 ['date'], check_return
=True, env
=_TZ_UTC
)
64 return datetime
.datetime
.strptime(
65 device_now_string
[0], '%a %b %d %H:%M:%S %Z %Y')
68 def _GetTombstoneData(device
, tombstone_file
):
69 """Retrieve the tombstone data from the device
72 device: An instance of DeviceUtils.
73 tombstone_file: the tombstone to retrieve
78 return device
.ReadFile(
79 '/data/tombstones/' + tombstone_file
, as_root
=True).splitlines()
82 def _EraseTombstone(device
, tombstone_file
):
83 """Deletes a tombstone from the device.
86 device: An instance of DeviceUtils.
87 tombstone_file: the tombstone to delete.
89 return device
.RunShellCommand(
90 ['rm', '/data/tombstones/' + tombstone_file
],
91 as_root
=True, check_return
=True)
94 def _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']
99 if arch
in device_abi
:
101 raise RuntimeError('Unknown device ABI: %s' % device_abi
)
103 def _ResolveSymbols(tombstone_data
, include_stack
, device_abi
):
104 """Run the stack tool for given tombstone input.
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.
112 A string for each line of resolved stack output.
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
)
119 device_abi
= found_abi
.group(1)
120 arch
= _DeviceAbiToArch(device_abi
)
124 stack_tool
= os
.path
.join(os
.path
.dirname(__file__
), '..', '..',
125 'third_party', 'android_platform', 'development',
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
:
136 def _ResolveTombstone(tombstone
):
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'])]
142 logging
.info('\n'.join(lines
))
143 logging
.info('Resolving...')
144 lines
+= _ResolveSymbols(tombstone
['data'], tombstone
['stack'],
145 tombstone
['device_abi'])
149 def _ResolveTombstones(jobs
, tombstones
):
150 """Resolve a list of tombstones.
153 jobs: the number of jobs to use with multiprocess.
154 tombstones: a list of tombstones.
157 logging
.warning('No tombstones to resolve.')
159 if len(tombstones
) == 1:
160 data
= _ResolveTombstone(tombstones
[0])
162 pool
= multiprocessing
.Pool(processes
=jobs
)
163 data
= pool
.map(_ResolveTombstone
, tombstones
)
168 def _GetTombstonesForDevice(device
, options
):
169 """Returns a list of tombstones on a given device.
172 device: An instance of DeviceUtils.
173 options: command line arguments from OptParse
176 all_tombstones
= list(_ListTombstones(device
))
177 if not all_tombstones
:
178 logging
.warning('No tombstones.')
181 # Sort the tombstones in date order, descending
182 all_tombstones
.sort(cmp=lambda a
, b
: cmp(b
[1], a
[1]))
184 # Only resolve the most recent unless --all-tombstones given.
185 tombstones
= all_tombstones
if options
.all_tombstones
else [all_tombstones
[0]]
187 device_now
= _GetDeviceDateTime(device
)
189 for tombstone_file
, tombstone_time
in tombstones
:
190 ret
+= [{'serial': str(device
),
191 'device_abi': device
.product_cpu_abi
,
192 'device_now': device_now
,
193 'time': tombstone_time
,
194 'file': tombstone_file
,
195 'stack': options
.stack
,
196 'data': _GetTombstoneData(device
, tombstone_file
)}]
197 except device_errors
.CommandFailedError
:
198 for line
in device
.RunShellCommand(
199 ['ls', '-a', '-l', '/data/tombstones'],
200 as_root
=True, check_return
=True, env
=_TZ_UTC
, timeout
=60):
201 logging
.info('%s: %s', str(device
), line
)
204 # Erase all the tombstones if desired.
205 if options
.wipe_tombstones
:
206 for tombstone_file
, _
in all_tombstones
:
207 _EraseTombstone(device
, tombstone_file
)
213 custom_handler
= logging
.StreamHandler(sys
.stdout
)
214 custom_handler
.setFormatter(run_tests_helper
.CustomFormatter())
215 logging
.getLogger().addHandler(custom_handler
)
216 logging
.getLogger().setLevel(logging
.INFO
)
218 parser
= optparse
.OptionParser()
219 parser
.add_option('--device',
220 help='The serial number of the device. If not specified '
221 'will use all devices.')
222 parser
.add_option('-a', '--all-tombstones', action
='store_true',
223 help="""Resolve symbols for all tombstones, rather than just
225 parser
.add_option('-s', '--stack', action
='store_true',
226 help='Also include symbols for stack data')
227 parser
.add_option('-w', '--wipe-tombstones', action
='store_true',
228 help='Erase all tombstones from device after processing')
229 parser
.add_option('-j', '--jobs', type='int',
231 help='Number of jobs to use when processing multiple '
233 options
, _
= parser
.parse_args()
236 devices
= [options
.device
]
238 devices
= android_commands
.GetAttachedDevices()
240 # This must be done serially because strptime can hit a race condition if
241 # used for the first time in a multithreaded environment.
242 # http://bugs.python.org/issue7980
244 for device_serial
in devices
:
245 device
= device_utils
.DeviceUtils(device_serial
)
246 tombstones
+= _GetTombstonesForDevice(device
, options
)
248 _ResolveTombstones(options
.jobs
, tombstones
)
250 if __name__
== '__main__':