Skip to content

Commit 30136ea

Browse files
committed
Fix callgrind output compression
callgrind format allows to compress repeated strings into one. yappi was using the syntax for compression but did not compress much. This change makes yappi actually compress repeated strings. kcachegrind merges functions with the same compressed ID into one (as if they were inlined) even if they were defined in different modules. Therefore this change assigns different IDs to different functions with the same name.
1 parent 1736058 commit 30136ea

File tree

1 file changed

+28
-10
lines changed

1 file changed

+28
-10
lines changed

yappi.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
except ImportError:
1515
from threading import get_ident # Python 3
1616

17+
from collections import defaultdict
1718
from contextlib import contextmanager
1819

1920
class YappiError(Exception): pass
@@ -642,26 +643,43 @@ def _save_as_CALLGRIND(self, path):
642643

643644
lines = [header]
644645

645-
# add function definitions
646-
file_ids = ['']
647-
func_ids = ['']
646+
# Each function has a distinct number even if its name already has a
647+
# number because kcachegrind merges functions with the same number.
648+
numbers_seq = enumerate(iter(int, None))
649+
names_seq = iter(lambda: defaultdict(lambda: next(numbers_seq)[0]), None)
650+
modules_seq = enumerate(iter(lambda: defaultdict(lambda: next(names_seq)), None))
651+
modules = defaultdict(lambda: next(modules_seq))
652+
# modules = {'file.py': [module_index, {'func': {line: func_index}}]}
653+
fl = lambda x: modules[x.module][0]
654+
fn = lambda x: modules[x.module][1][x.name][x.lineno]
655+
656+
# enumerate modules and functions
648657
for func_stat in self:
649-
file_ids += [ 'fl=(%d) %s' % (func_stat.index, func_stat.module) ]
650-
func_ids += [ 'fn=(%d) %s %s:%s' % (func_stat.index, func_stat.name, func_stat.module, func_stat.lineno) ]
658+
fn(func_stat)
659+
for child in func_stat.children:
660+
fn(child)
651661

652-
lines += file_ids + func_ids
662+
# add function definitions
663+
for module in sorted(modules):
664+
lines += ['', 'fl=(%d) %s' % (modules[module][0], module)]
665+
for func, defs in sorted(modules[module][1].items()):
666+
suffix = ''
667+
for line in sorted(defs):
668+
if len(defs) > 1: # disambiguate redefined functions
669+
suffix = ' +' + str(line)
670+
lines += ['fn=(%d) %s%s' % (defs[line], func, suffix)]
653671

654672
# add stats for each function we have a record of
655673
for func_stat in self:
656674
func_stats = [ '',
657-
'fl=(%d)' % func_stat.index,
658-
'fn=(%d)' % func_stat.index]
675+
'fl=(%d)' % fl(func_stat),
676+
'fn=(%d)' % fn(func_stat)]
659677
func_stats += [ '%s %s' % (func_stat.lineno, int(func_stat.tsub * 1e6)) ]
660678

661679
# children functions stats
662680
for child in func_stat.children:
663-
func_stats += [ 'cfl=(%d)' % child.index,
664-
'cfn=(%d)' % child.index,
681+
func_stats += [ 'cfl=(%d)' % fl(child),
682+
'cfn=(%d)' % fn(child),
665683
'calls=%d 0' % child.ncall,
666684
'0 %d' % int(child.ttot * 1e6)
667685
]

0 commit comments

Comments
 (0)