11
11
from _lsprof import Profiler, profiler_entry
14
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)
16
28
def profile(f, *args, **kwds):
17
"""Run a function profile.
19
Exceptions are not caught: If you need stats even when exceptions are to be
20
raised, pass in a closure that will catch the exceptions and transform them
21
appropriately for your driver function.
23
:return: The functions return value and a stats object.
25
profiler = BzrProfiler()
32
p.enable(subcalls=True)
33
threading.setprofile(_thread_profile)
28
35
ret = f(*args, **kwds)
30
stats = profiler.stop()
34
class BzrProfiler(object):
35
"""Bzr utility wrapper around Profiler.
37
For most uses the module level 'profile()' function will be suitable.
38
However profiling when a simple wrapped function isn't available may
39
be easier to accomplish using this class.
41
To use it, create a BzrProfiler and call start() on it. Some arbitrary
42
time later call stop() to stop profiling and retrieve the statistics
43
from the code executed in the interim.
49
This hooks into threading and will record all calls made until
52
self._g_threadmap = {}
54
self.p.enable(subcalls=True)
55
threading.setprofile(self._thread_profile)
60
This unhooks from threading and cleans up the profiler, returning
61
the gathered Stats object.
63
:return: A bzrlib.lsprof.Stats object.
66
for pp in self._g_threadmap.values():
38
for pp in _g_threadmap.values():
68
40
threading.setprofile(None)
72
for tid, pp in self._g_threadmap.items():
73
threads[tid] = Stats(pp.getstats(), {})
74
self._g_threadmap = None
75
return Stats(p.getstats(), threads)
77
def _thread_profile(self, f, *args, **kwds):
78
# we lose the first profile point for a new thread in order to
79
# trampoline a new Profile object into place
80
thr = thread.get_ident()
81
self._g_threadmap[thr] = p = Profiler()
82
# this overrides our sys.setprofile hook:
83
p.enable(subcalls=True, builtins=True)
43
for tid, pp in _g_threadmap.items():
44
threads[tid] = Stats(pp.getstats(), {})
46
return ret, Stats(p.getstats(), threads)
86
49
class Stats(object):
150
113
:param format: 'txt' for a text representation;
151
114
'callgrind' for calltree format;
152
115
otherwise a pickled Python object. A format of None indicates
153
that the format to use is to be found from the filename. If
154
the name starts with callgrind.out, callgrind format is used
155
otherwise the format is given by the filename extension.
116
that the format to use is to be found from the extension of
157
119
if format is None:
158
basename = os.path.basename(filename)
159
if basename.startswith('callgrind.out'):
162
ext = os.path.splitext(filename)[1]
120
ext = os.path.splitext(filename)[1]
165
123
outfile = open(filename, 'wb')
167
125
if format == "callgrind":
201
159
for entry in self.data:
202
160
totaltime = int(entry.totaltime * 1000)
203
161
max_cost = max(max_cost, totaltime)
204
self.out_file.write('summary: %d\n' % (max_cost,))
162
print >> self.out_file, 'summary: %d' % (max_cost,)
206
164
def _entry(self, entry):
207
165
out_file = self.out_file
208
166
code = entry.code
209
167
inlinetime = int(entry.inlinetime * 1000)
210
#out_file.write('ob=%s\n' % (code.co_filename,))
211
if isinstance(code, str):
212
out_file.write('fi=~\n')
214
out_file.write('fi=%s\n' % (code.co_filename,))
215
out_file.write('fn=%s\n' % (label(code, True),))
216
if isinstance(code, str):
217
out_file.write('0 %s\n' % (inlinetime,))
219
out_file.write('%d %d\n' % (code.co_firstlineno, inlinetime))
168
#print >> out_file, 'ob=%s' % (code.co_filename,)
169
if isinstance(code, str):
170
print >> out_file, 'fi=~'
172
print >> out_file, 'fi=%s' % (code.co_filename,)
173
print >> out_file, 'fn=%s' % (label(code, True),)
174
if isinstance(code, str):
175
print >> out_file, '0 ', inlinetime
177
print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
220
178
# recursive calls are counted in entry.calls
222
180
calls = entry.calls
228
186
lineno = code.co_firstlineno
229
187
for subentry in calls:
230
188
self._subentry(lineno, subentry)
233
191
def _subentry(self, lineno, subentry):
234
192
out_file = self.out_file
235
193
code = subentry.code
236
194
totaltime = int(subentry.totaltime * 1000)
237
#out_file.write('cob=%s\n' % (code.co_filename,))
238
out_file.write('cfn=%s\n' % (label(code, True),))
195
#print >> out_file, 'cob=%s' % (code.co_filename,)
196
print >> out_file, 'cfn=%s' % (label(code, True),)
239
197
if isinstance(code, str):
240
out_file.write('cfi=~\n')
241
out_file.write('calls=%d 0\n' % (subentry.callcount,))
198
print >> out_file, 'cfi=~'
199
print >> out_file, 'calls=%d 0' % (subentry.callcount,)
243
out_file.write('cfi=%s\n' % (code.co_filename,))
244
out_file.write('calls=%d %d\n' % (
245
subentry.callcount, code.co_firstlineno))
246
out_file.write('%d %d\n' % (lineno, totaltime))
201
print >> out_file, 'cfi=%s' % (code.co_filename,)
202
print >> out_file, 'calls=%d %d' % (
203
subentry.callcount, code.co_firstlineno)
204
print >> out_file, '%d %d' % (lineno, totaltime)