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