1
# this is copied from the lsprof distro because somehow
2
# it is not installed by distutils
3
# I made one modification to profile so that it returns a pair
4
# instead of just the Stats object
11
from _lsprof import Profiler, profiler_entry
13
__all__ = ['profile', 'Stats']
18
def _thread_profile(f, *args, **kwds):
19
# we lose the first profile point for a new thread in order to trampoline
20
# a new Profile object into place
22
thr = thread.get_ident()
23
_g_threadmap[thr] = p = Profiler()
24
# this overrides our sys.setprofile hook:
25
p.enable(subcalls=True, builtins=True)
28
def profile(f, *args, **kwds):
32
p.enable(subcalls=True)
33
threading.setprofile(_thread_profile)
34
# Note: The except clause is needed below so that profiling data still
35
# gets dumped even when exceptions are encountered. The except clause code
36
# is taken straight from run_bzr_catch_errrors() in commands.py and ought
37
# to be kept in sync with it.
39
ret = f(*args, **kwds)
40
except (KeyboardInterrupt, Exception), e:
42
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
46
for pp in _g_threadmap.values():
48
threading.setprofile(None)
51
for tid, pp in _g_threadmap.items():
52
threads[tid] = Stats(pp.getstats(), {})
54
return ret, Stats(p.getstats(), threads)
60
def __init__(self, data, threads):
62
self.threads = threads
64
def sort(self, crit="inlinetime"):
66
if crit not in profiler_entry.__dict__:
67
raise ValueError, "Can't sort by %s" % crit
68
self.data.sort(lambda b, a: cmp(getattr(a, crit),
72
e.calls.sort(lambda b, a: cmp(getattr(a, crit),
75
def pprint(self, top=None, file=None):
82
cols = "% 12s %12s %11.4f %11.4f %s\n"
83
hcols = "% 12s %12s %12s %12s %s\n"
84
cols2 = "+%12s %12s %11.4f %11.4f + %s\n"
85
file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
86
"Inline(ms)", "module:lineno(function)"))
88
file.write(cols % (e.callcount, e.reccallcount, e.totaltime,
89
e.inlinetime, label(e.code)))
92
file.write(cols % ("+%s" % se.callcount, se.reccallcount,
93
se.totaltime, se.inlinetime,
94
"+%s" % label(se.code)))
97
"""Replace all references to code objects with string
98
descriptions; this makes it possible to pickle the instance."""
100
# this code is probably rather ickier than it needs to be!
101
for i in range(len(self.data)):
103
if not isinstance(e.code, str):
104
self.data[i] = type(e)((label(e.code),) + e[1:])
106
for j in range(len(e.calls)):
108
if not isinstance(se.code, str):
109
e.calls[j] = type(se)((label(se.code),) + se[1:])
110
for s in self.threads.values():
113
def calltree(self, file):
114
"""Output profiling data in calltree format (for KCacheGrind)."""
115
_CallTreeFilter(self.data).output(file)
117
def save(self, filename, format=None):
118
"""Save profiling data to a file.
120
:param filename: the name of the output file
121
:param format: 'txt' for a text representation;
122
'callgrind' for calltree format;
123
otherwise a pickled Python object. A format of None indicates
124
that the format to use is to be found from the filename. If
125
the name starts with callgrind.out, callgrind format is used
126
otherwise the format is given by the filename extension.
129
if filename.startswith('callgrind.out'):
132
ext = os.path.splitext(filename)[1]
135
outfile = open(filename, 'wb')
137
if format == "callgrind":
138
self.calltree(outfile)
139
elif format == "txt":
140
self.pprint(file=outfile)
143
cPickle.dump(self, outfile, 2)
148
class _CallTreeFilter(object):
149
"""Converter of a Stats object to input suitable for KCacheGrind.
151
This code is taken from http://ddaa.net/blog/python/lsprof-calltree
152
with the changes made by J.P. Calderone and Itamar applied. Note that
153
isinstance(code, str) needs to be used at times to determine if the code
154
object is actually an external code object (with a filename, etc.) or
158
def __init__(self, data):
162
def output(self, out_file):
163
self.out_file = out_file
164
print >> out_file, 'events: Ticks'
165
self._print_summary()
166
for entry in self.data:
169
def _print_summary(self):
171
for entry in self.data:
172
totaltime = int(entry.totaltime * 1000)
173
max_cost = max(max_cost, totaltime)
174
print >> self.out_file, 'summary: %d' % (max_cost,)
176
def _entry(self, entry):
177
out_file = self.out_file
179
inlinetime = int(entry.inlinetime * 1000)
180
#print >> out_file, 'ob=%s' % (code.co_filename,)
181
if isinstance(code, str):
182
print >> out_file, 'fi=~'
184
print >> out_file, 'fi=%s' % (code.co_filename,)
185
print >> out_file, 'fn=%s' % (label(code, True),)
186
if isinstance(code, str):
187
print >> out_file, '0 ', inlinetime
189
print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
190
# recursive calls are counted in entry.calls
195
if isinstance(code, str):
198
lineno = code.co_firstlineno
199
for subentry in calls:
200
self._subentry(lineno, subentry)
203
def _subentry(self, lineno, subentry):
204
out_file = self.out_file
206
totaltime = int(subentry.totaltime * 1000)
207
#print >> out_file, 'cob=%s' % (code.co_filename,)
208
print >> out_file, 'cfn=%s' % (label(code, True),)
209
if isinstance(code, str):
210
print >> out_file, 'cfi=~'
211
print >> out_file, 'calls=%d 0' % (subentry.callcount,)
213
print >> out_file, 'cfi=%s' % (code.co_filename,)
214
print >> out_file, 'calls=%d %d' % (
215
subentry.callcount, code.co_firstlineno)
216
print >> out_file, '%d %d' % (lineno, totaltime)
220
def label(code, calltree=False):
221
if isinstance(code, str):
224
mname = _fn2mod[code.co_filename]
226
for k, v in sys.modules.items():
229
if getattr(v, '__file__', None) is None:
231
if not isinstance(v.__file__, str):
233
if v.__file__.startswith(code.co_filename):
234
mname = _fn2mod[code.co_filename] = k
237
mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
239
return '%s %s:%d' % (code.co_name, mname, code.co_firstlineno)
241
return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
244
if __name__ == '__main__':
246
sys.argv = sys.argv[1:]
248
print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
250
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
251
stats = profile(execfile, sys.argv[0], globals(), locals())