Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / build / android / tombstones.py
blob7449a73b9aab7baf216dc22dfb98a0aa4fa21a74
1 #!/usr/bin/env python
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.
12 import datetime
13 import multiprocessing
14 import os
15 import re
16 import subprocess
17 import sys
18 import optparse
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.
27 Args:
28 device: An instance of DeviceUtils.
30 Yields:
31 Tuples of (tombstone filename, date time of file on device).
32 """
33 lines = device.RunShellCommand('TZ=UTC su -c ls -a -l /data/tombstones')
34 for line in lines:
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],
38 '%Y-%m-%d %H:%M')
39 yield details[-1], t
42 def _GetDeviceDateTime(device):
43 """Determine the date time on the device.
45 Args:
46 device: An instance of DeviceUtils.
48 Returns:
49 A datetime instance.
50 """
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
59 Args:
60 device: An instance of DeviceUtils.
61 tombstone_file: the tombstone to retrieve
63 Returns:
64 A list of lines
65 """
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.
73 Args:
74 device: An instance of DeviceUtils.
75 tombstone_file: the tombstone to delete.
76 """
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']
85 for arch in arches:
86 if arch in device_abi:
87 return arch
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.
93 Args:
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.
98 Yields:
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)
105 if found_abi:
106 device_abi = found_abi.group(1)
107 arch = _DeviceAbiToArch(device_abi)
108 if not arch:
109 return
111 stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
112 'third_party', 'android_platform', 'development',
113 'scripts', 'stack')
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:
119 break
120 yield line
123 def _ResolveTombstone(tombstone):
124 lines = []
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)
130 print 'Resolving...'
131 lines += _ResolveSymbols(tombstone['data'], tombstone['stack'],
132 tombstone['device_abi'])
133 return lines
136 def _ResolveTombstones(jobs, tombstones):
137 """Resolve a list of tombstones.
139 Args:
140 jobs: the number of jobs to use with multiprocess.
141 tombstones: a list of tombstones.
143 if not tombstones:
144 print 'No device attached? Or no tombstones?'
145 return
146 if len(tombstones) == 1:
147 data = _ResolveTombstone(tombstones[0])
148 else:
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.
158 Args:
159 device: An instance of DeviceUtils.
160 options: command line arguments from OptParse
162 ret = []
163 all_tombstones = list(_ListTombstones(device))
164 if not all_tombstones:
165 print 'No device attached? Or no tombstones?'
166 return ret
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)
189 return ret
192 def main():
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
199 the most recent""")
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',
205 default=4,
206 help='Number of jobs to use when processing multiple '
207 'crash stacks.')
208 options, _ = parser.parse_args()
210 if options.device:
211 devices = [options.device]
212 else:
213 devices = android_commands.GetAttachedDevices()
215 tombstones = []
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__':
223 sys.exit(main())