| #!/usr/bin/python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """"Processes a log file and resolves IPC message identifiers. |
| |
| Resolves IPC messages of the form [unknown type NNNNNN] to named IPC messages. |
| |
| e.g. logfile containing |
| |
| I/stderr ( 3915): ipc 3915.3.1370207904 2147483647 S [unknown type 66372] |
| |
| will be transformed to: |
| |
| I/stderr ( 3915): ipc 3915.3.1370207904 2147483647 S ViewMsg_SetCSSColors |
| |
| In order to find the message header files efficiently, it requires that |
| Chromium is checked out using git. |
| """ |
| |
| import optparse |
| import os |
| import re |
| import subprocess |
| import sys |
| |
| |
| def _SourceDir(): |
| """Get chromium's source directory.""" |
| return os.path.join(sys.path[0], '..') |
| |
| |
| def _ReadLines(f): |
| """Read from file f and generate right-stripped lines.""" |
| for line in f: |
| yield line.rstrip() |
| |
| |
| def _GetMsgStartTable(): |
| """Read MsgStart enumeration from ipc/ipc_message_utils.h. |
| |
| Determines the message type identifiers by reading. |
| header file ipc/ipc_message_utils.h and looking for |
| enum IPCMessageStart. Assumes following code format in header file: |
| enum IPCMessageStart { |
| Type1MsgStart ..., |
| Type2MsgStart, |
| }; |
| |
| Returns: |
| A dictionary mapping StartName to enumeration value. |
| """ |
| ipc_message_file = _SourceDir() + '/ipc/ipc_message_utils.h' |
| ipc_message_lines = _ReadLines(open(ipc_message_file)) |
| is_msg_start = False |
| count = 0 |
| msg_start_table = dict() |
| for line in ipc_message_lines: |
| if is_msg_start: |
| if line.strip() == '};': |
| break |
| msgstart_index = line.find('MsgStart') |
| msg_type = line[:msgstart_index] + 'MsgStart' |
| msg_start_table[msg_type.strip()] = count |
| count+=1 |
| elif line.strip() == 'enum IPCMessageStart {': |
| is_msg_start = True |
| |
| return msg_start_table |
| |
| |
| def _FindMessageHeaderFiles(): |
| """Look through the source directory for *_messages.h.""" |
| os.chdir(_SourceDir()) |
| pipe = subprocess.Popen(['git', 'ls-files', '--', '*_messages.h'], |
| stdout=subprocess.PIPE) |
| return _ReadLines(pipe.stdout) |
| |
| |
| def _GetMsgId(msg_start, line_number, msg_start_table): |
| """Construct the meessage id given the msg_start and the line number.""" |
| hex_str = '%x%04x' % (msg_start_table[msg_start], line_number) |
| return int(hex_str, 16) |
| |
| |
| def _ReadHeaderFile(f, msg_start_table, msg_map): |
| """Read a header file and construct a map from message_id to message name.""" |
| msg_def_re = re.compile( |
| '^IPC_(?:SYNC_)?MESSAGE_[A-Z0-9_]+\(([A-Za-z0-9_]+).*') |
| msg_start_re = re.compile( |
| '^\s*#define\s+IPC_MESSAGE_START\s+([a-zA-Z0-9_]+MsgStart).*') |
| msg_start = None |
| msg_name = None |
| line_number = 0 |
| |
| for line in f: |
| line_number+=1 |
| match = re.match(msg_start_re, line) |
| if match: |
| msg_start = match.group(1) |
| # print "msg_start = " + msg_start |
| match = re.match(msg_def_re, line) |
| if match: |
| msg_name = match.group(1) |
| # print "msg_name = " + msg_name |
| if msg_start and msg_name: |
| msg_id = _GetMsgId(msg_start, line_number, msg_start_table) |
| msg_map[msg_id] = msg_name |
| return msg_map |
| |
| |
| def _ResolveMsg(msg_type, msg_map): |
| """Fully resolve a message type to a name.""" |
| if msg_type in msg_map: |
| return msg_map[msg_type] |
| else: |
| return '[Unknown message %d (0x%x)]x' % (msg_type, msg_type) |
| |
| |
| def _ProcessLog(f, msg_map): |
| """Read lines from f and resolve the IPC messages according to msg_map.""" |
| unknown_msg_re = re.compile('\[unknown type (\d+)\]') |
| for line in f: |
| line = line.rstrip() |
| match = re.search(unknown_msg_re, line) |
| if match: |
| line = re.sub(unknown_msg_re, |
| _ResolveMsg(int(match.group(1)), msg_map), |
| line) |
| print line |
| |
| |
| def _GetMsgMap(): |
| """Returns a dictionary mapping from message number to message name.""" |
| msg_start_table = _GetMsgStartTable() |
| msg_map = dict() |
| for header_file in _FindMessageHeaderFiles(): |
| _ReadHeaderFile(open(header_file), |
| msg_start_table, |
| msg_map) |
| return msg_map |
| |
| |
| def main(): |
| """Processes one or more log files with IPC logging messages. |
| |
| Replaces '[unknown type NNNNNN]' with resolved |
| IPC messages. |
| |
| Reads from standard input if no log files specified on the |
| command line. |
| """ |
| parser = optparse.OptionParser('usage: %prog [LOGFILE...]') |
| (_, args) = parser.parse_args() |
| |
| msg_map = _GetMsgMap() |
| log_files = args |
| |
| if log_files: |
| for log_file in log_files: |
| _ProcessLog(open(log_file), msg_map) |
| else: |
| _ProcessLog(sys.stdin, msg_map) |
| |
| |
| if __name__ == '__main__': |
| main() |