"""
Script to calculate the Cyclomatic Complexity of every function.
Joxean Koret, 2019
Public Domain
"""
import idaapi
from tarjan_sort import strongly_connected_components
#-------------------------------------------------------------------------------
class CCFGDataChooser(idaapi.Choose2):
def __init__(self, title, items):
idaapi.Choose2.__init__(self,
title,
[ ["Address", 8], ["Name", 50], ["Nodes", 8],
["Edges", 8], ["CC", 8], ["Loops", 8], ["Switchs", 8]])
self.items = []
for item in items:
element = ["0x%08x" % item[0], item[1],
"%04d" % item[2], "%04d" % item[3], "%04d" % item[4],
"%04d" % item[5], "%03d" % item[6]]
self.items.append(element)
def OnSelectLine(self, n):
ea = int(self.items[n][0], 16)
jumpto(ea)
def OnGetLine(self, n):
return self.items[n]
def OnGetSize(self):
return len(self.items)
#-------------------------------------------------------------------------------
def analyze_function(ea):
nodes = 0
edges = 0
cc = 0
g = {}
switches = set()
func = idaapi.get_func(ea)
if func is not None:
nodes = 0
edges = 0
flow = idaapi.FlowChart(func)
for block in flow:
nodes += 1
g[block.startEA] = []
for succ in block.succs():
edges += 1
g[block.startEA].append(succ.startEA)
for pred in block.preds():
edges += 1
try:
g[pred.startEA].append(block.startEA)
except KeyError:
g[pred.startEA] = [block.startEA]
if block.startEA == 0:
continue
for head in Heads(block.startEA, block.endEA):
switch = idaapi.get_switch_info_ex(head)
if switch:
switches.add(switch.startea)
cc = edges - nodes + 2
return nodes, edges, cc, g, len(switches)
#-------------------------------------------------------------------------------
def main():
vals = []
items = []
for ea in Functions():
nodes, edges, cc, graph, switches = analyze_function(ea)
try:
scc = strongly_connected_components(graph)
loops = 0
for item in scc:
val = len(item)
if val > 1:
loops += 1
except:
loops = -1
name = GetFunctionName(ea)
tmp = Demangle(name, INF_SHORT_DN)
if tmp != "" and tmp is not None:
name = tmp
items.append([ea, name, nodes, edges, cc, loops, switches])
vals.append(cc)
line = "Cyclomatic complexity: Max %d, Avg %d"
print(line % (max(vals), sum(vals)/len(vals)))
ch = CCFGDataChooser("Control Flow Graphs Data", items)
ch.Show()
if __name__ == "__main__":
main()
Comments