Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Shutdown adb_logcat_monitor and print accumulated logs. | 7 """Shutdown adb_logcat_monitor and print accumulated logs. |
| 8 | 8 |
| 9 To test, call './adb_logcat_printer.py <base_dir>' where | 9 To test, call './adb_logcat_printer.py <base_dir>' where |
| 10 <base_dir> contains 'adb logcat -v threadtime' files named as | 10 <base_dir> contains 'adb logcat -v threadtime' files named as |
| 11 logcat_<deviceID>_<sequenceNum> | 11 logcat_<deviceID>_<sequenceNum> |
| 12 | 12 |
| 13 The script will print the files to out, and will combine multiple | 13 The script will print the files to out, and will combine multiple |
| 14 logcats from a single device if there is overlap. | 14 logcats from a single device if there is overlap. |
| 15 | 15 |
| 16 Additionally, if a <base_dir>/LOGCAT_MONITOR_PID exists, the script | 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 | 17 will attempt to terminate the contained PID by sending a SIGINT and |
| 18 monitoring for the deletion of the aforementioned file. | 18 monitoring for the deletion of the aforementioned file. |
| 19 """ | 19 """ |
| 20 | 20 |
| 21 import cStringIO | 21 import cStringIO |
| 22 import logging | 22 import logging |
| 23 import optparse | |
| 23 import os | 24 import os |
| 24 import re | 25 import re |
| 25 import signal | 26 import signal |
| 26 import sys | 27 import sys |
| 27 import time | 28 import time |
| 28 | 29 |
| 29 | 30 |
| 30 # Set this to debug for more verbose output | 31 # Set this to debug for more verbose output |
| 31 LOG_LEVEL = logging.INFO | 32 LOG_LEVEL = logging.INFO |
| 32 | 33 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 45 for cur_file, cur_file_lines in list_of_lists: | 46 for cur_file, cur_file_lines in list_of_lists: |
| 46 # Ignore files with just the logcat header | 47 # Ignore files with just the logcat header |
| 47 if len(cur_file_lines) < 2: | 48 if len(cur_file_lines) < 2: |
| 48 continue | 49 continue |
| 49 common_index = 0 | 50 common_index = 0 |
| 50 # Skip this step if list just has empty string | 51 # Skip this step if list just has empty string |
| 51 if len(cur_device_log) > 1: | 52 if len(cur_device_log) > 1: |
| 52 try: | 53 try: |
| 53 line = cur_device_log[-1] | 54 line = cur_device_log[-1] |
| 54 # Used to make sure we only splice on a timestamped line | 55 # 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 if re.match(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line): |
| 56 common_index = cur_file_lines.index(line) | 57 common_index = cur_file_lines.index(line) |
| 57 else: | 58 else: |
| 58 logger.warning('splice error - no timestamp in "%s"?', line.strip()) | 59 logger.warning('splice error - no timestamp in "%s"?', line.strip()) |
| 59 except ValueError: | 60 except ValueError: |
| 60 # The last line was valid but wasn't found in the next file | 61 # The last line was valid but wasn't found in the next file |
| 61 cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****'] | 62 cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****'] |
| 62 logger.info('Unable to splice %s. Incomplete logcat?', cur_file) | 63 logger.info('Unable to splice %s. Incomplete logcat?', cur_file) |
| 63 | 64 |
| 64 cur_device_log += ['*'*30 + ' %s' % cur_file] | 65 cur_device_log += ['*'*30 + ' %s' % cur_file] |
| 65 cur_device_log.extend(cur_file_lines[common_index:]) | 66 cur_device_log.extend(cur_file_lines[common_index:]) |
| 66 | 67 |
| 67 return cur_device_log | 68 return cur_device_log |
| 68 | 69 |
| 69 | 70 |
| 70 def FindLogFiles(base_dir): | 71 def FindLogFiles(base_dir): |
| 71 """Search a directory for logcat files. | 72 """Search a directory for logcat files. |
| 72 | 73 |
| 73 Args: | 74 Args: |
| 74 base_dir: directory to search | 75 base_dir: directory to search |
| 75 | 76 |
| 76 Returns: | 77 Returns: |
| 77 Mapping of device_id to a sorted list of file paths for a given device | 78 Mapping of device_id to a sorted list of file paths for a given device |
| 78 """ | 79 """ |
| 79 logcat_filter = re.compile('^logcat_(\w+)_(\d+)$') | 80 logcat_filter = re.compile(r'^logcat_(\w+)_(\d+)$') |
| 80 # list of tuples (<device_id>, <seq num>, <full file path>) | 81 # list of tuples (<device_id>, <seq num>, <full file path>) |
| 81 filtered_list = [] | 82 filtered_list = [] |
| 82 for cur_file in os.listdir(base_dir): | 83 for cur_file in os.listdir(base_dir): |
| 83 matcher = logcat_filter.match(cur_file) | 84 matcher = logcat_filter.match(cur_file) |
| 84 if matcher: | 85 if matcher: |
| 85 filtered_list += [(matcher.group(1), int(matcher.group(2)), | 86 filtered_list += [(matcher.group(1), int(matcher.group(2)), |
| 86 os.path.join(base_dir, cur_file))] | 87 os.path.join(base_dir, cur_file))] |
| 87 filtered_list.sort() | 88 filtered_list.sort() |
| 88 file_map = {} | 89 file_map = {} |
| 89 for device_id, _, cur_file in filtered_list: | 90 for device_id, _, cur_file in filtered_list: |
| 90 if not device_id in file_map: | 91 if device_id not in file_map: |
| 91 file_map[device_id] = [] | 92 file_map[device_id] = [] |
| 92 | 93 |
| 93 file_map[device_id] += [cur_file] | 94 file_map[device_id] += [cur_file] |
| 94 return file_map | 95 return file_map |
| 95 | 96 |
| 96 | 97 |
| 97 def GetDeviceLogs(log_filenames, logger): | 98 def GetDeviceLogs(log_filenames, logger): |
| 98 """Read log files, combine and format. | 99 """Read log files, combine and format. |
| 99 | 100 |
| 100 Args: | 101 Args: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 142 logger.info('Waiting for logcat process to terminate.') | 143 logger.info('Waiting for logcat process to terminate.') |
| 143 i += 1 | 144 i += 1 |
| 144 if i >= 10: | 145 if i >= 10: |
| 145 logger.warning('Monitor pid did not terminate. Continuing anyway.') | 146 logger.warning('Monitor pid did not terminate. Continuing anyway.') |
| 146 return | 147 return |
| 147 | 148 |
| 148 except (ValueError, IOError, OSError): | 149 except (ValueError, IOError, OSError): |
| 149 logger.exception('Error signaling logcat monitor - continuing') | 150 logger.exception('Error signaling logcat monitor - continuing') |
| 150 | 151 |
| 151 | 152 |
| 152 def main(base_dir, output_file): | 153 def main(argv): |
| 154 parser = optparse.OptionParser(usage='Usage: %prog [options] <log dir>') | |
|
bulach
2013/11/14 19:50:04
nit: while at it, it seems a bit confusing to have
Isaac (away)
2013/11/21 11:11:34
--output-path is optional, while the extra arg is
| |
| 155 parser.add_option('--output-path', | |
| 156 help='Output file path (if unspecified, prints to stdout)') | |
| 157 options, args = parser.parse_args(argv) | |
| 158 if len(args) != 1: | |
| 159 parser.error('Wrong number of unparsed args') | |
| 160 base_dir = args[0] | |
| 161 if options.output_path: | |
| 162 output_file = open(options.output_path, 'w') | |
| 163 else: | |
| 164 output_file = sys.stdout | |
| 165 | |
| 153 log_stringio = cStringIO.StringIO() | 166 log_stringio = cStringIO.StringIO() |
| 154 logger = logging.getLogger('LogcatPrinter') | 167 logger = logging.getLogger('LogcatPrinter') |
| 155 logger.setLevel(LOG_LEVEL) | 168 logger.setLevel(LOG_LEVEL) |
| 156 sh = logging.StreamHandler(log_stringio) | 169 sh = logging.StreamHandler(log_stringio) |
| 157 sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s' | 170 sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s' |
| 158 ' %(message)s')) | 171 ' %(message)s')) |
| 159 logger.addHandler(sh) | 172 logger.addHandler(sh) |
| 160 | 173 |
| 161 try: | 174 try: |
| 162 # Wait at least 5 seconds after base_dir is created before printing. | 175 # Wait at least 5 seconds after base_dir is created before printing. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 189 output_file.write(f.read()) | 202 output_file.write(f.read()) |
| 190 except: | 203 except: |
| 191 logger.exception('Unexpected exception') | 204 logger.exception('Unexpected exception') |
| 192 | 205 |
| 193 logger.info('Done.') | 206 logger.info('Done.') |
| 194 sh.flush() | 207 sh.flush() |
| 195 output_file.write('\nLogcat Printer Event Log\n') | 208 output_file.write('\nLogcat Printer Event Log\n') |
| 196 output_file.write(log_stringio.getvalue()) | 209 output_file.write(log_stringio.getvalue()) |
| 197 | 210 |
| 198 if __name__ == '__main__': | 211 if __name__ == '__main__': |
| 199 if len(sys.argv) == 1: | 212 sys.exit(main(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)) | |
| OLD | NEW |