blob: 6982e06bbe97e25157135bfaa4a1803559fb5a50 [file] [log] [blame]
Joe Onorato75f444e2017-04-01 16:26:17 -07001
2import datetime
3import re
4
5BUFFER_BEGIN = re.compile("^--------- beginning of (.*)$")
6BUFFER_SWITCH = re.compile("^--------- switch to (.*)$")
7HEADER = re.compile("^\\[ (\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d.\\d\\d\\d) +(.+?): *(\\d+): *(\\d+) *([EWIDV])/(.*?) *\\]$")
Mark Salyzyn6d1633e2017-07-13 11:33:12 -07008HEADER_TYPE2 = re.compile("^(\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d.\\d\\d\\d) *(\\d+) *(\\d+) *([EWIDV]) ([^ :]*?): (.*?)$")
Joe Onorato75f444e2017-04-01 16:26:17 -07009CHATTY_IDENTICAL = re.compile("^.* identical (\\d+) lines$")
10
11STATE_BEGIN = 0
12STATE_BUFFER = 1
13STATE_HEADER = 2
14STATE_TEXT = 3
15STATE_BLANK = 4
16
17class 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
60def 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
75def 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
Mark Salyzyn6d1633e2017-07-13 11:33:12 -0700139 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
Joe Onorato75f444e2017-04-01 16:26:17 -0700158 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: