#!/usr/bin/python3 """List the leaf functions in callgrind output. """ import sys, re def leaves(lines, log=lambda line: None): names = {} functions = set() nonleaves = set() calls = {} for line in lines: if mo := re.match(r'^fn=\((\d+)\)\s*$', line): num = mo.group(1) current_fn = num functions.add(num) elif mo := re.match(r'^fn=\((\d+)\)\s*(\S.+)$', line): num, name = mo.groups() current_fn = num names[num] = name functions.add(num) elif mo := re.match(r'^fn=.*', line): # The number is supposed to be optional but I don’t have # test data for this. raise UnimplementedError(line) elif mo := re.match(r'^cfn=\((\d+)\)\s*$', line): nonleaves.add(current_fn) current_callee = mo.group(1) elif mo := re.match(r'^cfn=\((\d+)\)\s*(\S.+)$', line): num, name = mo.groups() names[num] = name current_callee = num nonleaves.add(current_fn) elif mo := re.match(r'^calls=(\d+)', line): if current_callee not in calls: calls[current_callee] = 0 calls[current_callee] += int(mo.group(1)) log(f'{sum(calls.values())} total calls in {len(calls)} functions\n') # XXX dirty for num in functions - nonleaves: name = names[num] if num in names else f'({num})' yield name, calls.get(num, 0) if __name__ == '__main__': for leaf, calls in leaves(sys.stdin, log=sys.stderr.write): print(calls, leaf)