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.
13 import multiprocessing
20 from pylib
import android_commands
21 from pylib
.device
import device_utils
24 def _ListTombstones(device
):
25 """List the tombstone files on the device.
28 device: An instance of DeviceUtils.
31 Tuples of (tombstone filename, date time of file on device).
33 lines
= device
.RunShellCommand('TZ=UTC su -c ls -a -l /data/tombstones')
35 if 'tombstone' in line
and not 'No such file or directory' in line
:
36 details
= line
.split()
37 t
= datetime
.datetime
.strptime(details
[-3] + ' ' + details
[-2],
42 def _GetDeviceDateTime(device
):
43 """Determine the date time on the device.
46 device: An instance of DeviceUtils.
51 device_now_string
= device
.RunShellCommand('TZ=UTC date')
52 return datetime
.datetime
.strptime(
53 device_now_string
[0], '%a %b %d %H:%M:%S %Z %Y')
56 def _GetTombstoneData(device
, tombstone_file
):
57 """Retrieve the tombstone data from the device
60 device: An instance of DeviceUtils.
61 tombstone_file: the tombstone to retrieve
66 return device
.ReadFile(
67 '/data/tombstones/' + tombstone_file
, as_root
=True).splitlines()
70 def _EraseTombstone(device
, tombstone_file
):
71 """Deletes a tombstone from the device.
74 device: An instance of DeviceUtils.
75 tombstone_file: the tombstone to delete.
77 return device
.RunShellCommand(
78 'rm /data/tombstones/' + tombstone_file
, as_root
=True)
81 def _DeviceAbiToArch(device_abi
):
82 # The order of this list is significant to find the more specific match (e.g.,
83 # arm64) before the less specific (e.g., arm).
84 arches
= ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips']
86 if arch
in device_abi
:
88 raise RuntimeError('Unknown device ABI: %s' % device_abi
)
90 def _ResolveSymbols(tombstone_data
, include_stack
, device_abi
):
91 """Run the stack tool for given tombstone input.
94 tombstone_data: a list of strings of tombstone data.
95 include_stack: boolean whether to include stack data in output.
96 device_abi: the default ABI of the device which generated the tombstone.
99 A string for each line of resolved stack output.
101 # Check if the tombstone data has an ABI listed, if so use this in preference
102 # to the device's default ABI.
103 for line
in tombstone_data
:
104 found_abi
= re
.search('ABI: \'(.+?)\'', line
)
106 device_abi
= found_abi
.group(1)
107 arch
= _DeviceAbiToArch(device_abi
)
111 stack_tool
= os
.path
.join(os
.path
.dirname(__file__
), '..', '..',
112 'third_party', 'android_platform', 'development',
114 proc
= subprocess
.Popen([stack_tool
, '--arch', arch
], stdin
=subprocess
.PIPE
,
115 stdout
=subprocess
.PIPE
)
116 output
= proc
.communicate(input='\n'.join(tombstone_data
))[0]
117 for line
in output
.split('\n'):
118 if not include_stack
and 'Stack Data:' in line
:
123 def _ResolveTombstone(tombstone
):
125 lines
+= [tombstone
['file'] + ' created on ' + str(tombstone
['time']) +
126 ', about this long ago: ' +
127 (str(tombstone
['device_now'] - tombstone
['time']) +
128 ' Device: ' + tombstone
['serial'])]
129 print '\n'.join(lines
)
131 lines
+= _ResolveSymbols(tombstone
['data'], tombstone
['stack'],
132 tombstone
['device_abi'])
136 def _ResolveTombstones(jobs
, tombstones
):
137 """Resolve a list of tombstones.
140 jobs: the number of jobs to use with multiprocess.
141 tombstones: a list of tombstones.
144 print 'No device attached? Or no tombstones?'
146 if len(tombstones
) == 1:
147 data
= _ResolveTombstone(tombstones
[0])
149 pool
= multiprocessing
.Pool(processes
=jobs
)
150 data
= pool
.map(_ResolveTombstone
, tombstones
)
151 data
= ['\n'.join(d
) for d
in data
]
152 print '\n'.join(data
)
155 def _GetTombstonesForDevice(device
, options
):
156 """Returns a list of tombstones on a given device.
159 device: An instance of DeviceUtils.
160 options: command line arguments from OptParse
163 all_tombstones
= list(_ListTombstones(device
))
164 if not all_tombstones
:
165 print 'No device attached? Or no tombstones?'
168 # Sort the tombstones in date order, descending
169 all_tombstones
.sort(cmp=lambda a
, b
: cmp(b
[1], a
[1]))
171 # Only resolve the most recent unless --all-tombstones given.
172 tombstones
= all_tombstones
if options
.all_tombstones
else [all_tombstones
[0]]
174 device_now
= _GetDeviceDateTime(device
)
175 for tombstone_file
, tombstone_time
in tombstones
:
176 ret
+= [{'serial': str(device
),
177 'device_abi': device
.product_cpu_abi
,
178 'device_now': device_now
,
179 'time': tombstone_time
,
180 'file': tombstone_file
,
181 'stack': options
.stack
,
182 'data': _GetTombstoneData(device
, tombstone_file
)}]
184 # Erase all the tombstones if desired.
185 if options
.wipe_tombstones
:
186 for tombstone_file
, _
in all_tombstones
:
187 _EraseTombstone(device
, tombstone_file
)
193 parser
= optparse
.OptionParser()
194 parser
.add_option('--device',
195 help='The serial number of the device. If not specified '
196 'will use all devices.')
197 parser
.add_option('-a', '--all-tombstones', action
='store_true',
198 help="""Resolve symbols for all tombstones, rather than just
200 parser
.add_option('-s', '--stack', action
='store_true',
201 help='Also include symbols for stack data')
202 parser
.add_option('-w', '--wipe-tombstones', action
='store_true',
203 help='Erase all tombstones from device after processing')
204 parser
.add_option('-j', '--jobs', type='int',
206 help='Number of jobs to use when processing multiple '
208 options
, _
= parser
.parse_args()
211 devices
= [options
.device
]
213 devices
= android_commands
.GetAttachedDevices()
216 for device_serial
in devices
:
217 device
= device_utils
.DeviceUtils(device_serial
)
218 tombstones
+= _GetTombstonesForDevice(device
, options
)
220 _ResolveTombstones(options
.jobs
, tombstones
)
222 if __name__
== '__main__':