Home | History | Annotate | Download | only in logblame
      1 
      2 import datetime
      3 import re
      4 
      5 BUFFER_BEGIN = re.compile("^--------- beginning of (.*)$")
      6 BUFFER_SWITCH = re.compile("^--------- switch to (.*)$")
      7 HEADER = re.compile("^\\[ (\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d.\\d\\d\\d) +(.+?): *(\\d+): *(\\d+) *([EWIDV])/(.*?) *\\]$")
      8 HEADER_TYPE2 = re.compile("^(\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d.\\d\\d\\d) *(\\d+) *(\\d+) *([EWIDV]) ([^ :]*?): (.*?)$")
      9 CHATTY_IDENTICAL = re.compile("^.* identical (\\d+) lines$")
     10 
     11 STATE_BEGIN = 0
     12 STATE_BUFFER = 1
     13 STATE_HEADER = 2
     14 STATE_TEXT = 3
     15 STATE_BLANK = 4
     16 
     17 class LogLine(object):
     18   """Represents a line of android logs."""
     19   def __init__(self, buf=None, timestamp=None, uid=None, pid=None, tid=None, level=None,
     20       tag=None, text=""):
     21     self.buf = buf
     22     self.timestamp = timestamp
     23     self.uid = uid
     24     self.pid = pid
     25     self.tid = tid
     26     self.level = level
     27     self.tag = tag
     28     self.text = text
     29     self.process = None
     30 
     31   def __str__(self):
     32     return "{%s} {%s} {%s} {%s} {%s} {%s}/{%s}: {%s}" % (self.buf, self.timestamp, self.uid,
     33         self.pid, self.tid, self.level, self.tag, self.text)
     34 
     35   def __eq__(self, other):
     36       return (
     37             self.buf == other.buf
     38             and self.timestamp == other.timestamp 
     39             and self.uid == other.uid 
     40             and self.pid == other.pid 
     41             and self.tid == other.tid 
     42             and self.level == other.level 
     43             and self.tag == other.tag 
     44             and self.text == other.text 
     45           )
     46 
     47   def clone(self):
     48     logLine = LogLine(self.buf, self.timestamp, self.uid, self.pid, self.tid, self.level,
     49         self.tag, self.text)
     50     logLine.process = self.process
     51     return logLine
     52 
     53   def memory(self):
     54     """Return an estimate of how much memory is used for the log.
     55       32 bytes of header + 8 bytes for the pointer + the length of the tag and the text.
     56       This ignores the overhead of the list of log lines itself."""
     57     return 32 + 8 + len(self.tag) + 1 + len(self.text) + 1
     58 
     59 
     60 def ParseLogcat(f, processes, duration=None):
     61   previous = None
     62   for logLine in ParseLogcatInner(f, processes, duration):
     63     if logLine.tag == "chatty" and logLine.level == "I":
     64       m = CHATTY_IDENTICAL.match(logLine.text)
     65       if m:
     66         for i in range(int(m.group(1))):
     67           clone = previous.clone()
     68           clone.timestamp = logLine.timestamp
     69           yield clone
     70         continue
     71     previous = logLine
     72     yield logLine
     73 
     74 
     75 def ParseLogcatInner(f, processes, duration=None):
     76   """Parses a file object containing log text and returns a list of LogLine objects."""
     77   result = []
     78 
     79   buf = None
     80   timestamp = None
     81   uid = None
     82   pid = None
     83   tid = None
     84   level = None
     85   tag = None
     86 
     87   state = STATE_BEGIN
     88   logLine = None
     89   previous = None
     90 
     91   if duration:
     92     endTime = datetime.datetime.now() + datetime.timedelta(seconds=duration)
     93 
     94   # TODO: use a nonblocking / timeout read so we stop if there are
     95   # no logs coming out (haha joke, right!)
     96   for line in f:
     97     if duration and endTime <= datetime.datetime.now():
     98       break
     99 
    100     if len(line) > 0 and line[-1] == '\n':
    101       line = line[0:-1]
    102 
    103     m = BUFFER_BEGIN.match(line)
    104     if m:
    105       if logLine:
    106         yield logLine
    107         logLine = None
    108       buf = m.group(1)
    109       state = STATE_BUFFER
    110       continue
    111 
    112     m = BUFFER_SWITCH.match(line)
    113     if m:
    114       if logLine:
    115         yield logLine
    116         logLine = None
    117       buf = m.group(1)
    118       state = STATE_BUFFER
    119       continue
    120 
    121     m = HEADER.match(line)
    122     if m:
    123       if logLine:
    124         yield logLine
    125       logLine = LogLine(
    126             buf=buf,
    127             timestamp=m.group(1),
    128             uid=m.group(2),
    129             pid=m.group(3),
    130             tid=m.group(4),
    131             level=m.group(5),
    132             tag=m.group(6)
    133           )
    134       previous = logLine
    135       logLine.process = processes.FindPid(logLine.pid, logLine.uid)
    136       state = STATE_HEADER
    137       continue
    138 
    139     m = HEADER_TYPE2.match(line)
    140     if m:
    141       if logLine:
    142         yield logLine
    143       logLine = LogLine(
    144             buf=buf,
    145             timestamp=m.group(1),
    146             uid="0",
    147             pid=m.group(2),
    148             tid=m.group(3),
    149             level=m.group(4),
    150             tag=m.group(5),
    151             text=m.group(6)
    152           )
    153       previous = logLine
    154       logLine.process = processes.FindPid(logLine.pid, logLine.uid)
    155       state = STATE_BEGIN
    156       continue
    157 
    158     if not len(line):
    159       if state == STATE_BLANK:
    160         if logLine:
    161           logLine.text += "\n"
    162       state = STATE_BLANK
    163       continue
    164 
    165     if logLine:
    166       if state == STATE_HEADER:
    167         logLine.text += line
    168       elif state == STATE_TEXT:
    169         logLine.text += "\n"
    170         logLine.text += line
    171       elif state == STATE_BLANK:
    172         if len(logLine.text):
    173           logLine.text += "\n"
    174         logLine.text += "\n"
    175         logLine.text += line
    176     state = STATE_TEXT
    177 
    178   if logLine:
    179     yield logLine
    180 
    181 
    182 # vim: set ts=2 sw=2 sts=2 tw=100 nocindent autoindent smartindent expandtab:
    183