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