| ## | |
| # Generate symbal for SMI handler profile info. | |
| # | |
| # This tool depends on DIA2Dump.exe (VS) or nm (gcc) to parse debug entry. | |
| # | |
| # Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent | |
| # | |
| ## | |
| from __future__ import print_function | |
| import os | |
| import re | |
| import sys | |
| from optparse import OptionParser | |
| from xml.dom.minidom import parse | |
| import xml.dom.minidom | |
| 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 [functionName] | |
| else : | |
| return [functionName, sourceName, lineName] | |
| index += 1 | |
| return [] | |
| 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: | |
| match = re.match(patchLineFileMatchString, reportLine) | |
| if match is not None: | |
| 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]) | |
| 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: | |
| match = re.match(patchLineFileMatchString, reportLine) | |
| if match is not None: | |
| 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]) | |
| class SymbolsFile: | |
| def __init__(self): | |
| self.symbolsTable = {} | |
| symbolsFile = "" | |
| driverName = "" | |
| rvaName = "" | |
| symbolName = "" | |
| def getSymbolName(driverName, rva): | |
| global symbolsFile | |
| try : | |
| symbolList = symbolsFile.symbolsTable[driverName] | |
| if symbolList is not None: | |
| return symbolList.getSymbol (rva) | |
| else: | |
| return [] | |
| except Exception: | |
| return [] | |
| def myOptionParser(): | |
| usage = "%prog [--version] [-h] [--help] [-i inputfile [-o outputfile] [-g guidreffile]]" | |
| 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") | |
| Parser.add_option("-g", "--guidref", dest="guidreffilename", type="string", help="The input guid ref file output from build") | |
| (Options, args) = Parser.parse_args() | |
| if Options.inputfilename is None: | |
| Parser.error("no input file specified") | |
| if Options.outputfilename is None: | |
| Options.outputfilename = "SmiHandlerProfileInfoSymbol.xml" | |
| return Options | |
| dictGuid = { | |
| '00000000-0000-0000-0000-000000000000':'gZeroGuid', | |
| '2A571201-4966-47F6-8B86-F31E41F32F10':'gEfiEventLegacyBootGuid', | |
| '27ABF055-B1B8-4C26-8048-748F37BAA2DF':'gEfiEventExitBootServicesGuid', | |
| '7CE88FB3-4BD7-4679-87A8-A8D8DEE50D2B':'gEfiEventReadyToBootGuid', | |
| '02CE967A-DD7E-4FFC-9EE7-810CF0470880':'gEfiEndOfDxeEventGroupGuid', | |
| '60FF8964-E906-41D0-AFED-F241E974E08E':'gEfiDxeSmmReadyToLockProtocolGuid', | |
| '18A3C6DC-5EEA-48C8-A1C1-B53389F98999':'gEfiSmmSwDispatch2ProtocolGuid', | |
| '456D2859-A84B-4E47-A2EE-3276D886997D':'gEfiSmmSxDispatch2ProtocolGuid', | |
| '4CEC368E-8E8E-4D71-8BE1-958C45FC8A53':'gEfiSmmPeriodicTimerDispatch2ProtocolGuid', | |
| 'EE9B8D90-C5A6-40A2-BDE2-52558D33CCA1':'gEfiSmmUsbDispatch2ProtocolGuid', | |
| '25566B03-B577-4CBF-958C-ED663EA24380':'gEfiSmmGpiDispatch2ProtocolGuid', | |
| '7300C4A1-43F2-4017-A51B-C81A7F40585B':'gEfiSmmStandbyButtonDispatch2ProtocolGuid', | |
| '1B1183FA-1823-46A7-8872-9C578755409D':'gEfiSmmPowerButtonDispatch2ProtocolGuid', | |
| '58DC368D-7BFA-4E77-ABBC-0E29418DF930':'gEfiSmmIoTrapDispatch2ProtocolGuid', | |
| } | |
| def genGuidString(guidreffile): | |
| guidLines = guidreffile.readlines() | |
| for guidLine in guidLines: | |
| guidLineList = guidLine.split(" ") | |
| if len(guidLineList) == 2: | |
| guid = guidLineList[0] | |
| guidName = guidLineList[1] | |
| if guid not in dictGuid : | |
| dictGuid[guid] = guidName | |
| def createSym(symbolName): | |
| SymbolNode = xml.dom.minidom.Document().createElement("Symbol") | |
| SymbolFunction = xml.dom.minidom.Document().createElement("Function") | |
| SymbolFunctionData = xml.dom.minidom.Document().createTextNode(symbolName[0]) | |
| SymbolFunction.appendChild(SymbolFunctionData) | |
| SymbolNode.appendChild(SymbolFunction) | |
| if (len(symbolName)) >= 2: | |
| SymbolSourceFile = xml.dom.minidom.Document().createElement("SourceFile") | |
| SymbolSourceFileData = xml.dom.minidom.Document().createTextNode(symbolName[1]) | |
| SymbolSourceFile.appendChild(SymbolSourceFileData) | |
| SymbolNode.appendChild(SymbolSourceFile) | |
| if (len(symbolName)) >= 3: | |
| SymbolLineNumber = xml.dom.minidom.Document().createElement("LineNumber") | |
| SymbolLineNumberData = xml.dom.minidom.Document().createTextNode(str(symbolName[2])) | |
| SymbolLineNumber.appendChild(SymbolLineNumberData) | |
| SymbolNode.appendChild(SymbolLineNumber) | |
| return SymbolNode | |
| def main(): | |
| global symbolsFile | |
| global Options | |
| Options = myOptionParser() | |
| symbolsFile = SymbolsFile() | |
| try : | |
| DOMTree = xml.dom.minidom.parse(Options.inputfilename) | |
| except Exception: | |
| print("fail to open input " + Options.inputfilename) | |
| return 1 | |
| if Options.guidreffilename is not None: | |
| try : | |
| guidreffile = open(Options.guidreffilename) | |
| except Exception: | |
| print("fail to open guidref" + Options.guidreffilename) | |
| return 1 | |
| genGuidString(guidreffile) | |
| guidreffile.close() | |
| SmiHandlerProfile = DOMTree.documentElement | |
| SmiHandlerDatabase = SmiHandlerProfile.getElementsByTagName("SmiHandlerDatabase") | |
| SmiHandlerCategory = SmiHandlerDatabase[0].getElementsByTagName("SmiHandlerCategory") | |
| for smiHandlerCategory in SmiHandlerCategory: | |
| SmiEntry = smiHandlerCategory.getElementsByTagName("SmiEntry") | |
| for smiEntry in SmiEntry: | |
| if smiEntry.hasAttribute("HandlerType"): | |
| guidValue = smiEntry.getAttribute("HandlerType") | |
| if guidValue in dictGuid: | |
| smiEntry.setAttribute("HandlerType", dictGuid[guidValue]) | |
| SmiHandler = smiEntry.getElementsByTagName("SmiHandler") | |
| for smiHandler in SmiHandler: | |
| Module = smiHandler.getElementsByTagName("Module") | |
| Pdb = Module[0].getElementsByTagName("Pdb") | |
| if (len(Pdb)) >= 1: | |
| driverName = Module[0].getAttribute("Name") | |
| pdbName = Pdb[0].childNodes[0].data | |
| Module[0].removeChild(Pdb[0]) | |
| 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) | |
| Handler = smiHandler.getElementsByTagName("Handler") | |
| RVA = Handler[0].getElementsByTagName("RVA") | |
| print(" Handler RVA: %s" % RVA[0].childNodes[0].data) | |
| if (len(RVA)) >= 1: | |
| rvaName = RVA[0].childNodes[0].data | |
| symbolName = getSymbolName (driverName, int(rvaName, 16)) | |
| if (len(symbolName)) >= 1: | |
| SymbolNode = createSym(symbolName) | |
| Handler[0].appendChild(SymbolNode) | |
| Caller = smiHandler.getElementsByTagName("Caller") | |
| RVA = Caller[0].getElementsByTagName("RVA") | |
| print(" Caller RVA: %s" % RVA[0].childNodes[0].data) | |
| if (len(RVA)) >= 1: | |
| rvaName = RVA[0].childNodes[0].data | |
| symbolName = getSymbolName (driverName, int(rvaName, 16)) | |
| if (len(symbolName)) >= 1: | |
| SymbolNode = createSym(symbolName) | |
| Caller[0].appendChild(SymbolNode) | |
| try : | |
| newfile = open(Options.outputfilename, "w") | |
| except Exception: | |
| print("fail to open output" + Options.outputfilename) | |
| return 1 | |
| newfile.write(DOMTree.toprettyxml(indent = "\t", newl = "\n", encoding = "utf-8")) | |
| newfile.close() | |
| if __name__ == '__main__': | |
| sys.exit(main()) |