26
29
def profile(f, *args, **kwds):
30
"""Run a function profile.
32
:return: The functions return value and a stats object.
28
34
global _g_threadmap
30
36
p.enable(subcalls=True)
31
37
threading.setprofile(_thread_profile)
38
# Note: The except clause is needed below so that profiling data still
39
# gets dumped even when exceptions are encountered. The except clause code
40
# is taken straight from run_bzr_catch_errrors() in commands.py and ought
41
# to be kept in sync with it.
33
ret = f(*args, **kwds)
44
ret = f(*args, **kwds)
45
except (KeyboardInterrupt, Exception), e:
47
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
36
51
for pp in _g_threadmap.values():
104
119
"""Output profiling data in calltree format (for KCacheGrind)."""
105
120
_CallTreeFilter(self.data).output(file)
122
def save(self, filename, format=None):
123
"""Save profiling data to a file.
125
:param filename: the name of the output file
126
:param format: 'txt' for a text representation;
127
'callgrind' for calltree format;
128
otherwise a pickled Python object. A format of None indicates
129
that the format to use is to be found from the filename. If
130
the name starts with callgrind.out, callgrind format is used
131
otherwise the format is given by the filename extension.
134
basename = os.path.basename(filename)
135
if basename.startswith('callgrind.out'):
138
ext = os.path.splitext(filename)[1]
141
outfile = open(filename, 'wb')
143
if format == "callgrind":
144
self.calltree(outfile)
145
elif format == "txt":
146
self.pprint(file=outfile)
149
cPickle.dump(self, outfile, 2)
108
154
class _CallTreeFilter(object):
155
"""Converter of a Stats object to input suitable for KCacheGrind.
157
This code is taken from http://ddaa.net/blog/python/lsprof-calltree
158
with the changes made by J.P. Calderone and Itamar applied. Note that
159
isinstance(code, str) needs to be used at times to determine if the code
160
object is actually an external code object (with a filename, etc.) or
110
164
def __init__(self, data):
123
177
for entry in self.data:
124
178
totaltime = int(entry.totaltime * 1000)
125
179
max_cost = max(max_cost, totaltime)
126
print >> self.out_file, 'summary: %d' % (max_cost,)
180
self.out_file.write('summary: %d\n' % (max_cost,))
128
182
def _entry(self, entry):
129
183
out_file = self.out_file
130
184
code = entry.code
131
185
inlinetime = int(entry.inlinetime * 1000)
132
#print >> out_file, 'ob=%s' % (code.co_filename,)
133
print >> out_file, 'fi=%s' % (code.co_filename,)
134
print >> out_file, 'fn=%s' % (label(code, True),)
135
print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
186
#out_file.write('ob=%s\n' % (code.co_filename,))
187
if isinstance(code, str):
188
out_file.write('fi=~\n')
190
out_file.write('fi=%s\n' % (code.co_filename,))
191
out_file.write('fn=%s\n' % (label(code, True),))
192
if isinstance(code, str):
193
out_file.write('0 %s\n' % (inlinetime,))
195
out_file.write('%d %d\n' % (code.co_firstlineno, inlinetime))
136
196
# recursive calls are counted in entry.calls
138
198
calls = entry.calls
201
if isinstance(code, str):
204
lineno = code.co_firstlineno
141
205
for subentry in calls:
142
self._subentry(code.co_firstlineno, subentry)
206
self._subentry(lineno, subentry)
145
209
def _subentry(self, lineno, subentry):
146
210
out_file = self.out_file
147
211
code = subentry.code
148
212
totaltime = int(subentry.totaltime * 1000)
149
#print >> out_file, 'cob=%s' % (code.co_filename,)
150
print >> out_file, 'cfn=%s' % (label(code, True),)
151
print >> out_file, 'cfi=%s' % (code.co_filename,)
152
print >> out_file, 'calls=%d %d' % (
153
subentry.callcount, code.co_firstlineno)
154
print >> out_file, '%d %d' % (lineno, totaltime)
213
#out_file.write('cob=%s\n' % (code.co_filename,))
214
out_file.write('cfn=%s\n' % (label(code, True),))
215
if isinstance(code, str):
216
out_file.write('cfi=~\n')
217
out_file.write('calls=%d 0\n' % (subentry.callcount,))
219
out_file.write('cfi=%s\n' % (code.co_filename,))
220
out_file.write('calls=%d %d\n' % (
221
subentry.callcount, code.co_firstlineno))
222
out_file.write('%d %d\n' % (lineno, totaltime))