|
14 | 14 | except ImportError:
|
15 | 15 | from threading import get_ident # Python 3
|
16 | 16 |
|
| 17 | +from collections import defaultdict |
17 | 18 | from contextlib import contextmanager
|
18 | 19 |
|
19 | 20 | class YappiError(Exception): pass
|
@@ -642,26 +643,43 @@ def _save_as_CALLGRIND(self, path):
|
642 | 643 |
|
643 | 644 | lines = [header]
|
644 | 645 |
|
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 |
648 | 657 | 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) |
651 | 661 |
|
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)] |
653 | 671 |
|
654 | 672 | # add stats for each function we have a record of
|
655 | 673 | for func_stat in self:
|
656 | 674 | 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)] |
659 | 677 | func_stats += [ '%s %s' % (func_stat.lineno, int(func_stat.tsub * 1e6)) ]
|
660 | 678 |
|
661 | 679 | # children functions stats
|
662 | 680 | 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), |
665 | 683 | 'calls=%d 0' % child.ncall,
|
666 | 684 | '0 %d' % int(child.ttot * 1e6)
|
667 | 685 | ]
|
|
0 commit comments