Skip to content

Commit 4a01fdb

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 bf5fcf1 commit 4a01fdb

File tree

1 file changed

+29
-10
lines changed

1 file changed

+29
-10
lines changed

yappi.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
Sumer Cip 2014
66
"""
7+
import itertools
78
import os
89
import sys
910
import _yappi
@@ -14,6 +15,7 @@
1415
except ImportError:
1516
from threading import get_ident # Python 3
1617

18+
from collections import defaultdict
1719
from contextlib import contextmanager
1820

1921
class YappiError(Exception): pass
@@ -648,26 +650,43 @@ def _save_as_CALLGRIND(self, path):
648650

649651
lines = [header]
650652

651-
# add function definitions
652-
file_ids = ['']
653-
func_ids = ['']
653+
# Each function has a distinct number even if its name already has a
654+
# number because kcachegrind merges functions with the same number.
655+
numbers_seq = itertools.count()
656+
func_defs = lambda: defaultdict(lambda: defaultdict(lambda: next(numbers_seq)))
657+
modules_seq = enumerate(iter(func_defs, None))
658+
modules = defaultdict(lambda: next(modules_seq))
659+
# modules = {'file.py': [module_index, {'func': {line: func_index}}]}
660+
fl = lambda x: modules[x.module][0]
661+
fn = lambda x: modules[x.module][1][x.name][x.lineno]
662+
663+
# enumerate modules and functions
654664
for func_stat in self:
655-
file_ids += [ 'fl=(%d) %s' % (func_stat.index, func_stat.module) ]
656-
func_ids += [ 'fn=(%d) %s %s:%s' % (func_stat.index, func_stat.name, func_stat.module, func_stat.lineno) ]
665+
fn(func_stat)
666+
for child in func_stat.children:
667+
fn(child)
657668

658-
lines += file_ids + func_ids
669+
# add function definitions
670+
for module in sorted(modules):
671+
lines += ['', 'fl=(%d) %s' % (modules[module][0], module)]
672+
for func, defs in sorted(modules[module][1].items()):
673+
suffix = ''
674+
for line in sorted(defs):
675+
if len(defs) > 1: # disambiguate redefined functions
676+
suffix = ' +' + str(line)
677+
lines += ['fn=(%d) %s%s' % (defs[line], func, suffix)]
659678

660679
# add stats for each function we have a record of
661680
for func_stat in self:
662681
func_stats = [ '',
663-
'fl=(%d)' % func_stat.index,
664-
'fn=(%d)' % func_stat.index]
682+
'fl=(%d)' % fl(func_stat),
683+
'fn=(%d)' % fn(func_stat)]
665684
func_stats += [ '%s %s' % (func_stat.lineno, int(func_stat.tsub * 1e6)) ]
666685

667686
# children functions stats
668687
for child in func_stat.children:
669-
func_stats += [ 'cfl=(%d)' % child.index,
670-
'cfn=(%d)' % child.index,
688+
func_stats += [ 'cfl=(%d)' % fl(child),
689+
'cfn=(%d)' % fn(child),
671690
'calls=%d 0' % child.ncall,
672691
'0 %d' % int(child.ttot * 1e6)
673692
]

0 commit comments

Comments
 (0)