# HG changeset patch # User Nicolas Dumazet # Date 1239193257 -7200 # Node ID 9a1b86cfd29e4d9965e985a299370161c7efa046 # Parent fd9debb3ea1b27a43f6129a4add09f8bad1ee4e5 profiling: Adding support for kcachegrind output format, using lsprofcalltree diff -r fd9debb3ea1b -r 9a1b86cfd29e doc/hgrc.5.txt --- a/doc/hgrc.5.txt Thu Apr 02 16:01:30 2009 +0900 +++ b/doc/hgrc.5.txt Wed Apr 08 14:20:57 2009 +0200 @@ -552,6 +552,10 @@ Generate a profiling report. When saving to a file, it should be noted that only the report is saved, and the profiling data is not kept. + kcachegrind;; + Format profiling data for kcachegrind use: + when saving to a file, the generated file can directly be loaded + into kcachegrind. output;; File path where profiling data or report should be saved. If the file exists, it is replaced. diff -r fd9debb3ea1b -r 9a1b86cfd29e mercurial/dispatch.py --- a/mercurial/dispatch.py Thu Apr 02 16:01:30 2009 +0900 +++ b/mercurial/dispatch.py Wed Apr 08 14:20:57 2009 +0200 @@ -381,7 +381,7 @@ if options['profile']: format = ui.config('profiling', 'format', default='text') - if not format in ['text']: + if not format in ['text', 'kcachegrind']: ui.warn(_("unrecognized profiling format '%s'" " - Ignored\n") % format) format = 'text' @@ -407,9 +407,16 @@ return checkargs() finally: p.disable() - stats = lsprof.Stats(p.getstats()) - stats.sort() - stats.pprint(top=10, file=ostream, climit=5) + + if format == 'kcachegrind': + import lsprofcalltree + calltree = lsprofcalltree.KCacheGrind(p) + calltree.output(ostream) + else: + # format == 'text' + stats = lsprof.Stats(p.getstats()) + stats.sort() + stats.pprint(top=10, file=ostream, climit=5) if output: ostream.close() diff -r fd9debb3ea1b -r 9a1b86cfd29e mercurial/lsprofcalltree.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/lsprofcalltree.py Wed Apr 08 14:20:57 2009 +0200 @@ -0,0 +1,90 @@ +""" +lsprofcalltree.py - lsprof output which is readable by kcachegrind + +Authors: + * David Allouche allouche.net> + * Jp Calderone & Itamar Shtull-Trauring + * Johan Dahlin + +This software may be used and distributed according to the terms +of the GNU General Public License, incorporated herein by reference. +""" + +import optparse +import os +import sys + +def label(code): + if isinstance(code, str): + return '~' + code # built-in functions ('~' sorts at the end) + else: + return '%s %s:%d' % (code.co_name, + code.co_filename, + code.co_firstlineno) + +class KCacheGrind(object): + def __init__(self, profiler): + self.data = profiler.getstats() + self.out_file = None + + def output(self, out_file): + self.out_file = out_file + print >> out_file, 'events: Ticks' + self._print_summary() + for entry in self.data: + self._entry(entry) + + def _print_summary(self): + max_cost = 0 + for entry in self.data: + totaltime = int(entry.totaltime * 1000) + max_cost = max(max_cost, totaltime) + print >> self.out_file, 'summary: %d' % (max_cost,) + + def _entry(self, entry): + out_file = self.out_file + + code = entry.code + #print >> out_file, 'ob=%s' % (code.co_filename,) + if isinstance(code, str): + print >> out_file, 'fi=~' + else: + print >> out_file, 'fi=%s' % (code.co_filename,) + print >> out_file, 'fn=%s' % (label(code),) + + inlinetime = int(entry.inlinetime * 1000) + if isinstance(code, str): + print >> out_file, '0 ', inlinetime + else: + print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime) + + # recursive calls are counted in entry.calls + if entry.calls: + calls = entry.calls + else: + calls = [] + + if isinstance(code, str): + lineno = 0 + else: + lineno = code.co_firstlineno + + for subentry in calls: + self._subentry(lineno, subentry) + print >> out_file + + def _subentry(self, lineno, subentry): + out_file = self.out_file + code = subentry.code + #print >> out_file, 'cob=%s' % (code.co_filename,) + print >> out_file, 'cfn=%s' % (label(code),) + if isinstance(code, str): + print >> out_file, 'cfi=~' + print >> out_file, 'calls=%d 0' % (subentry.callcount,) + else: + print >> out_file, 'cfi=%s' % (code.co_filename,) + print >> out_file, 'calls=%d %d' % ( + subentry.callcount, code.co_firstlineno) + + totaltime = int(subentry.totaltime * 1000) + print >> out_file, '%d %d' % (lineno, totaltime) diff -r fd9debb3ea1b -r 9a1b86cfd29e tests/test-profile --- a/tests/test-profile Thu Apr 02 16:01:30 2009 +0900 +++ b/tests/test-profile Wed Apr 08 14:20:57 2009 +0200 @@ -18,4 +18,15 @@ hg --profile --config profiling.format=text st 2>&1 \ | grep CallCount > /dev/null || echo --profile format=text failed + + echo "[profiling]" >> $HGRCPATH + echo "format=kcachegrind" >> $HGRCPATH + + hg --profile st 2>../out || echo --profile format=kcachegrind failed + grep 'events: Ticks' < ../out > /dev/null || echo --profile output is wrong + + hg --profile --config profiling.output=../out st 2>&1 \ + || echo --profile format=kcachegrind + output to file failed + grep 'events: Ticks' < ../out > /dev/null \ + || echo --profile output is wrong fi