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.
14 import multiprocessing
20 from pylib
import android_commands
23 def _ListTombstones(adb
):
24 """List the tombstone files on the device.
27 adb: An instance of AndroidCommands.
30 Tuples of (tombstone filename, date time of file on device).
32 lines
= adb
.RunShellCommand('TZ=UTC su -c ls -a -l /data/tombstones')
34 if 'tombstone' in line
and not 'No such file or directory' in line
:
35 details
= line
.split()
36 t
= datetime
.datetime
.strptime(details
[-3] + ' ' + details
[-2],
41 def _GetDeviceDateTime(adb
):
42 """Determine the date time on the device.
45 adb: An instance of AndroidCommands.
50 device_now_string
= adb
.RunShellCommand('TZ=UTC date')
51 return datetime
.datetime
.strptime(
52 device_now_string
[0], '%a %b %d %H:%M:%S %Z %Y')
55 def _GetTombstoneData(adb
, tombstone_file
):
56 """Retrieve the tombstone data from the device
59 tombstone_file: the tombstone to retrieve
64 return adb
.GetProtectedFileContents('/data/tombstones/' + tombstone_file
)
67 def _EraseTombstone(adb
, tombstone_file
):
68 """Deletes a tombstone from the device.
71 tombstone_file: the tombstone to delete.
73 return adb
.RunShellCommandWithSU('rm /data/tombstones/' + tombstone_file
)
76 def _ResolveSymbols(tombstone_data
, include_stack
):
77 """Run the stack tool for given tombstone input.
80 tombstone_data: a list of strings of tombstone data.
81 include_stack: boolean whether to include stack data in output.
84 A string for each line of resolved stack output.
86 stack_tool
= os
.path
.join(os
.path
.dirname(__file__
), '..', '..',
87 'third_party', 'android_platform', 'development',
89 proc
= subprocess
.Popen(stack_tool
, stdin
=subprocess
.PIPE
,
90 stdout
=subprocess
.PIPE
)
91 output
= proc
.communicate(input='\n'.join(tombstone_data
))[0]
92 for line
in output
.split('\n'):
93 if not include_stack
and 'Stack Data:' in line
:
98 def _ResolveTombstone(tombstone
):
100 lines
+= [tombstone
['file'] + ' created on ' + str(tombstone
['time']) +
101 ', about this long ago: ' +
102 (str(tombstone
['device_now'] - tombstone
['time']) +
103 ' Device: ' + tombstone
['serial'])]
104 print '\n'.join(lines
)
106 lines
+= _ResolveSymbols(tombstone
['data'], tombstone
['stack'])
110 def _ResolveTombstones(jobs
, tombstones
):
111 """Resolve a list of tombstones.
114 jobs: the number of jobs to use with multiprocess.
115 tombstones: a list of tombstones.
118 print 'No device attached? Or no tombstones?'
120 if len(tombstones
) == 1:
121 data
= _ResolveTombstone(tombstones
[0])
123 pool
= multiprocessing
.Pool(processes
=jobs
)
124 data
= pool
.map(_ResolveTombstone
, tombstones
)
125 data
= ['\n'.join(d
) for d
in data
]
126 print '\n'.join(data
)
129 def _GetTombstonesForDevice(adb
, options
):
130 """Returns a list of tombstones on a given adb connection.
133 adb: An instance of Androidcommands.
134 options: command line arguments from OptParse
137 all_tombstones
= list(_ListTombstones(adb
))
138 if not all_tombstones
:
139 print 'No device attached? Or no tombstones?'
142 # Sort the tombstones in date order, descending
143 all_tombstones
.sort(cmp=lambda a
, b
: cmp(b
[1], a
[1]))
145 # Only resolve the most recent unless --all-tombstones given.
146 tombstones
= all_tombstones
if options
.all_tombstones
else [all_tombstones
[0]]
148 device_now
= _GetDeviceDateTime(adb
)
149 for tombstone_file
, tombstone_time
in tombstones
:
150 ret
+= [{'serial': adb
.Adb().GetSerialNumber(),
151 'device_now': device_now
,
152 'time': tombstone_time
,
153 'file': tombstone_file
,
154 'stack': options
.stack
,
155 'data': _GetTombstoneData(adb
, tombstone_file
)}]
157 # Erase all the tombstones if desired.
158 if options
.wipe_tombstones
:
159 for tombstone_file
, _
in all_tombstones
:
160 _EraseTombstone(adb
, tombstone_file
)
165 parser
= optparse
.OptionParser()
166 parser
.add_option('--device',
167 help='The serial number of the device. If not specified '
168 'will use all devices.')
169 parser
.add_option('-a', '--all-tombstones', action
='store_true',
170 help="""Resolve symbols for all tombstones, rather than just
172 parser
.add_option('-s', '--stack', action
='store_true',
173 help='Also include symbols for stack data')
174 parser
.add_option('-w', '--wipe-tombstones', action
='store_true',
175 help='Erase all tombstones from device after processing')
176 parser
.add_option('-j', '--jobs', type='int',
178 help='Number of jobs to use when processing multiple '
180 options
, args
= parser
.parse_args()
183 devices
= [options
.device
]
185 devices
= android_commands
.GetAttachedDevices()
188 for device
in devices
:
189 adb
= android_commands
.AndroidCommands(device
)
190 tombstones
+= _GetTombstonesForDevice(adb
, options
)
192 _ResolveTombstones(options
.jobs
, tombstones
)
194 if __name__
== '__main__':