563 lines
16 KiB
Python
Executable File
563 lines
16 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#from calendar import c
|
|
import sys
|
|
import os
|
|
import copy
|
|
import argparse
|
|
import statistics
|
|
import glob
|
|
import subprocess
|
|
import re
|
|
import time
|
|
|
|
from string import digits
|
|
|
|
class LogLine:
|
|
remove_digits = str.maketrans('', '', digits)
|
|
def __init__(self):
|
|
self.lineNum = 0
|
|
self.timeStamp = 0
|
|
self.delta = 0
|
|
self.deltaDiff = 0
|
|
self.text = "none"
|
|
self.textKey = "none"
|
|
|
|
def parse(self, index, line, priorTimeStamp):
|
|
_line = line.strip()
|
|
words = _line.split("]", 1)
|
|
timeString = words[0].strip(" [")
|
|
self.lineNum = index
|
|
self.timeStamp = float(timeString)
|
|
self.delta = self.timeStamp - priorTimeStamp
|
|
self.text = words[1][:150]
|
|
self.textKey = self.text.translate(self.remove_digits)
|
|
priorTimeStamp = self.timeStamp
|
|
return self
|
|
|
|
def getTextKey(self):
|
|
textKey = self.textKey
|
|
return textKey
|
|
|
|
def print(self):
|
|
print("I, {:5d}, T, {:8.4f}, D, {: .4f}, DD, ({: .4f}) {}".format(self.lineNum, self.timeStamp, self.delta, self.deltaDiff, self.text))
|
|
|
|
def toString(self):
|
|
return "I, {:5d}, T, {:8.4f}, D, {: .4f}, DD, ({: .4f}) {}".format(self.lineNum, self.timeStamp, self.delta, self.deltaDiff, self.text)
|
|
|
|
def sortByDelta(item):
|
|
return item.delta
|
|
|
|
def sortByTimeStamp(item):
|
|
return item.timeStamp
|
|
|
|
class LogLineListStats:
|
|
def __init__(self):
|
|
self.numItems = 0
|
|
self.firstTimeStamp = 0
|
|
self.lastTimeStamp = 0
|
|
self.deltaSum = 0
|
|
self.deltaDiffSum = 0
|
|
self.status = "unknown"
|
|
self.name = "unknown"
|
|
|
|
def print(self):
|
|
print("Name {:25} NumItems {:4d} FirstTimeStamp {:.3f}, lastTimeStamp {:.3f}, deltaTime {:.3f} deltaSum {:.3f}, deltaDiffSum {:.3f} Status {}".format(self.name, self.numItems, self.firstTimeStamp, self.lastTimeStamp, (self.lastTimeStamp - self.firstTimeStamp), self.deltaSum, self.deltaDiffSum, self.status))
|
|
|
|
def add(self, _other):
|
|
if (_other.firstTimeStamp< self.firstTimeStamp):
|
|
self.firstTimeStamp = _other.firstTimeStamp
|
|
|
|
if (_other.lastTimeStamp > self.lastTimeStamp):
|
|
self.lastTimeStamp = _other.lastTimeStamp
|
|
self.deltaSum += _other.deltaSum
|
|
|
|
|
|
# ------------------------------------------------------
|
|
|
|
class LogLineList:
|
|
|
|
def __init__(self, _name= ""):
|
|
self.list = []
|
|
self.name = _name
|
|
|
|
def clear(self):
|
|
self.list.clear()
|
|
|
|
def append(self, item):
|
|
self.list.append(item)
|
|
|
|
def print(self, numItems=None):
|
|
printLineNum = 0
|
|
timeStart = 0
|
|
sumDelta = 0
|
|
sumDeltaDiff = 0
|
|
print("List: {}", self.name)
|
|
for item in self.list:
|
|
if (timeStart==0):
|
|
timeStart = item.timeStamp
|
|
timeOffset = item.timeStamp - timeStart
|
|
sumDelta += item.delta
|
|
sumDeltaDiff += item.deltaDiff
|
|
printLineNum += 1
|
|
printLine = "{:4d} {:.4f} {: .4f} ({: .4f}) | {} ".format(printLineNum, timeOffset, sumDelta, sumDeltaDiff, item.toString())
|
|
print(printLine)
|
|
if (numItems!=None):
|
|
numItems -= 1
|
|
if (numItems<=0):
|
|
break
|
|
|
|
def find(self, word):
|
|
itemList = []
|
|
for item in self.list:
|
|
if item.text.find(word) != -1:
|
|
itemList.append(item)
|
|
return itemList
|
|
def findFirst(self, word):
|
|
itemList = self.find(word)
|
|
if (itemList!=None):
|
|
if (len(itemList)>0):
|
|
return itemList[0]
|
|
return None
|
|
|
|
def findTextKey(self, textKey):
|
|
itemList = []
|
|
for item in self.list:
|
|
if item.getTextKey()==textKey:
|
|
itemList.append(item)
|
|
if (len(itemList)==0):
|
|
return None
|
|
return itemList[0]
|
|
|
|
def findItem(self, item):
|
|
textKey = item.getTextKey()
|
|
return self.findTextKey(textKey)
|
|
|
|
def findExactItem(self, item):
|
|
text = item.text
|
|
return self.find(text)
|
|
|
|
def filter(self, startKeyWord, endKeyWord, delta=0):
|
|
resultsList = LogLineList()
|
|
startTime = self.findFirst(startKeyWord).timeStamp
|
|
endTime = self.findFirst(endKeyWord).timeStamp
|
|
for item in self.list:
|
|
if ((item.timeStamp >= startTime) and (item.timeStamp<=endTime)):
|
|
if (item.timeStamp == startTime):
|
|
item.delta = 0
|
|
if ((item.delta > delta) or ((item.timeStamp == startTime))):
|
|
resultsList.append(item)
|
|
resultsList.name = self.name
|
|
return resultsList
|
|
|
|
|
|
def findCommon(self, otherList):
|
|
commonList = LogLineList()
|
|
commonList.name = self.name + "_common"
|
|
notCommonList = LogLineList()
|
|
notCommonList.name = self.name + "_notCommon"
|
|
numFoundItems = 0
|
|
numNotFoundItems = 0
|
|
for item in self.list:
|
|
dm1 = otherList.findExactItem(item)
|
|
_item = copy.deepcopy(item)
|
|
if dm1!=None:
|
|
commonList.append(_item)
|
|
numFoundItems += 1
|
|
else:
|
|
notCommonList.append(_item)
|
|
numNotFoundItems += 1
|
|
print("FindCommon {} {} {} {}".format(len(self.list), len(otherList.list), numFoundItems, numNotFoundItems ))
|
|
return commonList, notCommonList
|
|
|
|
def difference(self, otherList):
|
|
diffList = LogLineList()
|
|
diffList.name = otherList.name + "Diff"
|
|
for item in self.list:
|
|
thisItem = copy.deepcopy(item)
|
|
otherItem = otherList.findItem(item)
|
|
if (item.text.find("EXT4-fs (sda11): recovery complete")!=-1):
|
|
print("here")
|
|
if otherItem==None:
|
|
print("LogLineList::difference() !Error, other does not have {}".format(item.text))
|
|
else:
|
|
thisItem.deltaDiff = otherItem.delta - item.delta
|
|
|
|
diffList.append(thisItem)
|
|
return diffList
|
|
|
|
def analyze(self, checkPeriod = True, includeFirst = True):
|
|
numItems = 0
|
|
firstTimeStamp = 0
|
|
firstDelta = 0
|
|
lastTimeStamp = 0
|
|
deltaSum = 0
|
|
deltaDiffSum = 0
|
|
for item in self.list:
|
|
numItems += 1
|
|
deltaSum += item.delta
|
|
deltaDiffSum += item.deltaDiff
|
|
if firstTimeStamp==0:
|
|
firstTimeStamp = item.timeStamp
|
|
firstDelta = item.delta
|
|
deltaSum = 0
|
|
deltaDiffSum = 0
|
|
if (item.timeStamp<firstTimeStamp):
|
|
firstTimeStamp = item.timeStamp
|
|
firstDelta = item.delta
|
|
|
|
if (item.timeStamp > lastTimeStamp):
|
|
lastTimeStamp = item.timeStamp
|
|
timePeriod = lastTimeStamp - firstTimeStamp
|
|
status = "pass"
|
|
if (checkPeriod):
|
|
diff = timePeriod - deltaSum
|
|
if (abs(diff)>0.0001):
|
|
print("LogLineList::Analyze() {} ERROR! TimePeriod:{}, CumulativeDelta: {} ".format(self.name, timePeriod, deltaSum))
|
|
status = "ERROR"
|
|
logLineListStats = LogLineListStats()
|
|
logLineListStats.numItems = numItems
|
|
logLineListStats.firstTimeStamp = firstTimeStamp
|
|
logLineListStats.lastTimeStamp = lastTimeStamp
|
|
logLineListStats.deltaSum = deltaSum
|
|
logLineListStats.deltaDiffSum = deltaDiffSum
|
|
logLineListStats.status = status
|
|
logLineListStats.name = self.name
|
|
return logLineListStats
|
|
|
|
def addList(self, otherList):
|
|
self.list.extend(otherList.list)
|
|
self.list = sorted(self.list, key=sortByTimeStamp)
|
|
|
|
|
|
class LogFile:
|
|
priorTimeStamp = 0.0
|
|
def __init__(self, _fileName = ""):
|
|
self.logLineList = LogLineList()
|
|
if (_fileName!=""):
|
|
self.load(_fileName)
|
|
|
|
def loadLines(self, lines):
|
|
logLineList = LogLineList()
|
|
for index, line in enumerate(lines):
|
|
logLine = LogLine().parse(index, line, self.priorTimeStamp)
|
|
self.priorTimeStamp = logLine.timeStamp
|
|
logLineList.append(logLine)
|
|
return logLineList
|
|
|
|
def load(self, _fileName):
|
|
self.name = _fileName
|
|
try:
|
|
file = open(_fileName, 'r')
|
|
lines = file.readlines()
|
|
self.logLineList = self.loadLines(lines)
|
|
file.close()
|
|
except:
|
|
print("Error, file '{}' does not exist".format(self.name))
|
|
|
|
def print(self, numItems=None):
|
|
self.logLineList.print(numItems)
|
|
|
|
# -----------------------------------------------------
|
|
|
|
class MetricSet:
|
|
def __init__(self, _names):
|
|
self.columnNames = _names
|
|
self.rowColArray = []
|
|
self.rowSum = []
|
|
self.rowMax = []
|
|
self.rowMin = []
|
|
self.rowStd = []
|
|
self.rowMedian = []
|
|
for col in self.columnNames:
|
|
self.rowSum.append(0)
|
|
self.rowMax.append(0)
|
|
self.rowMin.append(sys.maxsize)
|
|
self.rowStd.append(0)
|
|
self.rowMedian.append(0)
|
|
|
|
def appendSet(self, values):
|
|
self.rowColArray.append(values)
|
|
|
|
def print(self):
|
|
print("{}".format(" Line#"), end='')
|
|
for words in self.columnNames:
|
|
print(", '{}'".format(words), end='')
|
|
print("")
|
|
|
|
for row, values in enumerate(self.rowColArray):
|
|
print("{:6d}".format(row), end='')
|
|
for col, value in enumerate(values):
|
|
print(", {:.3f}".format(value), end='')
|
|
print("")
|
|
|
|
print("{}".format(" MAX"), end='')
|
|
for value in self.rowMax:
|
|
print(", {:.3f}".format(value), end='')
|
|
print("")
|
|
|
|
|
|
print("{}".format(" AVE"), end='')
|
|
for value in self.rowSum:
|
|
print(", {:.3f}".format(value), end='')
|
|
print("")
|
|
|
|
print("{}".format(" MIN"), end='')
|
|
for value in self.rowMin:
|
|
print(", {:2.3f}".format(value), end='')
|
|
print("")
|
|
|
|
print("{}".format(" STD"), end='')
|
|
for value in self.rowStd:
|
|
print(", {:2.3f}".format(value), end='')
|
|
print("")
|
|
|
|
print("{}".format("MEDIAN"), end='')
|
|
for value in self.rowMedian:
|
|
print(", {:2.3f}".format(value), end='')
|
|
print("")
|
|
|
|
def analyze(self):
|
|
stdCols = []
|
|
numCols = len(self.columnNames)
|
|
numRows = len(self.rowColArray)
|
|
for col in range(numCols):
|
|
stdCols.append([])
|
|
|
|
# compute sum
|
|
for row, values in enumerate(self.rowColArray):
|
|
for col, value in enumerate(values):
|
|
self.rowSum[col] += value
|
|
if value > self.rowMax[col]:
|
|
self.rowMax[col] = value
|
|
if value < self.rowMin[col]:
|
|
self.rowMin[col] = value
|
|
|
|
# compute std
|
|
for col in range(numCols):
|
|
for row in range(numRows):
|
|
try:
|
|
val = self.rowColArray[row][col]
|
|
stdCols[col].append(val)
|
|
except IndexError:
|
|
i = 3
|
|
for col, colList in enumerate(stdCols):
|
|
stdValue = 0
|
|
if (len(colList)>0):
|
|
stdValue = statistics.pstdev(colList)
|
|
stdMedian = statistics.median(colList)
|
|
self.rowStd[col] = stdValue
|
|
self.rowMedian[col] = stdMedian
|
|
|
|
#compute average
|
|
for col, value in enumerate(self.rowSum):
|
|
if numRows > 0:
|
|
self.rowSum[col] = self.rowSum[col] / numRows
|
|
else:
|
|
self.rowSum[col] = 0
|
|
|
|
class AnalyzeFile:
|
|
initFirstTime = 0
|
|
initSecondTime = 0
|
|
|
|
def __init__(self, _fileName, _keyWords = ["init first", "init second", "boot_completed"]):
|
|
self.fileName = _fileName
|
|
self.logFile = LogFile(_fileName)
|
|
self.workingList = []
|
|
self.keyWords = _keyWords
|
|
|
|
def report(self):
|
|
print("-----------------------")
|
|
print("Reporting on '{}'".format(self.fileName))
|
|
for word in self.keyWords:
|
|
item = self.logFile.logLineList.findFirst(word)
|
|
item.print()
|
|
print("-----------------------")
|
|
|
|
def getMetrics(self, metricsSet):
|
|
values = []
|
|
for word in self.keyWords:
|
|
item = self.logFile.logLineList.findFirst(word)
|
|
if item is not None:
|
|
values.append(item.timeStamp)
|
|
else:
|
|
print("Did not find {} ".format(word))
|
|
metricsSet.appendSet(values)
|
|
|
|
def keyWordReport(self, keyword):
|
|
numItems = 0
|
|
cumd = 0
|
|
items = self.logFile.logLineList.find(keyword)
|
|
for item in items:
|
|
item.print()
|
|
numItems += 1
|
|
cumd += item.delta
|
|
print("Num {} found = {}, Sum delay = {:.2f} ".format(keyword, numItems, cumd))
|
|
|
|
for item in items:
|
|
lineKeywords = item.text.split(" ")
|
|
if (len(lineKeywords)>2):
|
|
if lineKeywords[2] == "Service":
|
|
tookIndex = item.text.find("took")
|
|
if (tookIndex!=None):
|
|
tookTime = item.text[tookIndex:tookIndex+10]
|
|
print("{} took {}".format(lineKeywords[3], tookTime))
|
|
|
|
|
|
class Analyzer:
|
|
def __init__(self):
|
|
self.fileName = []
|
|
|
|
def rebootAndRunCmdToFile(self, fileNamePrefix, msgPrefix, Cmd, numTimes, startIndex):
|
|
captured = False
|
|
error = False
|
|
filenameNum = ""
|
|
for i in range(numTimes):
|
|
postfix = str(i+startIndex)
|
|
filenameNum = fileNamePrefix + "-" + postfix + ".txt"
|
|
print(msgPrefix + " to {}".format(filenameNum))
|
|
# try 5 times to capure status 'boot_completed'
|
|
for i in range(5):
|
|
captured = False
|
|
rebootCmd = "adb shell su root reboot"
|
|
fullCmd = Cmd + " > {}".format(filenameNum)
|
|
x = os.system(rebootCmd)
|
|
if (x!=0):
|
|
print("Error")
|
|
error = True
|
|
break
|
|
time.sleep(45)
|
|
x = os.system(fullCmd)
|
|
if (x!=0):
|
|
print("Error")
|
|
error = True
|
|
break
|
|
# check for boot complete
|
|
try:
|
|
checkBootComplete = "grep boot_complete {}".format(filenameNum)
|
|
output = subprocess.check_output(checkBootComplete, shell=True)
|
|
captured = True
|
|
break
|
|
except:
|
|
captured = False
|
|
print("trying again for {}".format(filenameNum))
|
|
if not captured:
|
|
print("ERROR - failed to capture {}".format(filenameNum))
|
|
if error:
|
|
os.system("rm {}".format(filenameNum))
|
|
return captured
|
|
|
|
def getBuildID(self):
|
|
buildIDCmd = "adb shell su root getprop ro.build.version.incremental"
|
|
buildString = subprocess.check_output(buildIDCmd, shell = True)
|
|
numberList = re.findall(r'\d+', buildString.decode('ISO-8859-1') )
|
|
if (numberList==None): return 0
|
|
if (len(numberList)==0): return 0
|
|
buildID = numberList[0]
|
|
return buildID
|
|
|
|
def pullDmesgLogs(self, BuildID, numTimes, startIndex):
|
|
fileNamePrefix = BuildID
|
|
msgPrefix = "Pulling Kernel dmesg logs"
|
|
cmd = "adb shell su root dmesg"
|
|
return self.rebootAndRunCmdToFile(fileNamePrefix, msgPrefix, cmd, numTimes, startIndex)
|
|
|
|
def pullLogcatLogs(self, BuildID, numTimes, startIndex):
|
|
fileNamePrefix = "LC-"+BuildID
|
|
msgPrefix = "Pulling Kernel Logcat"
|
|
cmd = "adb logcat -b all -d"
|
|
return self.rebootAndRunCmdToFile(fileNamePrefix, msgPrefix, cmd, numTimes, startIndex)
|
|
|
|
def runBootAnalyze(self, filename, numTimes, startIndex):
|
|
ABT = os.environ["ANDROID_BUILD_TOP"]
|
|
if (len(ABT)<=0):
|
|
print("ERROR - ANDROID_BUILD_TOP not set")
|
|
BAFILE = "BA-" + filename + "-" + str(numTimes + startIndex) + ".txt"
|
|
BACmd = ABT + "/system/extras/boottime_tools/bootanalyze/bootanalyze.py -c " + ABT + "/system/extras/boottime_tools/bootanalyze/config.yaml -n 20 -r -t > " + BAFILE
|
|
print(BACmd)
|
|
x = os.system(BACmd)
|
|
if (x!=0):
|
|
print("ERROR running bootanalze")
|
|
return False
|
|
return True
|
|
|
|
def pullAll(self):
|
|
BuildID = self.getBuildID()
|
|
Cmd = "adb bugreport bugreport-{}".format(BuildID)
|
|
print(Cmd)
|
|
x = os.system(Cmd)
|
|
if (x!=0):
|
|
print("ERROR Pulling all data")
|
|
return False
|
|
self.pullDmesgLogs(BuildID, 20, 0)
|
|
self.pullLogcatLogs(BuildID, 2, 0)
|
|
self.runBootAnalyze(BuildID, 20, 0)
|
|
self.summaryReportOnDmesgLogFiles(BuildID, 20)
|
|
|
|
def summaryReportOnDmesgLogFiles(self, BuildID, numFiles):
|
|
metricKeyWords = ["init first", "init second", "boot_completed"]
|
|
metricSet = MetricSet(metricKeyWords)
|
|
print("Summary report on log files with build ID {}".format(BuildID))
|
|
dirList = glob.glob("{}*.txt".format(BuildID))
|
|
numFilesAnalyzed = 0
|
|
for index, file in enumerate(dirList):
|
|
analyzeFile = AnalyzeFile(file, metricKeyWords)
|
|
#check it's a kernel log file
|
|
item = analyzeFile.logFile.logLineList.findFirst("build.fingerprint")
|
|
if (item!=None):
|
|
#check if it has the correct build ID
|
|
if (item.text.find(BuildID)==-1):
|
|
continue
|
|
else:
|
|
print("BuildID {} not found in file {} fingerprint {}".format(BuildID, file, item))
|
|
continue
|
|
analyzeFile.getMetrics(metricSet)
|
|
numFilesAnalyzed += 1
|
|
if ((index+1)>=numFiles):
|
|
break
|
|
if (numFilesAnalyzed>0):
|
|
metricSet.analyze()
|
|
metricSet.print()
|
|
else:
|
|
print("No files criteria {}* and build.fingerprint with {}".format(BuildID, BuildID))
|
|
|
|
def rename(self, BuildID1, BuildID2, fileType):
|
|
print("Summary report on log files with build ID {}".format(BuildID1))
|
|
dirList = glob.glob("*{}*".format(BuildID1))
|
|
for index, file in enumerate(dirList):
|
|
findRes = file.find(BuildID1)
|
|
if (findRes!=-1):
|
|
newFile = file.replace(BuildID1, BuildID2, 1)
|
|
newFile += fileType
|
|
os.system("mv {} {}".format(file, newFile))
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='pull all data files from seahawk and run dmesg summary report. The data files will be prefixed with the build ID')
|
|
|
|
parser.add_argument("-plc", nargs=3, metavar=('<BuildID>', '<numTimes>', '<startIndex>'), help="pull logcat numTimes from seahawk")
|
|
parser.add_argument("-pdm", nargs=3, metavar=('<BuildID>', '<numTimes>', '<startIndex>'), help="pull dmesg logs numTimes from seahawk")
|
|
parser.add_argument("-pba", nargs=2, metavar=('<BuildID>', '<numTimes>'), help="pull bootanalyze numTimes from seahawk")
|
|
parser.add_argument("-rd", nargs=2, metavar=('<BuildID>', '<numFiles>'), help="summary report on <numFiles> dmesg log files named <BuildID>-*.txt in current directory")
|
|
parser.add_argument("-pA", action='store_true', help="pull all data from seahawk a default number of times")
|
|
parser.add_argument("-t", nargs="*", help="test - do not use")
|
|
args = parser.parse_args()
|
|
|
|
|
|
if args.pdm!=None:
|
|
Analyzer().pullDmesgLogs(args.pdm[0], int(args.pdm[1]), int(args.pdm[2]))
|
|
|
|
if args.plc!=None:
|
|
Analyzer().pullLogcatLogs(args.plc[0], int(args.plc[1]), int(args.plc[2]))
|
|
|
|
if args.pba!=None:
|
|
Analyzer().runBootAnalyze(args.pba[0], int(args.pba[1]), 0)
|
|
|
|
if args.pA!=None:
|
|
Analyzer().pullAll()
|
|
|
|
if args.rd!=None:
|
|
Analyzer().summaryReportOnDmesgLogFiles(args.rd[0], int(args.rd[1]))
|
|
|
|
if args.t!=None:
|
|
Analyzer().getBuildID()
|
|
|