2016-06-26 07:07:26 +02:00
##
# 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
2016-07-04 12:39:34 +02:00
versionNumber = " 1.1 "
2016-06-26 07:07:26 +02:00
__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
2016-07-04 12:39:34 +02:00
patchLineFileMatchString = " ([0-9a-fA-F]*) \ s+[T|D|t|d] \ s+( \ w+) \ s*((?:[a-zA-Z]:)?[ \ w+ \ -./_a-zA-Z0-9 \\ \\ ]*):?([0-9]*) "
2016-06-26 07:07:26 +02:00
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 ( ) )