winIDEA SDK
Loading...
Searching...
No Matches
analyze_call_tree.py
# This script is licensed under BSD License, see file LICENSE.txt, or search for `License` in the SDK online help.
#
# (c) TASKING Germany GmbH, 2023
"""
This script generates a call tree for a function from its disassembly
information. This script is ony a proof of concept. Use testIDEA to
generate scripts, which produce more readable call graphs.
"""
import sys
import isystem.connect as ic
import subprocess as sp
winidea_id = ''
graph = ""
vertex = set()
def createGraphImage(graphFileName):
print('Creating call graph image ...')
cmd = 'dot -Tpng -O ' + graphFileName
try:
sp.check_call(cmd, shell=True)
except Exception as ex:
print("\nERROR: Can not run the graphviz 'dot' utility. Command: ", cmd,
'\n\n' + str(ex), file=sys.stderr)
raise
return graphFileName + ".png"
def writeGraphFile(outFileName, graphStr):
print('Writing graph calls to file: ', outFileName)
with open(outFileName, 'w') as outf:
outf.write(graphStr)
outf.write('\n')
outf.close()
return outFileName
def printFunction(cmgr, functionName):
global graph
print("Printing: " + functionName)
graph += "\"" + functionName + "\" " + \
"[shape=circle, style=bold, label=\"" + \
str(functionName) + "\"];\n"
debugCtrl = ic.CDebugFacade(cmgr)
try:
functionInfo = debugCtrl.getSymbolInfo(ic.IConnectDebug.fCacheCode | ic.IConnectDebug.fRealTime | ic.IConnectDebug.gafFunctions, functionName)
except Exception:
print("Not a known function")
return
dataCtrl2 = ic.CDataController2(cmgr)
disassembly = dataCtrl2.getDisassembly(0,
functionInfo.getMemArea(),
functionInfo.getAddress(),
functionInfo.getSizeMAUs())
daLines = disassembly.Lines()
numLines = daLines.size()
for idx in range(numLines):
disassemblyLine = daLines.at(idx)
if disassemblyLine.IsCall():
symbolName: str = debugCtrl.getSymbolAtAddress(ic.IConnectDebug.sFunctions,
functionInfo.getMemArea(),
disassemblyLine.BranchTarget())
if not symbolName:
symbolName = hex(disassemblyLine.BranchTarget())
# some qualified symbols have names like "debug.c#"static_func
# and quotes interfere with dot syntax, so replace them
symbolName = symbolName.replace('"', "'")
graph += "\"" + str(functionName) + "\" -> \"" + str(symbolName) + "\"\n"
if symbolName not in vertex:
vertex.add(symbolName)
printFunction(cmgr, symbolName)
# It is important that we release the allocated resource
dataCtrl2.release(disassembly)
def analyzeFunction(cmgr, functionName):
global graph
graph += "digraph {\n"
graph += "graph [rank=max]\n"
printFunction(cmgr, functionName)
graph += "}\n\n"
return graph
def main():
global graph
if len(sys.argv) != 2:
print("Usage: ", sys.argv[0], " <function>")
print('defaulting to "main"')
function = 'main'
else:
function = sys.argv[1]
try:
cmgr = ic.ConnectionMgr()
cmgr.connect(ic.CConnectionConfig().instanceId(winidea_id))
graph = analyzeFunction(cmgr, function)
graphFileName = writeGraphFile(str(function) + ".tree.dot", graph)
imageFileName = createGraphImage(graphFileName)
try:
# this opens default png viewer on Windows
sp.check_call('start ' + imageFileName, shell=True)
except Exception:
# try with default viewer on KUbuntu
sp.check_call('gwenview ' + imageFileName, shell=True)
except Exception as ex:
print(str(ex), file=sys.stderr)
if __name__ == "__main__":
main()