- add sources.
[platform/framework/web/crosswalk.git] / src / build / android / tombstones.py
1 #!/usr/bin/env python
2 #
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.
6 #
7 # Find the most recent tombstone file(s) on all connected devices
8 # and prints their stacks.
9 #
10 # Assumes tombstone file was created with current symbols.
11
12 import datetime
13 import logging
14 import multiprocessing
15 import os
16 import subprocess
17 import sys
18 import optparse
19
20 from pylib import android_commands
21
22
23 def _ListTombstones(adb):
24   """List the tombstone files on the device.
25
26   Args:
27     adb: An instance of AndroidCommands.
28
29   Yields:
30     Tuples of (tombstone filename, date time of file on device).
31   """
32   lines = adb.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
39
40
41 def _GetDeviceDateTime(adb):
42   """Determine the date time on the device.
43
44   Args:
45     adb: An instance of AndroidCommands.
46
47   Returns:
48     A datetime instance.
49   """
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')
53
54
55 def _GetTombstoneData(adb, tombstone_file):
56   """Retrieve the tombstone data from the device
57
58   Args:
59     tombstone_file: the tombstone to retrieve
60
61   Returns:
62     A list of lines
63   """
64   return adb.GetProtectedFileContents('/data/tombstones/' + tombstone_file)
65
66
67 def _EraseTombstone(adb, tombstone_file):
68   """Deletes a tombstone from the device.
69
70   Args:
71     tombstone_file: the tombstone to delete.
72   """
73   return adb.RunShellCommandWithSU('rm /data/tombstones/' + tombstone_file)
74
75
76 def _ResolveSymbols(tombstone_data, include_stack):
77   """Run the stack tool for given tombstone input.
78
79   Args:
80     tombstone_data: a list of strings of tombstone data.
81     include_stack: boolean whether to include stack data in output.
82
83   Yields:
84     A string for each line of resolved stack output.
85   """
86   stack_tool = os.path.join(os.path.dirname(__file__), '..', '..',
87                             'third_party', 'android_platform', 'development',
88                             'scripts', 'stack')
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:
94       break
95     yield line
96
97
98 def _ResolveTombstone(tombstone):
99   lines = []
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)
105   print 'Resolving...'
106   lines += _ResolveSymbols(tombstone['data'], tombstone['stack'])
107   return lines
108
109
110 def _ResolveTombstones(jobs, tombstones):
111   """Resolve a list of tombstones.
112
113   Args:
114     jobs: the number of jobs to use with multiprocess.
115     tombstones: a list of tombstones.
116   """
117   if not tombstones:
118     print 'No device attached?  Or no tombstones?'
119     return
120   if len(tombstones) == 1:
121     data = _ResolveTombstone(tombstones[0])
122   else:
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)
127
128
129 def _GetTombstonesForDevice(adb, options):
130   """Returns a list of tombstones on a given adb connection.
131
132   Args:
133     adb: An instance of Androidcommands.
134     options: command line arguments from OptParse
135   """
136   ret = []
137   all_tombstones = list(_ListTombstones(adb))
138   if not all_tombstones:
139     print 'No device attached?  Or no tombstones?'
140     return ret
141
142   # Sort the tombstones in date order, descending
143   all_tombstones.sort(cmp=lambda a, b: cmp(b[1], a[1]))
144
145   # Only resolve the most recent unless --all-tombstones given.
146   tombstones = all_tombstones if options.all_tombstones else [all_tombstones[0]]
147
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)}]
156
157   # Erase all the tombstones if desired.
158   if options.wipe_tombstones:
159     for tombstone_file, _ in all_tombstones:
160       _EraseTombstone(adb, tombstone_file)
161
162   return ret
163
164 def main():
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
171                          the most recent""")
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',
177                     default=4,
178                     help='Number of jobs to use when processing multiple '
179                          'crash stacks.')
180   options, args = parser.parse_args()
181
182   if options.device:
183     devices = [options.device]
184   else:
185     devices = android_commands.GetAttachedDevices()
186
187   tombstones = []
188   for device in devices:
189     adb = android_commands.AndroidCommands(device)
190     tombstones += _GetTombstonesForDevice(adb, options)
191
192   _ResolveTombstones(options.jobs, tombstones)
193
194 if __name__ == '__main__':
195   sys.exit(main())