Fix compilation error encountered when DEBUG set to 1.
[chromium-blink-merge.git] / build / android / tombstones.py
blobe7549ff73ed12bda06d3315929155a3c65d8a845
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 subprocess
16 import sys
17 import optparse
19 from pylib import android_commands
20 from pylib.device import device_utils
23 def _ListTombstones(device):
24 """List the tombstone files on the device.
26 Args:
27 device: An instance of DeviceUtils.
29 Yields:
30 Tuples of (tombstone filename, date time of file on device).
31 """
32 lines = device.RunShellCommand('TZ=UTC su -c ls -a -l /data/tombstones')
33 for line in lines:
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],
37 '%Y-%m-%d %H:%M')
38 yield details[-1], t
41 def _GetDeviceDateTime(device):
42 """Determine the date time on the device.
44 Args:
45 device: An instance of DeviceUtils.
47 Returns:
48 A datetime instance.
49 """
50 device_now_string = device.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(device, tombstone_file):
56 """Retrieve the tombstone data from the device
58 Args:
59 device: An instance of DeviceUtils.
60 tombstone_file: the tombstone to retrieve
62 Returns:
63 A list of lines
64 """
65 return device.ReadFile('/data/tombstones/' + tombstone_file, as_root=True)
68 def _EraseTombstone(device, tombstone_file):
69 """Deletes a tombstone from the device.
71 Args:
72 device: An instance of DeviceUtils.
73 tombstone_file: the tombstone to delete.
74 """
75 return device.RunShellCommand(
76 'rm /data/tombstones/' + tombstone_file, as_root=True)
79 def _ResolveSymbols(tombstone_data, include_stack):
80 """Run the stack tool for given tombstone input.
82 Args:
83 tombstone_data: a list of strings of tombstone data.
84 include_stack: boolean whether to include stack data in output.
86 Yields:
87 A string for each line of resolved stack output.
88 """
89 stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
90 'third_party', 'android_platform', 'development',
91 'scripts', 'stack')
92 proc = subprocess.Popen(stack_tool, stdin=subprocess.PIPE,
93 stdout=subprocess.PIPE)
94 output = proc.communicate(input='\n'.join(tombstone_data))[0]
95 for line in output.split('\n'):
96 if not include_stack and 'Stack Data:' in line:
97 break
98 yield line
101 def _ResolveTombstone(tombstone):
102 lines = []
103 lines += [tombstone['file'] + ' created on ' + str(tombstone['time']) +
104 ', about this long ago: ' +
105 (str(tombstone['device_now'] - tombstone['time']) +
106 ' Device: ' + tombstone['serial'])]
107 print '\n'.join(lines)
108 print 'Resolving...'
109 lines += _ResolveSymbols(tombstone['data'], tombstone['stack'])
110 return lines
113 def _ResolveTombstones(jobs, tombstones):
114 """Resolve a list of tombstones.
116 Args:
117 jobs: the number of jobs to use with multiprocess.
118 tombstones: a list of tombstones.
120 if not tombstones:
121 print 'No device attached? Or no tombstones?'
122 return
123 if len(tombstones) == 1:
124 data = _ResolveTombstone(tombstones[0])
125 else:
126 pool = multiprocessing.Pool(processes=jobs)
127 data = pool.map(_ResolveTombstone, tombstones)
128 data = ['\n'.join(d) for d in data]
129 print '\n'.join(data)
132 def _GetTombstonesForDevice(device, options):
133 """Returns a list of tombstones on a given device.
135 Args:
136 device: An instance of DeviceUtils.
137 options: command line arguments from OptParse
139 ret = []
140 all_tombstones = list(_ListTombstones(device))
141 if not all_tombstones:
142 print 'No device attached? Or no tombstones?'
143 return ret
145 # Sort the tombstones in date order, descending
146 all_tombstones.sort(cmp=lambda a, b: cmp(b[1], a[1]))
148 # Only resolve the most recent unless --all-tombstones given.
149 tombstones = all_tombstones if options.all_tombstones else [all_tombstones[0]]
151 device_now = _GetDeviceDateTime(device)
152 for tombstone_file, tombstone_time in tombstones:
153 ret += [{'serial': device.old_interface.Adb().GetSerialNumber(),
154 'device_now': device_now,
155 'time': tombstone_time,
156 'file': tombstone_file,
157 'stack': options.stack,
158 'data': _GetTombstoneData(device, tombstone_file)}]
160 # Erase all the tombstones if desired.
161 if options.wipe_tombstones:
162 for tombstone_file, _ in all_tombstones:
163 _EraseTombstone(device, tombstone_file)
165 return ret
167 def main():
168 parser = optparse.OptionParser()
169 parser.add_option('--device',
170 help='The serial number of the device. If not specified '
171 'will use all devices.')
172 parser.add_option('-a', '--all-tombstones', action='store_true',
173 help="""Resolve symbols for all tombstones, rather than just
174 the most recent""")
175 parser.add_option('-s', '--stack', action='store_true',
176 help='Also include symbols for stack data')
177 parser.add_option('-w', '--wipe-tombstones', action='store_true',
178 help='Erase all tombstones from device after processing')
179 parser.add_option('-j', '--jobs', type='int',
180 default=4,
181 help='Number of jobs to use when processing multiple '
182 'crash stacks.')
183 options, _ = parser.parse_args()
185 if options.device:
186 devices = [options.device]
187 else:
188 devices = android_commands.GetAttachedDevices()
190 tombstones = []
191 for device_serial in devices:
192 device = device_utils.DeviceUtils(device_serial)
193 tombstones += _GetTombstonesForDevice(device, options)
195 _ResolveTombstones(options.jobs, tombstones)
197 if __name__ == '__main__':
198 sys.exit(main())