Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """"Processes a log file and resolves IPC message identifiers.
      7 
      8 Resolves IPC messages of the form [unknown type NNNNNN] to named IPC messages.
      9 
     10 e.g. logfile containing
     11 
     12 I/stderr  ( 3915): ipc 3915.3.1370207904 2147483647 S [unknown type 66372]
     13 
     14 will be transformed to:
     15 
     16 I/stderr  ( 3915): ipc 3915.3.1370207904 2147483647 S ViewMsg_SetCSSColors
     17 
     18 In order to find the message header files efficiently, it requires that
     19 Chromium is checked out using git.
     20 """
     21 
     22 import optparse
     23 import os
     24 import re
     25 import subprocess
     26 import sys
     27 
     28 
     29 def _SourceDir():
     30   """Get chromium's source directory."""
     31   return os.path.join(sys.path[0], '..')
     32 
     33 
     34 def _ReadLines(f):
     35   """Read from file f and generate right-stripped lines."""
     36   for line in f:
     37     yield line.rstrip()
     38 
     39 
     40 def _GetMsgStartTable():
     41   """Read MsgStart enumeration from ipc/ipc_message_utils.h.
     42 
     43   Determines the message type identifiers by reading.
     44   header file ipc/ipc_message_utils.h and looking for
     45   enum IPCMessageStart.  Assumes following code format in header file:
     46   enum IPCMessageStart {
     47      Type1MsgStart ...,
     48      Type2MsgStart,
     49   };
     50 
     51   Returns:
     52       A dictionary mapping StartName to enumeration value.
     53   """
     54   ipc_message_file = _SourceDir() + '/ipc/ipc_message_utils.h'
     55   ipc_message_lines = _ReadLines(open(ipc_message_file))
     56   is_msg_start = False
     57   count = 0
     58   msg_start_table = dict()
     59   for line in ipc_message_lines:
     60     if is_msg_start:
     61       if line.strip() == '};':
     62         break
     63       msgstart_index = line.find('MsgStart')
     64       msg_type = line[:msgstart_index] + 'MsgStart'
     65       msg_start_table[msg_type.strip()] = count
     66       count+=1
     67     elif line.strip() == 'enum IPCMessageStart {':
     68       is_msg_start = True
     69 
     70   return msg_start_table
     71 
     72 
     73 def _FindMessageHeaderFiles():
     74   """Look through the source directory for *_messages.h."""
     75   os.chdir(_SourceDir())
     76   pipe = subprocess.Popen(['git', 'ls-files', '--', '*_messages.h'],
     77                           stdout=subprocess.PIPE)
     78   return _ReadLines(pipe.stdout)
     79 
     80 
     81 def _GetMsgId(msg_start, line_number, msg_start_table):
     82   """Construct the meessage id given the msg_start and the line number."""
     83   hex_str = '%x%04x' % (msg_start_table[msg_start], line_number)
     84   return int(hex_str, 16)
     85 
     86 
     87 def _ReadHeaderFile(f, msg_start_table, msg_map):
     88   """Read a header file and construct a map from message_id to message name."""
     89   msg_def_re = re.compile(
     90       '^IPC_(?:SYNC_)?MESSAGE_[A-Z0-9_]+\(([A-Za-z0-9_]+).*')
     91   msg_start_re = re.compile(
     92       '^\s*#define\s+IPC_MESSAGE_START\s+([a-zA-Z0-9_]+MsgStart).*')
     93   msg_start = None
     94   msg_name = None
     95   line_number = 0
     96 
     97   for line in f:
     98     line_number+=1
     99     match = re.match(msg_start_re, line)
    100     if match:
    101       msg_start = match.group(1)
    102       # print "msg_start = " + msg_start
    103     match = re.match(msg_def_re, line)
    104     if match:
    105       msg_name = match.group(1)
    106       # print "msg_name = " + msg_name
    107     if msg_start and msg_name:
    108       msg_id = _GetMsgId(msg_start, line_number, msg_start_table)
    109       msg_map[msg_id] = msg_name
    110   return msg_map
    111 
    112 
    113 def _ResolveMsg(msg_type, msg_map):
    114   """Fully resolve a message type to a name."""
    115   if msg_type in msg_map:
    116     return msg_map[msg_type]
    117   else:
    118     return '[Unknown message %d (0x%x)]x' % (msg_type, msg_type)
    119 
    120 
    121 def _ProcessLog(f, msg_map):
    122   """Read lines from f and resolve the IPC messages according to msg_map."""
    123   unknown_msg_re = re.compile('\[unknown type (\d+)\]')
    124   for line in f:
    125     line = line.rstrip()
    126     match = re.search(unknown_msg_re, line)
    127     if match:
    128       line = re.sub(unknown_msg_re,
    129                     _ResolveMsg(int(match.group(1)), msg_map),
    130                     line)
    131     print line
    132 
    133 
    134 def _GetMsgMap():
    135   """Returns a dictionary mapping from message number to message name."""
    136   msg_start_table = _GetMsgStartTable()
    137   msg_map = dict()
    138   for header_file in _FindMessageHeaderFiles():
    139     _ReadHeaderFile(open(header_file),
    140                     msg_start_table,
    141                     msg_map)
    142   return msg_map
    143 
    144 
    145 def main():
    146   """Processes one or more log files with IPC logging messages.
    147 
    148      Replaces '[unknown type NNNNNN]' with resolved
    149      IPC messages.
    150 
    151      Reads from standard input if no log files specified on the
    152      command line.
    153   """
    154   parser = optparse.OptionParser('usage: %prog [LOGFILE...]')
    155   (_, args) = parser.parse_args()
    156 
    157   msg_map = _GetMsgMap()
    158   log_files = args
    159 
    160   if log_files:
    161     for log_file in log_files:
    162       _ProcessLog(open(log_file), msg_map)
    163   else:
    164     _ProcessLog(sys.stdin, msg_map)
    165 
    166 
    167 if __name__ == '__main__':
    168   main()
    169