# lsprofcalltree.py: lsprof output which is readable by kcachegrind # David Allouche # Jp Calderone & Itamar Shtull-Trauring # Johan Dahlin import optparse import os import sys try: import cProfile except ImportError: raise SystemExit("This script requires cProfile from Python 2.5") def label(code): if isinstance(code, str): return ('~', 0, 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) def main(args): usage = "%s [-o output_file_path] scriptfile [arg] ..." parser = optparse.OptionParser(usage=usage % sys.argv[0]) parser.allow_interspersed_args = False parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) if not sys.argv[1:]: parser.print_usage() sys.exit(2) options, args = parser.parse_args() if not options.outfile: options.outfile = '%s.log' % os.path.basename(args[0]) sys.argv[:] = args prof = cProfile.Profile() try: try: prof = prof.run('execfile(%r)' % (sys.argv[0],)) except SystemExit: pass finally: kg = KCacheGrind(prof) kg.output(file(options.outfile, 'w')) if __name__ == '__main__': sys.exit(main(sys.argv))