1## 2# Generate symbal for memory profile info. 3# 4# This tool depends on DIA2Dump.exe (VS) or nm (gcc) to parse debug entry. 5# 6# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> 7# SPDX-License-Identifier: BSD-2-Clause-Patent 8# 9## 10 11from __future__ import print_function 12import os 13import re 14import sys 15from optparse import OptionParser 16 17versionNumber = "1.1" 18__copyright__ = "Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved." 19 20class Symbols: 21 def __init__(self): 22 self.listLineAddress = [] 23 self.pdbName = "" 24 # Cache for function 25 self.functionName = "" 26 # Cache for line 27 self.sourceName = "" 28 29 30 def getSymbol (self, rva): 31 index = 0 32 lineName = 0 33 sourceName = "??" 34 while index + 1 < self.lineCount : 35 if self.listLineAddress[index][0] <= rva and self.listLineAddress[index + 1][0] > rva : 36 offset = rva - self.listLineAddress[index][0] 37 functionName = self.listLineAddress[index][1] 38 lineName = self.listLineAddress[index][2] 39 sourceName = self.listLineAddress[index][3] 40 if lineName == 0 : 41 return " (" + self.listLineAddress[index][1] + "() - " + ")" 42 else : 43 return " (" + self.listLineAddress[index][1] + "() - " + sourceName + ":" + str(lineName) + ")" 44 index += 1 45 46 return " (unknown)" 47 48 def parse_debug_file(self, driverName, pdbName): 49 if cmp (pdbName, "") == 0 : 50 return 51 self.pdbName = pdbName; 52 53 try: 54 nmCommand = "nm" 55 nmLineOption = "-l" 56 print("parsing (debug) - " + pdbName) 57 os.system ('%s %s %s > nmDump.line.log' % (nmCommand, nmLineOption, pdbName)) 58 except : 59 print('ERROR: nm command not available. Please verify PATH') 60 return 61 62 # 63 # parse line 64 # 65 linefile = open("nmDump.line.log") 66 reportLines = linefile.readlines() 67 linefile.close() 68 69 # 000113ca T AllocatePool c:\home\edk-ii\MdePkg\Library\UefiMemoryAllocationLib\MemoryAllocationLib.c:399 70 patchLineFileMatchString = "([0-9a-fA-F]*)\s+[T|D|t|d]\s+(\w+)\s*((?:[a-zA-Z]:)?[\w+\-./_a-zA-Z0-9\\\\]*):?([0-9]*)" 71 72 for reportLine in reportLines: 73 #print "check - " + reportLine 74 match = re.match(patchLineFileMatchString, reportLine) 75 if match is not None: 76 #print "match - " + reportLine[:-1] 77 #print "0 - " + match.group(0) 78 #print "1 - " + match.group(1) 79 #print "2 - " + match.group(2) 80 #print "3 - " + match.group(3) 81 #print "4 - " + match.group(4) 82 83 rva = int (match.group(1), 16) 84 functionName = match.group(2) 85 sourceName = match.group(3) 86 if cmp (match.group(4), "") != 0 : 87 lineName = int (match.group(4)) 88 else : 89 lineName = 0 90 self.listLineAddress.append ([rva, functionName, lineName, sourceName]) 91 92 self.lineCount = len (self.listLineAddress) 93 94 self.listLineAddress = sorted(self.listLineAddress, key=lambda symbolAddress:symbolAddress[0]) 95 96 #for key in self.listLineAddress : 97 #print "rva - " + "%x"%(key[0]) + ", func - " + key[1] + ", line - " + str(key[2]) + ", source - " + key[3] 98 99 def parse_pdb_file(self, driverName, pdbName): 100 if cmp (pdbName, "") == 0 : 101 return 102 self.pdbName = pdbName; 103 104 try: 105 #DIA2DumpCommand = "\"C:\\Program Files (x86)\Microsoft Visual Studio 14.0\\DIA SDK\\Samples\\DIA2Dump\\x64\\Debug\\Dia2Dump.exe\"" 106 DIA2DumpCommand = "Dia2Dump.exe" 107 #DIA2SymbolOption = "-p" 108 DIA2LinesOption = "-l" 109 print("parsing (pdb) - " + pdbName) 110 #os.system ('%s %s %s > DIA2Dump.symbol.log' % (DIA2DumpCommand, DIA2SymbolOption, pdbName)) 111 os.system ('%s %s %s > DIA2Dump.line.log' % (DIA2DumpCommand, DIA2LinesOption, pdbName)) 112 except : 113 print('ERROR: DIA2Dump command not available. Please verify PATH') 114 return 115 116 # 117 # parse line 118 # 119 linefile = open("DIA2Dump.line.log") 120 reportLines = linefile.readlines() 121 linefile.close() 122 123 # ** GetDebugPrintErrorLevel 124 # line 32 at [0000C790][0001:0000B790], len = 0x3 c:\home\edk-ii\mdepkg\library\basedebugprinterrorlevellib\basedebugprinterrorlevellib.c (MD5: 687C0AE564079D35D56ED5D84A6164CC) 125 # line 36 at [0000C793][0001:0000B793], len = 0x5 126 # line 37 at [0000C798][0001:0000B798], len = 0x2 127 128 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*" 129 patchLineFileMatchStringFunc = "\*\*\s+(\w+)\s*" 130 131 for reportLine in reportLines: 132 #print "check line - " + reportLine 133 match = re.match(patchLineFileMatchString, reportLine) 134 if match is not None: 135 #print "match - " + reportLine[:-1] 136 #print "0 - " + match.group(0) 137 #print "1 - " + match.group(1) 138 #print "2 - " + match.group(2) 139 if cmp (match.group(3), "") != 0 : 140 self.sourceName = match.group(3) 141 sourceName = self.sourceName 142 functionName = self.functionName 143 144 rva = int (match.group(2), 16) 145 lineName = int (match.group(1)) 146 self.listLineAddress.append ([rva, functionName, lineName, sourceName]) 147 else : 148 match = re.match(patchLineFileMatchStringFunc, reportLine) 149 if match is not None: 150 self.functionName = match.group(1) 151 152 self.lineCount = len (self.listLineAddress) 153 self.listLineAddress = sorted(self.listLineAddress, key=lambda symbolAddress:symbolAddress[0]) 154 155 #for key in self.listLineAddress : 156 #print "rva - " + "%x"%(key[0]) + ", func - " + key[1] + ", line - " + str(key[2]) + ", source - " + key[3] 157 158class SymbolsFile: 159 def __init__(self): 160 self.symbolsTable = {} 161 162symbolsFile = "" 163 164driverName = "" 165rvaName = "" 166symbolName = "" 167 168def getSymbolName(driverName, rva): 169 global symbolsFile 170 171 #print "driverName - " + driverName 172 173 try : 174 symbolList = symbolsFile.symbolsTable[driverName] 175 if symbolList is not None: 176 return symbolList.getSymbol (rva) 177 else: 178 return " (???)" 179 except Exception: 180 return " (???)" 181 182def processLine(newline): 183 global driverName 184 global rvaName 185 186 driverPrefixLen = len("Driver - ") 187 # get driver name 188 if cmp(newline[0:driverPrefixLen], "Driver - ") == 0 : 189 driverlineList = newline.split(" ") 190 driverName = driverlineList[2] 191 #print "Checking : ", driverName 192 193 # EDKII application output 194 pdbMatchString = "Driver - \w* \(Usage - 0x[0-9a-fA-F]+\) \(Pdb - ([:\-.\w\\\\/]*)\)\s*" 195 pdbName = "" 196 match = re.match(pdbMatchString, newline) 197 if match is not None: 198 #print "match - " + newline 199 #print "0 - " + match.group(0) 200 #print "1 - " + match.group(1) 201 pdbName = match.group(1) 202 #print "PDB - " + pdbName 203 204 symbolsFile.symbolsTable[driverName] = Symbols() 205 206 if cmp (pdbName[-3:], "pdb") == 0 : 207 symbolsFile.symbolsTable[driverName].parse_pdb_file (driverName, pdbName) 208 else : 209 symbolsFile.symbolsTable[driverName].parse_debug_file (driverName, pdbName) 210 211 elif cmp(newline, "") == 0 : 212 driverName = "" 213 214 # check entry line 215 if newline.find ("<==") != -1 : 216 entry_list = newline.split(" ") 217 rvaName = entry_list[4] 218 #print "rva : ", rvaName 219 symbolName = getSymbolName (driverName, int(rvaName, 16)) 220 else : 221 rvaName = "" 222 symbolName = "" 223 224 if cmp(rvaName, "") == 0 : 225 return newline 226 else : 227 return newline + symbolName 228 229def myOptionParser(): 230 usage = "%prog [--version] [-h] [--help] [-i inputfile [-o outputfile]]" 231 Parser = OptionParser(usage=usage, description=__copyright__, version="%prog " + str(versionNumber)) 232 Parser.add_option("-i", "--inputfile", dest="inputfilename", type="string", help="The input memory profile info file output from MemoryProfileInfo application in MdeModulePkg") 233 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") 234 235 (Options, args) = Parser.parse_args() 236 if Options.inputfilename is None: 237 Parser.error("no input file specified") 238 if Options.outputfilename is None: 239 Options.outputfilename = "MemoryProfileInfoSymbol.txt" 240 return Options 241 242def main(): 243 global symbolsFile 244 global Options 245 Options = myOptionParser() 246 247 symbolsFile = SymbolsFile() 248 249 try : 250 file = open(Options.inputfilename) 251 except Exception: 252 print("fail to open " + Options.inputfilename) 253 return 1 254 try : 255 newfile = open(Options.outputfilename, "w") 256 except Exception: 257 print("fail to open " + Options.outputfilename) 258 return 1 259 260 try: 261 while True: 262 line = file.readline() 263 if not line: 264 break 265 newline = line[:-1] 266 267 newline = processLine(newline) 268 269 newfile.write(newline) 270 newfile.write("\n") 271 finally: 272 file.close() 273 newfile.close() 274 275if __name__ == '__main__': 276 sys.exit(main()) 277