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