diff options
author | robrajapakse <robrajapakse@google.com> | 2022-10-11 10:16:20 -0700 |
---|---|---|
committer | robrajapakse <robrajapakse@google.com> | 2022-10-11 10:37:29 -0700 |
commit | 51d4eda00de2f888b0bdd95946c027d007d7e4a3 (patch) | |
tree | 78fa611bf8ed1649ada85c54b34571f884cb31b6 /perf_tools | |
parent | 51cbaa40fab82382a0916bfbc60feabbcbf61a05 (diff) | |
download | extras-51d4eda00de2f888b0bdd95946c027d007d7e4a3.tar.gz |
Tool to pull 20 data sets from a seahawk for SBT analysis
Test: Manual
Bug: 241473698
Change-Id: Iee039709493fca776704a89e41c8cde19acdedb0
Diffstat (limited to 'perf_tools')
-rwxr-xr-x | perf_tools/sbtpull.py | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/perf_tools/sbtpull.py b/perf_tools/sbtpull.py new file mode 100755 index 00000000..c6af55ce --- /dev/null +++ b/perf_tools/sbtpull.py @@ -0,0 +1,562 @@ +#!/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() + |