xref: /aosp_15_r20/development/tools/logblame/analyze_logs.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python2.7
2*90c8c64dSAndroid Build Coastguard Worker
3*90c8c64dSAndroid Build Coastguard Workerimport argparse
4*90c8c64dSAndroid Build Coastguard Workerimport datetime
5*90c8c64dSAndroid Build Coastguard Workerimport re
6*90c8c64dSAndroid Build Coastguard Workerimport subprocess
7*90c8c64dSAndroid Build Coastguard Workerimport sys
8*90c8c64dSAndroid Build Coastguard Worker
9*90c8c64dSAndroid Build Coastguard Workerimport logs
10*90c8c64dSAndroid Build Coastguard Workerimport ps
11*90c8c64dSAndroid Build Coastguard Worker
12*90c8c64dSAndroid Build Coastguard WorkerDURATION_RE = re.compile("((\\d+)w)?((\\d+)d)?((\\d+)h)?((\\d+)m)?((\\d+)s)?")
13*90c8c64dSAndroid Build Coastguard Worker
14*90c8c64dSAndroid Build Coastguard Workerclass Bucket(object):
15*90c8c64dSAndroid Build Coastguard Worker  """Bucket of stats for a particular key managed by the Stats object."""
16*90c8c64dSAndroid Build Coastguard Worker  def __init__(self):
17*90c8c64dSAndroid Build Coastguard Worker    self.count = 0
18*90c8c64dSAndroid Build Coastguard Worker    self.memory = 0
19*90c8c64dSAndroid Build Coastguard Worker    self.lines = []
20*90c8c64dSAndroid Build Coastguard Worker
21*90c8c64dSAndroid Build Coastguard Worker  def __str__(self):
22*90c8c64dSAndroid Build Coastguard Worker    return "(%s,%s)" % (self.count, self.memory)
23*90c8c64dSAndroid Build Coastguard Worker
24*90c8c64dSAndroid Build Coastguard Worker
25*90c8c64dSAndroid Build Coastguard Workerclass Stats(object):
26*90c8c64dSAndroid Build Coastguard Worker  """A group of stats with a particular key, where both memory and count are tracked."""
27*90c8c64dSAndroid Build Coastguard Worker  def __init__(self):
28*90c8c64dSAndroid Build Coastguard Worker    self._data = dict()
29*90c8c64dSAndroid Build Coastguard Worker
30*90c8c64dSAndroid Build Coastguard Worker  def add(self, key, logLine):
31*90c8c64dSAndroid Build Coastguard Worker    bucket = self._data.get(key)
32*90c8c64dSAndroid Build Coastguard Worker    if not bucket:
33*90c8c64dSAndroid Build Coastguard Worker      bucket = Bucket()
34*90c8c64dSAndroid Build Coastguard Worker      self._data[key] = bucket
35*90c8c64dSAndroid Build Coastguard Worker    bucket.count += 1
36*90c8c64dSAndroid Build Coastguard Worker    bucket.memory += logLine.memory()
37*90c8c64dSAndroid Build Coastguard Worker    bucket.lines.append(logLine)
38*90c8c64dSAndroid Build Coastguard Worker
39*90c8c64dSAndroid Build Coastguard Worker  def __iter__(self):
40*90c8c64dSAndroid Build Coastguard Worker    return self._data.iteritems()
41*90c8c64dSAndroid Build Coastguard Worker
42*90c8c64dSAndroid Build Coastguard Worker  def data(self):
43*90c8c64dSAndroid Build Coastguard Worker    return [(key, bucket) for key, bucket in self._data.iteritems()]
44*90c8c64dSAndroid Build Coastguard Worker
45*90c8c64dSAndroid Build Coastguard Worker  def byCount(self):
46*90c8c64dSAndroid Build Coastguard Worker    result = self.data()
47*90c8c64dSAndroid Build Coastguard Worker    result.sort(lambda a, b: -cmp(a[1].count, b[1].count))
48*90c8c64dSAndroid Build Coastguard Worker    return result
49*90c8c64dSAndroid Build Coastguard Worker
50*90c8c64dSAndroid Build Coastguard Worker  def byMemory(self):
51*90c8c64dSAndroid Build Coastguard Worker    result = self.data()
52*90c8c64dSAndroid Build Coastguard Worker    result.sort(lambda a, b: -cmp(a[1].memory, b[1].memory))
53*90c8c64dSAndroid Build Coastguard Worker    return result
54*90c8c64dSAndroid Build Coastguard Worker
55*90c8c64dSAndroid Build Coastguard Worker
56*90c8c64dSAndroid Build Coastguard Workerdef ParseDuration(s):
57*90c8c64dSAndroid Build Coastguard Worker  """Parse a date of the format .w.d.h.m.s into the number of seconds."""
58*90c8c64dSAndroid Build Coastguard Worker  def make_int(index):
59*90c8c64dSAndroid Build Coastguard Worker    val = m.group(index)
60*90c8c64dSAndroid Build Coastguard Worker    if val:
61*90c8c64dSAndroid Build Coastguard Worker      return int(val)
62*90c8c64dSAndroid Build Coastguard Worker    else:
63*90c8c64dSAndroid Build Coastguard Worker      return 0
64*90c8c64dSAndroid Build Coastguard Worker  m = DURATION_RE.match(s)
65*90c8c64dSAndroid Build Coastguard Worker  if m:
66*90c8c64dSAndroid Build Coastguard Worker    weeks = make_int(2)
67*90c8c64dSAndroid Build Coastguard Worker    days = make_int(4)
68*90c8c64dSAndroid Build Coastguard Worker    hours = make_int(6)
69*90c8c64dSAndroid Build Coastguard Worker    minutes = make_int(8)
70*90c8c64dSAndroid Build Coastguard Worker    seconds = make_int(10)
71*90c8c64dSAndroid Build Coastguard Worker    return (weeks * 604800) + (days * 86400) + (hours * 3600) + (minutes * 60) + seconds
72*90c8c64dSAndroid Build Coastguard Worker  return 0
73*90c8c64dSAndroid Build Coastguard Worker
74*90c8c64dSAndroid Build Coastguard Workerdef FormatMemory(n):
75*90c8c64dSAndroid Build Coastguard Worker  """Prettify the number of bytes into gb, mb, etc."""
76*90c8c64dSAndroid Build Coastguard Worker  if n >= 1024 * 1024 * 1024:
77*90c8c64dSAndroid Build Coastguard Worker    return "%10d gb" % (n / (1024 * 1024 * 1024))
78*90c8c64dSAndroid Build Coastguard Worker  elif n >= 1024 * 1024:
79*90c8c64dSAndroid Build Coastguard Worker    return "%10d mb" % (n / (1024 * 1024))
80*90c8c64dSAndroid Build Coastguard Worker  elif n >= 1024:
81*90c8c64dSAndroid Build Coastguard Worker    return "%10d kb" % (n / 1024)
82*90c8c64dSAndroid Build Coastguard Worker  else:
83*90c8c64dSAndroid Build Coastguard Worker    return "%10d b " % n
84*90c8c64dSAndroid Build Coastguard Worker
85*90c8c64dSAndroid Build Coastguard Workerdef FormateTimeDelta(td):
86*90c8c64dSAndroid Build Coastguard Worker  """Format a time duration into the same format we accept on the commandline."""
87*90c8c64dSAndroid Build Coastguard Worker  seconds = (td.days * 86400) + (td.seconds) + int(td.microseconds / 1000000)
88*90c8c64dSAndroid Build Coastguard Worker  if seconds == 0:
89*90c8c64dSAndroid Build Coastguard Worker    return "0s"
90*90c8c64dSAndroid Build Coastguard Worker  result = ""
91*90c8c64dSAndroid Build Coastguard Worker  if seconds >= 604800:
92*90c8c64dSAndroid Build Coastguard Worker    weeks = int(seconds / 604800)
93*90c8c64dSAndroid Build Coastguard Worker    seconds -= weeks * 604800
94*90c8c64dSAndroid Build Coastguard Worker    result += "%dw" % weeks
95*90c8c64dSAndroid Build Coastguard Worker  if seconds >= 86400:
96*90c8c64dSAndroid Build Coastguard Worker    days = int(seconds / 86400)
97*90c8c64dSAndroid Build Coastguard Worker    seconds -= days * 86400
98*90c8c64dSAndroid Build Coastguard Worker    result += "%dd" % days
99*90c8c64dSAndroid Build Coastguard Worker  if seconds >= 3600:
100*90c8c64dSAndroid Build Coastguard Worker    hours = int(seconds / 3600)
101*90c8c64dSAndroid Build Coastguard Worker    seconds -= hours * 3600
102*90c8c64dSAndroid Build Coastguard Worker    result += "%dh" % hours
103*90c8c64dSAndroid Build Coastguard Worker  if seconds >= 60:
104*90c8c64dSAndroid Build Coastguard Worker    minutes = int(seconds / 60)
105*90c8c64dSAndroid Build Coastguard Worker    seconds -= minutes * 60
106*90c8c64dSAndroid Build Coastguard Worker    result += "%dm" % minutes
107*90c8c64dSAndroid Build Coastguard Worker  if seconds > 0:
108*90c8c64dSAndroid Build Coastguard Worker    result += "%ds" % seconds
109*90c8c64dSAndroid Build Coastguard Worker  return result
110*90c8c64dSAndroid Build Coastguard Worker
111*90c8c64dSAndroid Build Coastguard Worker
112*90c8c64dSAndroid Build Coastguard Workerdef WriteResult(totalCount, totalMemory, bucket, text):
113*90c8c64dSAndroid Build Coastguard Worker  """Write a bucket in the normalized format."""
114*90c8c64dSAndroid Build Coastguard Worker  print "%7d (%2d%%) %s (%2d%%)  %s" % (bucket.count, (100 * bucket.count / totalCount),
115*90c8c64dSAndroid Build Coastguard Worker      FormatMemory(bucket.memory), (100 * bucket.memory / totalMemory), text)
116*90c8c64dSAndroid Build Coastguard Worker
117*90c8c64dSAndroid Build Coastguard Worker
118*90c8c64dSAndroid Build Coastguard Workerdef ParseArgs(argv):
119*90c8c64dSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description="Process some integers.")
120*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("input", type=str, nargs="?",
121*90c8c64dSAndroid Build Coastguard Worker                      help="the logs file to read")
122*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("--clear", action="store_true",
123*90c8c64dSAndroid Build Coastguard Worker                      help="clear the log buffer before running logcat")
124*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("--duration", type=str, nargs=1,
125*90c8c64dSAndroid Build Coastguard Worker                      help="how long to run for (XdXhXmXs)")
126*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("--rawlogs", type=str, nargs=1,
127*90c8c64dSAndroid Build Coastguard Worker                      help="file to put the rawlogs into")
128*90c8c64dSAndroid Build Coastguard Worker
129*90c8c64dSAndroid Build Coastguard Worker  args = parser.parse_args()
130*90c8c64dSAndroid Build Coastguard Worker
131*90c8c64dSAndroid Build Coastguard Worker  args.durationSec = ParseDuration(args.duration[0]) if args.duration else 0
132*90c8c64dSAndroid Build Coastguard Worker
133*90c8c64dSAndroid Build Coastguard Worker  return args
134*90c8c64dSAndroid Build Coastguard Worker
135*90c8c64dSAndroid Build Coastguard Worker
136*90c8c64dSAndroid Build Coastguard Workerdef main(argv):
137*90c8c64dSAndroid Build Coastguard Worker  args = ParseArgs(argv)
138*90c8c64dSAndroid Build Coastguard Worker
139*90c8c64dSAndroid Build Coastguard Worker  processes = ps.ProcessSet()
140*90c8c64dSAndroid Build Coastguard Worker
141*90c8c64dSAndroid Build Coastguard Worker  if args.rawlogs:
142*90c8c64dSAndroid Build Coastguard Worker    rawlogs = file(args.rawlogs[0], "w")
143*90c8c64dSAndroid Build Coastguard Worker  else:
144*90c8c64dSAndroid Build Coastguard Worker    rawlogs = None
145*90c8c64dSAndroid Build Coastguard Worker
146*90c8c64dSAndroid Build Coastguard Worker  # Choose the input
147*90c8c64dSAndroid Build Coastguard Worker  if args.input:
148*90c8c64dSAndroid Build Coastguard Worker    # From a file of raw logs
149*90c8c64dSAndroid Build Coastguard Worker    try:
150*90c8c64dSAndroid Build Coastguard Worker      infile = file(args.input, "r")
151*90c8c64dSAndroid Build Coastguard Worker    except IOError:
152*90c8c64dSAndroid Build Coastguard Worker      sys.stderr.write("Error opening file for read: %s\n" % args.input[0])
153*90c8c64dSAndroid Build Coastguard Worker      sys.exit(1)
154*90c8c64dSAndroid Build Coastguard Worker  else:
155*90c8c64dSAndroid Build Coastguard Worker    # From running adb logcat on an attached device
156*90c8c64dSAndroid Build Coastguard Worker    if args.clear:
157*90c8c64dSAndroid Build Coastguard Worker      subprocess.check_call(["adb", "logcat", "-c"])
158*90c8c64dSAndroid Build Coastguard Worker    cmd = ["adb", "logcat", "-v", "long", "-D", "-v", "uid"]
159*90c8c64dSAndroid Build Coastguard Worker    if not args.durationSec:
160*90c8c64dSAndroid Build Coastguard Worker      cmd.append("-d")
161*90c8c64dSAndroid Build Coastguard Worker    logcat = subprocess.Popen(cmd, stdout=subprocess.PIPE)
162*90c8c64dSAndroid Build Coastguard Worker    infile = logcat.stdout
163*90c8c64dSAndroid Build Coastguard Worker
164*90c8c64dSAndroid Build Coastguard Worker    # Do one update because we know we'll need it, but then don't do it again
165*90c8c64dSAndroid Build Coastguard Worker    # if we're not streaming them.
166*90c8c64dSAndroid Build Coastguard Worker    processes.Update(True)
167*90c8c64dSAndroid Build Coastguard Worker    if args.durationSec:
168*90c8c64dSAndroid Build Coastguard Worker      processes.doUpdates = True
169*90c8c64dSAndroid Build Coastguard Worker
170*90c8c64dSAndroid Build Coastguard Worker  totalCount = 0
171*90c8c64dSAndroid Build Coastguard Worker  totalMemory = 0
172*90c8c64dSAndroid Build Coastguard Worker  byTag = Stats()
173*90c8c64dSAndroid Build Coastguard Worker  byPid = Stats()
174*90c8c64dSAndroid Build Coastguard Worker  byText = Stats()
175*90c8c64dSAndroid Build Coastguard Worker
176*90c8c64dSAndroid Build Coastguard Worker  startTime = datetime.datetime.now()
177*90c8c64dSAndroid Build Coastguard Worker
178*90c8c64dSAndroid Build Coastguard Worker  # Read the log lines from the parser and build a big mapping of everything
179*90c8c64dSAndroid Build Coastguard Worker  for logLine in logs.ParseLogcat(infile, processes, args.durationSec):
180*90c8c64dSAndroid Build Coastguard Worker    if rawlogs:
181*90c8c64dSAndroid Build Coastguard Worker      rawlogs.write("%-10s %s %-6s %-6s %-6s %s/%s: %s\n" %(logLine.buf, logLine.timestamp,
182*90c8c64dSAndroid Build Coastguard Worker          logLine.uid, logLine.pid, logLine.tid, logLine.level, logLine.tag, logLine.text))
183*90c8c64dSAndroid Build Coastguard Worker
184*90c8c64dSAndroid Build Coastguard Worker    totalCount += 1
185*90c8c64dSAndroid Build Coastguard Worker    totalMemory += logLine.memory()
186*90c8c64dSAndroid Build Coastguard Worker    byTag.add(logLine.tag, logLine)
187*90c8c64dSAndroid Build Coastguard Worker    byPid.add(logLine.pid, logLine)
188*90c8c64dSAndroid Build Coastguard Worker    byText.add(logLine.text, logLine)
189*90c8c64dSAndroid Build Coastguard Worker
190*90c8c64dSAndroid Build Coastguard Worker  endTime = datetime.datetime.now()
191*90c8c64dSAndroid Build Coastguard Worker
192*90c8c64dSAndroid Build Coastguard Worker  # Print the log analysis
193*90c8c64dSAndroid Build Coastguard Worker
194*90c8c64dSAndroid Build Coastguard Worker  # At this point, everything is loaded, don't bother looking
195*90c8c64dSAndroid Build Coastguard Worker  # for new processes
196*90c8c64dSAndroid Build Coastguard Worker  processes.doUpdates = False
197*90c8c64dSAndroid Build Coastguard Worker
198*90c8c64dSAndroid Build Coastguard Worker  print "Top tags by count"
199*90c8c64dSAndroid Build Coastguard Worker  print "-----------------"
200*90c8c64dSAndroid Build Coastguard Worker  i = 0
201*90c8c64dSAndroid Build Coastguard Worker  for k,v in byTag.byCount():
202*90c8c64dSAndroid Build Coastguard Worker    WriteResult(totalCount, totalMemory, v, k)
203*90c8c64dSAndroid Build Coastguard Worker    if i >= 10:
204*90c8c64dSAndroid Build Coastguard Worker      break
205*90c8c64dSAndroid Build Coastguard Worker    i += 1
206*90c8c64dSAndroid Build Coastguard Worker
207*90c8c64dSAndroid Build Coastguard Worker  print
208*90c8c64dSAndroid Build Coastguard Worker  print "Top tags by memory"
209*90c8c64dSAndroid Build Coastguard Worker  print "------------------"
210*90c8c64dSAndroid Build Coastguard Worker  i = 0
211*90c8c64dSAndroid Build Coastguard Worker  for k,v in byTag.byMemory():
212*90c8c64dSAndroid Build Coastguard Worker    WriteResult(totalCount, totalMemory, v, k)
213*90c8c64dSAndroid Build Coastguard Worker    if i >= 10:
214*90c8c64dSAndroid Build Coastguard Worker      break
215*90c8c64dSAndroid Build Coastguard Worker    i += 1
216*90c8c64dSAndroid Build Coastguard Worker
217*90c8c64dSAndroid Build Coastguard Worker  print
218*90c8c64dSAndroid Build Coastguard Worker  print "Top Processes by memory"
219*90c8c64dSAndroid Build Coastguard Worker  print "-----------------------"
220*90c8c64dSAndroid Build Coastguard Worker  i = 0
221*90c8c64dSAndroid Build Coastguard Worker  for k,v in byPid.byMemory():
222*90c8c64dSAndroid Build Coastguard Worker    WriteResult(totalCount, totalMemory, v,
223*90c8c64dSAndroid Build Coastguard Worker        "%-8s %s" % (k, processes.FindPid(k).DisplayName()))
224*90c8c64dSAndroid Build Coastguard Worker    if i >= 10:
225*90c8c64dSAndroid Build Coastguard Worker      break
226*90c8c64dSAndroid Build Coastguard Worker    i += 1
227*90c8c64dSAndroid Build Coastguard Worker
228*90c8c64dSAndroid Build Coastguard Worker  print
229*90c8c64dSAndroid Build Coastguard Worker  print "Top Duplicates by count"
230*90c8c64dSAndroid Build Coastguard Worker  print "-----------------------"
231*90c8c64dSAndroid Build Coastguard Worker  i = 0
232*90c8c64dSAndroid Build Coastguard Worker  for k,v in byText.byCount():
233*90c8c64dSAndroid Build Coastguard Worker    logLine = v.lines[0]
234*90c8c64dSAndroid Build Coastguard Worker    WriteResult(totalCount, totalMemory, v,
235*90c8c64dSAndroid Build Coastguard Worker        "%s/%s: %s" % (logLine.level, logLine.tag, logLine.text))
236*90c8c64dSAndroid Build Coastguard Worker    if i >= 10:
237*90c8c64dSAndroid Build Coastguard Worker      break
238*90c8c64dSAndroid Build Coastguard Worker    i += 1
239*90c8c64dSAndroid Build Coastguard Worker
240*90c8c64dSAndroid Build Coastguard Worker  print
241*90c8c64dSAndroid Build Coastguard Worker  print "Top Duplicates by memory"
242*90c8c64dSAndroid Build Coastguard Worker  print "-----------------------"
243*90c8c64dSAndroid Build Coastguard Worker  i = 0
244*90c8c64dSAndroid Build Coastguard Worker  for k,v in byText.byCount():
245*90c8c64dSAndroid Build Coastguard Worker    logLine = v.lines[0]
246*90c8c64dSAndroid Build Coastguard Worker    WriteResult(totalCount, totalMemory, v,
247*90c8c64dSAndroid Build Coastguard Worker        "%s/%s: %s" % (logLine.level, logLine.tag, logLine.text))
248*90c8c64dSAndroid Build Coastguard Worker    if i >= 10:
249*90c8c64dSAndroid Build Coastguard Worker      break
250*90c8c64dSAndroid Build Coastguard Worker    i += 1
251*90c8c64dSAndroid Build Coastguard Worker
252*90c8c64dSAndroid Build Coastguard Worker  print
253*90c8c64dSAndroid Build Coastguard Worker  print "Totals"
254*90c8c64dSAndroid Build Coastguard Worker  print "------"
255*90c8c64dSAndroid Build Coastguard Worker  print "%7d  %s" % (totalCount, FormatMemory(totalMemory))
256*90c8c64dSAndroid Build Coastguard Worker
257*90c8c64dSAndroid Build Coastguard Worker  print "Actual duration: %s" % FormateTimeDelta(endTime-startTime)
258*90c8c64dSAndroid Build Coastguard Worker
259*90c8c64dSAndroid Build Coastguard Workerif __name__ == "__main__":
260*90c8c64dSAndroid Build Coastguard Worker  main(sys.argv)
261*90c8c64dSAndroid Build Coastguard Worker
262*90c8c64dSAndroid Build Coastguard Worker# vim: set ts=2 sw=2 sts=2 tw=100 nocindent autoindent smartindent expandtab:
263