| ## | |
| # Generate symbal for memory profile info. | |
| # | |
| # This tool depends on DIA2Dump.exe (VS) or nm (gcc) to parse debug entry. | |
| # | |
| # Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> | |
| # This program and the accompanying materials are licensed and made available under | |
| # the terms and conditions of the BSD License that accompanies this distribution. | |
| # The full text of the license may be found at | |
| # http://opensource.org/licenses/bsd-license.php. | |
| # | |
| # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| # | |
| ## | |
| import os | |
| import re | |
| import sys | |
| from optparse import OptionParser | |
| versionNumber = "1.1" | |
| __copyright__ = "Copyright (c) 2016, Intel Corporation. All rights reserved." | |
| class Symbols: | |
| def __init__(self): | |
| self.listLineAddress = [] | |
| self.pdbName = "" | |
| # Cache for function | |
| self.functionName = "" | |
| # Cache for line | |
| self.sourceName = "" | |
| def getSymbol (self, rva): | |
| index = 0 | |
| lineName = 0 | |
| sourceName = "??" | |
| while index + 1 < self.lineCount : | |
| if self.listLineAddress[index][0] <= rva and self.listLineAddress[index + 1][0] > rva : | |
| offset = rva - self.listLineAddress[index][0] | |
| functionName = self.listLineAddress[index][1] | |
| lineName = self.listLineAddress[index][2] | |
| sourceName = self.listLineAddress[index][3] | |
| if lineName == 0 : | |
| return " (" + self.listLineAddress[index][1] + "() - " + ")" | |
| else : | |
| return " (" + self.listLineAddress[index][1] + "() - " + sourceName + ":" + str(lineName) + ")" | |
| index += 1 | |
| return " (unknown)" | |
| def parse_debug_file(self, driverName, pdbName): | |
| if cmp (pdbName, "") == 0 : | |
| return | |
| self.pdbName = pdbName; | |
| try: | |
| nmCommand = "nm" | |
| nmLineOption = "-l" | |
| print "parsing (debug) - " + pdbName | |
| os.system ('%s %s %s > nmDump.line.log' % (nmCommand, nmLineOption, pdbName)) | |
| except : | |
| print 'ERROR: nm command not available. Please verify PATH' | |
| return | |
| # | |
| # parse line | |
| # | |
| linefile = open("nmDump.line.log") | |
| reportLines = linefile.readlines() | |
| linefile.close() | |
| # 000113ca T AllocatePool c:\home\edk-ii\MdePkg\Library\UefiMemoryAllocationLib\MemoryAllocationLib.c:399 | |
| patchLineFileMatchString = "([0-9a-fA-F]*)\s+[T|D|t|d]\s+(\w+)\s*((?:[a-zA-Z]:)?[\w+\-./_a-zA-Z0-9\\\\]*):?([0-9]*)" | |
| for reportLine in reportLines: | |
| #print "check - " + reportLine | |
| match = re.match(patchLineFileMatchString, reportLine) | |
| if match is not None: | |
| #print "match - " + reportLine[:-1] | |
| #print "0 - " + match.group(0) | |
| #print "1 - " + match.group(1) | |
| #print "2 - " + match.group(2) | |
| #print "3 - " + match.group(3) | |
| #print "4 - " + match.group(4) | |
| rva = int (match.group(1), 16) | |
| functionName = match.group(2) | |
| sourceName = match.group(3) | |
| if cmp (match.group(4), "") != 0 : | |
| lineName = int (match.group(4)) | |
| else : | |
| lineName = 0 | |
| self.listLineAddress.append ([rva, functionName, lineName, sourceName]) | |
| self.lineCount = len (self.listLineAddress) | |
| self.listLineAddress = sorted(self.listLineAddress, key=lambda symbolAddress:symbolAddress[0]) | |
| #for key in self.listLineAddress : | |
| #print "rva - " + "%x"%(key[0]) + ", func - " + key[1] + ", line - " + str(key[2]) + ", source - " + key[3] | |
| def parse_pdb_file(self, driverName, pdbName): | |
| if cmp (pdbName, "") == 0 : | |
| return | |
| self.pdbName = pdbName; | |
| try: | |
| #DIA2DumpCommand = "\"C:\\Program Files (x86)\Microsoft Visual Studio 14.0\\DIA SDK\\Samples\\DIA2Dump\\x64\\Debug\\Dia2Dump.exe\"" | |
| DIA2DumpCommand = "Dia2Dump.exe" | |
| #DIA2SymbolOption = "-p" | |
| DIA2LinesOption = "-l" | |
| print "parsing (pdb) - " + pdbName | |
| #os.system ('%s %s %s > DIA2Dump.symbol.log' % (DIA2DumpCommand, DIA2SymbolOption, pdbName)) | |
| os.system ('%s %s %s > DIA2Dump.line.log' % (DIA2DumpCommand, DIA2LinesOption, pdbName)) | |
| except : | |
| print 'ERROR: DIA2Dump command not available. Please verify PATH' | |
| return | |
| # | |
| # parse line | |
| # | |
| linefile = open("DIA2Dump.line.log") | |
| reportLines = linefile.readlines() | |
| linefile.close() | |
| # ** GetDebugPrintErrorLevel | |
| # line 32 at [0000C790][0001:0000B790], len = 0x3 c:\home\edk-ii\mdepkg\library\basedebugprinterrorlevellib\basedebugprinterrorlevellib.c (MD5: 687C0AE564079D35D56ED5D84A6164CC) | |
| # line 36 at [0000C793][0001:0000B793], len = 0x5 | |
| # line 37 at [0000C798][0001:0000B798], len = 0x2 | |
| patchLineFileMatchString = "\s+line ([0-9]+) at \[([0-9a-fA-F]{8})\]\[[0-9a-fA-F]{4}\:[0-9a-fA-F]{8}\], len = 0x[0-9a-fA-F]+\s*([\w+\-\:./_a-zA-Z0-9\\\\]*)\s*" | |
| patchLineFileMatchStringFunc = "\*\*\s+(\w+)\s*" | |
| for reportLine in reportLines: | |
| #print "check line - " + reportLine | |
| match = re.match(patchLineFileMatchString, reportLine) | |
| if match is not None: | |
| #print "match - " + reportLine[:-1] | |
| #print "0 - " + match.group(0) | |
| #print "1 - " + match.group(1) | |
| #print "2 - " + match.group(2) | |
| if cmp (match.group(3), "") != 0 : | |
| self.sourceName = match.group(3) | |
| sourceName = self.sourceName | |
| functionName = self.functionName | |
| rva = int (match.group(2), 16) | |
| lineName = int (match.group(1)) | |
| self.listLineAddress.append ([rva, functionName, lineName, sourceName]) | |
| else : | |
| match = re.match(patchLineFileMatchStringFunc, reportLine) | |
| if match is not None: | |
| self.functionName = match.group(1) | |
| self.lineCount = len (self.listLineAddress) | |
| self.listLineAddress = sorted(self.listLineAddress, key=lambda symbolAddress:symbolAddress[0]) | |
| #for key in self.listLineAddress : | |
| #print "rva - " + "%x"%(key[0]) + ", func - " + key[1] + ", line - " + str(key[2]) + ", source - " + key[3] | |
| class SymbolsFile: | |
| def __init__(self): | |
| self.symbolsTable = {} | |
| symbolsFile = "" | |
| driverName = "" | |
| rvaName = "" | |
| symbolName = "" | |
| def getSymbolName(driverName, rva): | |
| global symbolsFile | |
| #print "driverName - " + driverName | |
| try : | |
| symbolList = symbolsFile.symbolsTable[driverName] | |
| if symbolList is not None: | |
| return symbolList.getSymbol (rva) | |
| else: | |
| return " (???)" | |
| except Exception: | |
| return " (???)" | |
| def processLine(newline): | |
| global driverName | |
| global rvaName | |
| driverPrefixLen = len("Driver - ") | |
| # get driver name | |
| if cmp(newline[0:driverPrefixLen],"Driver - ") == 0 : | |
| driverlineList = newline.split(" ") | |
| driverName = driverlineList[2] | |
| #print "Checking : ", driverName | |
| # EDKII application output | |
| pdbMatchString = "Driver - \w* \(Usage - 0x[0-9a-fA-F]+\) \(Pdb - ([:\-.\w\\\\/]*)\)\s*" | |
| pdbName = "" | |
| match = re.match(pdbMatchString, newline) | |
| if match is not None: | |
| #print "match - " + newline | |
| #print "0 - " + match.group(0) | |
| #print "1 - " + match.group(1) | |
| pdbName = match.group(1) | |
| #print "PDB - " + pdbName | |
| symbolsFile.symbolsTable[driverName] = Symbols() | |
| if cmp (pdbName[-3:], "pdb") == 0 : | |
| symbolsFile.symbolsTable[driverName].parse_pdb_file (driverName, pdbName) | |
| else : | |
| symbolsFile.symbolsTable[driverName].parse_debug_file (driverName, pdbName) | |
| elif cmp(newline,"") == 0 : | |
| driverName = "" | |
| # check entry line | |
| if newline.find ("<==") != -1 : | |
| entry_list = newline.split(" ") | |
| rvaName = entry_list[4] | |
| #print "rva : ", rvaName | |
| symbolName = getSymbolName (driverName, int(rvaName, 16)) | |
| else : | |
| rvaName = "" | |
| symbolName = "" | |
| if cmp(rvaName,"") == 0 : | |
| return newline | |
| else : | |
| return newline + symbolName | |
| def myOptionParser(): | |
| usage = "%prog [--version] [-h] [--help] [-i inputfile [-o outputfile]]" | |
| Parser = OptionParser(usage=usage, description=__copyright__, version="%prog " + str(versionNumber)) | |
| Parser.add_option("-i", "--inputfile", dest="inputfilename", type="string", help="The input memory profile info file output from MemoryProfileInfo application in MdeModulePkg") | |
| Parser.add_option("-o", "--outputfile", dest="outputfilename", type="string", help="The output memory profile info file with symbol, MemoryProfileInfoSymbol.txt will be used if it is not specified") | |
| (Options, args) = Parser.parse_args() | |
| if Options.inputfilename is None: | |
| Parser.error("no input file specified") | |
| if Options.outputfilename is None: | |
| Options.outputfilename = "MemoryProfileInfoSymbol.txt" | |
| return Options | |
| def main(): | |
| global symbolsFile | |
| global Options | |
| Options = myOptionParser() | |
| symbolsFile = SymbolsFile() | |
| try : | |
| file = open(Options.inputfilename) | |
| except Exception: | |
| print "fail to open " + Options.inputfilename | |
| return 1 | |
| try : | |
| newfile = open(Options.outputfilename, "w") | |
| except Exception: | |
| print "fail to open " + Options.outputfilename | |
| return 1 | |
| try: | |
| while 1: | |
| line = file.readline() | |
| if not line: | |
| break | |
| newline = line[:-1] | |
| newline = processLine(newline) | |
| newfile.write(newline) | |
| newfile.write("\n") | |
| finally: | |
| file.close() | |
| newfile.close() | |
| if __name__ == '__main__': | |
| sys.exit(main()) |