Move prefs::kLastPolicyStatisticsUpdate to the policy component.
[chromium-blink-merge.git] / build / android / adb_logcat_printer.py
blob5194668ec6e80dcf800758b1b9670c3fe9167f62
1 #!/usr/bin/env python
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.
7 """Shutdown adb_logcat_monitor and print accumulated logs.
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>
13 The script will print the files to out, and will combine multiple
14 logcats from a single device if there is overlap.
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 """
21 import cStringIO
22 import logging
23 import os
24 import re
25 import signal
26 import sys
27 import time
30 # Set this to debug for more verbose output
31 LOG_LEVEL = logging.INFO
34 def CombineLogFiles(list_of_lists, logger):
35 """Splices together multiple logcats from the same device.
37 Args:
38 list_of_lists: list of pairs (filename, list of timestamped lines)
39 logger: handler to log events
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)
64 cur_device_log += ['*'*30 + ' %s' % cur_file]
65 cur_device_log.extend(cur_file_lines[common_index:])
67 return cur_device_log
70 def FindLogFiles(base_dir):
71 """Search a directory for logcat files.
73 Args:
74 base_dir: directory to search
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] = []
93 file_map[device_id] += [cur_file]
94 return file_map
97 def GetDeviceLogs(log_filenames, logger):
98 """Read log files, combine and format.
100 Args:
101 log_filenames: mapping of device_id to sorted list of file paths
102 logger: logger handle for logging events
104 Returns:
105 list of formatted device logs, one for each device.
107 device_logs = []
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
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())
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
148 except (ValueError, IOError, OSError):
149 logger.exception('Error signaling logcat monitor - continuing')
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)
161 try:
162 # Wait at least 5 seconds after base_dir is created before printing.
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
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)
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')
193 logger.info('Done.')
194 sh.flush()
195 output_file.write('\nLogcat Printer Event Log\n')
196 output_file.write(log_stringio.getvalue())
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))