Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Side by Side Diff: build/android/adb_logcat_printer.py

Issue 10578032: Upstreaming android build tools (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed lint issues Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/android/adb_logcat_monitor.py ('k') | build/android/cmd_helper.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2012 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 """Shutdown adb_logcat_monitor and print accumulated logs.
8
9 To test, call './adb_logcat_printer.py <base_dir>' where
10 <base_dir> contains 'adb logcat -v threadtime' files named as
11 logcat_<deviceID>_<sequenceNum>
12
13 The script will print the files to out, and will combine multiple
14 logcats from a single device if there is overlap.
15
16 Additionally, if a <base_dir>/LOGCAT_MONITOR_PID exists, the script
17 will attempt to terminate the contained PID by sending a SIGINT and
18 monitoring for the deletion of the aforementioned file.
19 """
20
21 import cStringIO
22 import logging
23 import os
24 import re
25 import signal
26 import sys
27 import time
28
29
30 # Set this to debug for more verbose output
31 LOG_LEVEL = logging.INFO
32
33
34 def CombineLogFiles(list_of_lists, logger):
35 """Splices together multiple logcats from the same device.
36
37 Args:
38 list_of_lists: list of pairs (filename, list of timestamped lines)
39 logger: handler to log events
40
41 Returns:
42 list of lines with duplicates removed
43 """
44 cur_device_log = ['']
45 for cur_file, cur_file_lines in list_of_lists:
46 # Ignore files with just the logcat header
47 if len(cur_file_lines) < 2:
48 continue
49 common_index = 0
50 # Skip this step if list just has empty string
51 if len(cur_device_log) > 1:
52 try:
53 line = cur_device_log[-1]
54 # Used to make sure we only splice on a timestamped line
55 if re.match('^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line):
56 common_index = cur_file_lines.index(line)
57 else:
58 logger.warning('splice error - no timestamp in "%s"?', line.strip())
59 except ValueError:
60 # The last line was valid but wasn't found in the next file
61 cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****']
62 logger.info('Unable to splice %s. Incomplete logcat?', cur_file)
63
64 cur_device_log += ['*'*30 + ' %s' % cur_file]
65 cur_device_log.extend(cur_file_lines[common_index:])
66
67 return cur_device_log
68
69
70 def FindLogFiles(base_dir):
71 """Search a directory for logcat files.
72
73 Args:
74 base_dir: directory to search
75
76 Returns:
77 Mapping of device_id to a sorted list of file paths for a given device
78 """
79 logcat_filter = re.compile('^logcat_(\w+)_(\d+)$')
80 # list of tuples (<device_id>, <seq num>, <full file path>)
81 filtered_list = []
82 for cur_file in os.listdir(base_dir):
83 matcher = logcat_filter.match(cur_file)
84 if matcher:
85 filtered_list += [(matcher.group(1), int(matcher.group(2)),
86 os.path.join(base_dir, cur_file))]
87 filtered_list.sort()
88 file_map = {}
89 for device_id, _, cur_file in filtered_list:
90 if not device_id in file_map:
91 file_map[device_id] = []
92
93 file_map[device_id] += [cur_file]
94 return file_map
95
96
97 def GetDeviceLogs(log_filenames, logger):
98 """Read log files, combine and format.
99
100 Args:
101 log_filenames: mapping of device_id to sorted list of file paths
102 logger: logger handle for logging events
103
104 Returns:
105 list of formatted device logs, one for each device.
106 """
107 device_logs = []
108
109 for device, device_files in log_filenames.iteritems():
110 logger.debug('%s: %s', device, str(device_files))
111 device_file_lines = []
112 for cur_file in device_files:
113 with open(cur_file) as f:
114 device_file_lines += [(cur_file, f.read().splitlines())]
115 combined_lines = CombineLogFiles(device_file_lines, logger)
116 # Prepend each line with a short unique ID so it's easy to see
117 # when the device changes. We don't use the start of the device
118 # ID because it can be the same among devices. Example lines:
119 # AB324: foo
120 # AB324: blah
121 device_logs += [('\n' + device[-5:] + ': ').join(combined_lines)]
122 return device_logs
123
124
125 def ShutdownLogcatMonitor(base_dir, logger):
126 """Attempts to shutdown adb_logcat_monitor and blocks while waiting."""
127 try:
128 monitor_pid_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID')
129 with open(monitor_pid_path) as f:
130 monitor_pid = int(f.readline())
131
132 logger.info('Sending SIGTERM to %d', monitor_pid)
133 os.kill(monitor_pid, signal.SIGTERM)
134 i = 0
135 while True:
136 time.sleep(.2)
137 if not os.path.exists(monitor_pid_path):
138 return
139 if not os.path.exists('/proc/%d' % monitor_pid):
140 logger.warning('Monitor (pid %d) terminated uncleanly?', monitor_pid)
141 return
142 logger.info('Waiting for logcat process to terminate.')
143 i += 1
144 if i >= 10:
145 logger.warning('Monitor pid did not terminate. Continuing anyway.')
146 return
147
148 except (ValueError, IOError, OSError):
149 logger.exception('Error signaling logcat monitor - continuing')
150
151
152 def main(base_dir, output_file):
153 log_stringio = cStringIO.StringIO()
154 logger = logging.getLogger('LogcatPrinter')
155 logger.setLevel(LOG_LEVEL)
156 sh = logging.StreamHandler(log_stringio)
157 sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s'
158 ' %(message)s'))
159 logger.addHandler(sh)
160
161 try:
162 # Wait at least 5 seconds after base_dir is created before printing.
163 #
164 # The idea is that 'adb logcat > file' output consists of 2 phases:
165 # 1 Dump all the saved logs to the file
166 # 2 Stream log messages as they are generated
167 #
168 # We want to give enough time for phase 1 to complete. There's no
169 # good method to tell how long to wait, but it usually only takes a
170 # second. On most bots, this code path won't occur at all, since
171 # adb_logcat_monitor.py command will have spawned more than 5 seconds
172 # prior to called this shell script.
173 try:
174 sleep_time = 5 - (time.time() - os.path.getctime(base_dir))
175 except OSError:
176 sleep_time = 5
177 if sleep_time > 0:
178 logger.warning('Monitor just started? Sleeping %.1fs', sleep_time)
179 time.sleep(sleep_time)
180
181 assert os.path.exists(base_dir), '%s does not exist' % base_dir
182 ShutdownLogcatMonitor(base_dir, logger)
183 separator = '\n' + '*' * 80 + '\n\n'
184 for log in GetDeviceLogs(FindLogFiles(base_dir), logger):
185 output_file.write(log)
186 output_file.write(separator)
187 with open(os.path.join(base_dir, 'eventlog')) as f:
188 output_file.write('\nLogcat Monitor Event Log\n')
189 output_file.write(f.read())
190 except:
191 logger.exception('Unexpected exception')
192
193 logger.info('Done.')
194 sh.flush()
195 output_file.write('\nLogcat Printer Event Log\n')
196 output_file.write(log_stringio.getvalue())
197
198 if __name__ == '__main__':
199 if len(sys.argv) == 1:
200 print 'Usage: %s <base_dir>' % sys.argv[0]
201 sys.exit(1)
202 sys.exit(main(sys.argv[1], sys.stdout))
OLDNEW
« no previous file with comments | « build/android/adb_logcat_monitor.py ('k') | build/android/cmd_helper.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698