Disabled accessibility browser test for content editable descendents.
[chromium-blink-merge.git] / build / android / tombstones.py
blob3f3508631ada13a58cac2f83826cccef10fa6752
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 itertools
14 import logging
15 import multiprocessing
16 import os
17 import re
18 import subprocess
19 import sys
20 import optparse
22 from pylib.device import adb_wrapper
23 from pylib.device import device_errors
24 from pylib.device import device_filter
25 from pylib.device import device_utils
26 from pylib.utils import run_tests_helper
29 _TZ_UTC = {'TZ': 'UTC'}
31 def _ListTombstones(device):
32 """List the tombstone files on the device.
34 Args:
35 device: An instance of DeviceUtils.
37 Yields:
38 Tuples of (tombstone filename, date time of file on device).
39 """
40 try:
41 lines = device.RunShellCommand(
42 ['ls', '-a', '-l', '/data/tombstones'],
43 as_root=True, check_return=True, env=_TZ_UTC, timeout=60)
44 for line in lines:
45 if 'tombstone' in line and not 'No such file or directory' in line:
46 details = line.split()
47 t = datetime.datetime.strptime(details[-3] + ' ' + details[-2],
48 '%Y-%m-%d %H:%M')
49 yield details[-1], t
50 except device_errors.CommandFailedError:
51 logging.exception('Could not retrieve tombstones.')
54 def _GetDeviceDateTime(device):
55 """Determine the date time on the device.
57 Args:
58 device: An instance of DeviceUtils.
60 Returns:
61 A datetime instance.
62 """
63 device_now_string = device.RunShellCommand(
64 ['date'], check_return=True, env=_TZ_UTC)
65 return datetime.datetime.strptime(
66 device_now_string[0], '%a %b %d %H:%M:%S %Z %Y')
69 def _GetTombstoneData(device, tombstone_file):
70 """Retrieve the tombstone data from the device
72 Args:
73 device: An instance of DeviceUtils.
74 tombstone_file: the tombstone to retrieve
76 Returns:
77 A list of lines
78 """
79 return device.ReadFile(
80 '/data/tombstones/' + tombstone_file, as_root=True).splitlines()
83 def _EraseTombstone(device, tombstone_file):
84 """Deletes a tombstone from the device.
86 Args:
87 device: An instance of DeviceUtils.
88 tombstone_file: the tombstone to delete.
89 """
90 return device.RunShellCommand(
91 ['rm', '/data/tombstones/' + tombstone_file],
92 as_root=True, check_return=True)
95 def _DeviceAbiToArch(device_abi):
96 # The order of this list is significant to find the more specific match (e.g.,
97 # arm64) before the less specific (e.g., arm).
98 arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips']
99 for arch in arches:
100 if arch in device_abi:
101 return arch
102 raise RuntimeError('Unknown device ABI: %s' % device_abi)
104 def _ResolveSymbols(tombstone_data, include_stack, device_abi):
105 """Run the stack tool for given tombstone input.
107 Args:
108 tombstone_data: a list of strings of tombstone data.
109 include_stack: boolean whether to include stack data in output.
110 device_abi: the default ABI of the device which generated the tombstone.
112 Yields:
113 A string for each line of resolved stack output.
115 # Check if the tombstone data has an ABI listed, if so use this in preference
116 # to the device's default ABI.
117 for line in tombstone_data:
118 found_abi = re.search('ABI: \'(.+?)\'', line)
119 if found_abi:
120 device_abi = found_abi.group(1)
121 arch = _DeviceAbiToArch(device_abi)
122 if not arch:
123 return
125 stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
126 'third_party', 'android_platform', 'development',
127 'scripts', 'stack')
128 proc = subprocess.Popen([stack_tool, '--arch', arch], stdin=subprocess.PIPE,
129 stdout=subprocess.PIPE)
130 output = proc.communicate(input='\n'.join(tombstone_data))[0]
131 for line in output.split('\n'):
132 if not include_stack and 'Stack Data:' in line:
133 break
134 yield line
137 def _ResolveTombstone(tombstone):
138 lines = []
139 lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
140 ', about this long ago: ' +
141 (str(tombstone['device_now'] - tombstone['time']) +
142 ' Device: ' + tombstone['serial'])]
143 logging.info('\n'.join(lines))
144 logging.info('Resolving...')
145 lines += _ResolveSymbols(tombstone['data'], tombstone['stack'],
146 tombstone['device_abi'])
147 return lines
150 def _ResolveTombstones(jobs, tombstones):
151 """Resolve a list of tombstones.
153 Args:
154 jobs: the number of jobs to use with multiprocess.
155 tombstones: a list of tombstones.
157 if not tombstones:
158 logging.warning('No tombstones to resolve.')
159 return
160 if len(tombstones) == 1:
161 data = _ResolveTombstone(tombstones[0])
162 else:
163 pool = multiprocessing.Pool(processes=jobs)
164 data = pool.map(_ResolveTombstone, tombstones)
165 for d in data:
166 logging.info(d)
169 def _GetTombstonesForDevice(device, options):
170 """Returns a list of tombstones on a given device.
172 Args:
173 device: An instance of DeviceUtils.
174 options: command line arguments from OptParse
176 ret = []
177 all_tombstones = list(_ListTombstones(device))
178 if not all_tombstones:
179 logging.warning('No tombstones.')
180 return ret
182 # Sort the tombstones in date order, descending
183 all_tombstones.sort(cmp=lambda a, b: cmp(b[1], a[1]))
185 # Only resolve the most recent unless --all-tombstones given.
186 tombstones = all_tombstones if options.all_tombstones else [all_tombstones[0]]
188 device_now = _GetDeviceDateTime(device)
189 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):
202 logging.info('%s: %s', str(device), line)
203 raise
205 # Erase all the tombstones if desired.
206 if options.wipe_tombstones:
207 for tombstone_file, _ in all_tombstones:
208 _EraseTombstone(device, tombstone_file)
210 return ret
213 def main():
214 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)
219 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()
236 if options.device:
237 devices = [options.device]
238 else:
239 devices = adb_wrapper.AdbWrapper.Devices(
240 filters=device_filter.DefaultFilters())
242 # This must be done serially because strptime can hit a race condition if
243 # used for the first time in a multithreaded environment.
244 # http://bugs.python.org/issue7980
245 tombstones = []
246 for adb in devices:
247 device = device_utils.DeviceUtils(adb)
248 tombstones += _GetTombstonesForDevice(device, options)
250 _ResolveTombstones(options.jobs, tombstones)
252 if __name__ == '__main__':
253 sys.exit(main())