2017-01-22 12:28:13 +01:00
##
# 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>
# 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.
#
##
2018-06-25 12:31:26 +02:00
from __future__ import print_function
2017-01-22 12:28:13 +01:00
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 "
2018-06-25 12:31:26 +02:00
print ( " parsing (debug) - " + pdbName )
2017-01-22 12:28:13 +01:00
os . system ( ' %s %s %s > nmDump.line.log ' % ( nmCommand , nmLineOption , pdbName ) )
except :
2018-06-25 12:31:26 +02:00
print ( ' ERROR: nm command not available. Please verify PATH ' )
2017-01-22 12:28:13 +01:00
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 "
2018-06-25 12:31:26 +02:00
print ( " parsing (pdb) - " + pdbName )
2017-01-22 12:28:13 +01:00
#os.system ('%s %s %s > DIA2Dump.symbol.log' % (DIA2DumpCommand, DIA2SymbolOption, pdbName))
os . system ( ' %s %s %s > DIA2Dump.line.log ' % ( DIA2DumpCommand , DIA2LinesOption , pdbName ) )
except :
2018-06-25 12:31:26 +02:00
print ( ' ERROR: DIA2Dump command not available. Please verify PATH ' )
2017-01-22 12:28:13 +01:00
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 ]
2018-04-06 01:14:02 +02:00
if guid not in dictGuid :
2017-01-22 12:28:13 +01:00
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 :
2018-06-25 12:31:26 +02:00
print ( " fail to open input " + Options . inputfilename )
2017-01-22 12:28:13 +01:00
return 1
if Options . guidreffilename is not None :
try :
guidreffile = open ( Options . guidreffilename )
except Exception :
2018-06-25 12:31:26 +02:00
print ( " fail to open guidref " + Options . guidreffilename )
2017-01-22 12:28:13 +01:00
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 " )
2018-04-06 01:14:02 +02:00
if guidValue in dictGuid :
2017-01-22 12:28:13 +01:00
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 " )
2018-06-25 12:31:26 +02:00
print ( " Handler RVA: %s " % RVA [ 0 ] . childNodes [ 0 ] . data )
2017-01-22 12:28:13 +01:00
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 " )
2018-06-25 12:31:26 +02:00
print ( " Caller RVA: %s " % RVA [ 0 ] . childNodes [ 0 ] . data )
2017-01-22 12:28:13 +01:00
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 :
2018-06-25 12:31:26 +02:00
print ( " fail to open output " + Options . outputfilename )
2017-01-22 12:28:13 +01:00
return 1
newfile . write ( DOMTree . toprettyxml ( indent = " \t " , newl = " \n " , encoding = " utf-8 " ) )
newfile . close ( )
if __name__ == ' __main__ ' :
sys . exit ( main ( ) )