41
55
To use it, create a BzrProfiler and call start() on it. Some arbitrary
42
56
time later call stop() to stop profiling and retrieve the statistics
43
57
from the code executed in the interim.
59
Note that profiling involves a threading.Lock around the actual profiling.
60
This is needed because profiling involves global manipulation of the python
61
interpreter state. As such you cannot perform multiple profiles at once.
62
Trying to do so will lock out the second profiler unless the global
63
breezy.lsprof.BzrProfiler.profiler_block is set to 0. Setting it to 0 will
64
cause profiling to fail rather than blocking.
68
"""Serialise rather than failing to profile concurrent profile requests."""
70
profiler_lock = threading.Lock()
71
"""Global lock used to serialise profiles."""
47
74
"""Start profiling.
60
95
This unhooks from threading and cleans up the profiler, returning
61
96
the gathered Stats object.
63
:return: A bzrlib.lsprof.Stats object.
98
:return: A breezy.lsprof.Stats object.
66
for pp in self._g_threadmap.values():
68
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)
102
for pp in self._g_threadmap.values():
104
threading.setprofile(None)
108
for tid, pp in self._g_threadmap.items():
109
threads[tid] = Stats(pp.getstats(), {})
110
self._g_threadmap = None
111
return Stats(p.getstats(), threads)
113
self.__class__.profiler_lock.release()
77
115
def _thread_profile(self, f, *args, **kwds):
78
116
# we lose the first profile point for a new thread in order to
79
117
# trampoline a new Profile object into place
80
thr = thread.get_ident()
118
thr = _thread.get_ident()
81
119
self._g_threadmap[thr] = p = Profiler()
82
120
# this overrides our sys.setprofile hook:
83
121
p.enable(subcalls=True, builtins=True)
86
124
class Stats(object):
125
"""Wrapper around the collected data.
127
A Stats instance is created when the profiler finishes. Normal
128
usage is to use save() to write out the data to a file, or pprint()
129
to write human-readable information to the command line.
89
132
def __init__(self, data, threads):
91
134
self.threads = threads
93
def sort(self, crit="inlinetime"):
95
if crit not in profiler_entry.__dict__:
96
raise ValueError, "Can't sort by %s" % crit
97
self.data.sort(lambda b, a: cmp(getattr(a, crit),
136
def sort(self, crit="inlinetime", reverse=True):
137
"""Sort the data by the supplied critera.
139
:param crit: the data attribute used as the sort key."""
140
if crit not in profiler_entry.__dict__ or crit == 'code':
141
raise ValueError("Can't sort by %s" % crit)
143
key_func = operator.attrgetter(crit)
144
self.data.sort(key=key_func, reverse=reverse)
99
146
for e in self.data:
101
e.calls.sort(lambda b, a: cmp(getattr(a, crit),
148
e.calls.sort(key=key_func, reverse=reverse)
104
150
def pprint(self, top=None, file=None):
151
"""Pretty-print the data as plain text for human consumption.
153
:param top: only output the top n entries.
154
The default value of None means output all data.
155
:param file: the output file; if None, output will
156
default to stdout."""
107
158
file = sys.stdout
162
213
ext = os.path.splitext(filename)[1]
165
outfile = open(filename, 'wb')
216
with open(filename, 'wb') as outfile:
167
217
if format == "callgrind":
168
self.calltree(outfile)
218
# The callgrind format states it is 'ASCII based':
219
# <http://valgrind.org/docs/manual/cl-format.html>
220
# But includes filenames so lets ignore and use UTF-8.
221
self.calltree(codecs.getwriter('utf-8')(outfile))
169
222
elif format == "txt":
170
self.pprint(file=outfile)
223
self.pprint(file=codecs.getwriter('utf-8')(outfile))
173
cPickle.dump(self, outfile, 2)
226
pickle.dump(self, outfile, 2)
178
229
class _CallTreeFilter(object):
235
286
code = subentry.code
236
287
totaltime = int(subentry.totaltime * 1000)
237
288
#out_file.write('cob=%s\n' % (code.co_filename,))
238
out_file.write('cfn=%s\n' % (label(code, True),))
239
289
if isinstance(code, str):
240
290
out_file.write('cfi=~\n')
291
out_file.write('cfn=%s\n' % (label(code, True),))
241
292
out_file.write('calls=%d 0\n' % (subentry.callcount,))
243
294
out_file.write('cfi=%s\n' % (code.co_filename,))
295
out_file.write('cfn=%s\n' % (label(code, True),))
244
296
out_file.write('calls=%d %d\n' % (
245
297
subentry.callcount, code.co_firstlineno))
246
298
out_file.write('%d %d\n' % (lineno, totaltime))
271
323
return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
274
if __name__ == '__main__':
276
327
sys.argv = sys.argv[1:]
278
329
sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
280
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
281
stats = profile(execfile, sys.argv[0], globals(), locals())
332
result, stats = profile(runpy.run_path, sys.argv[0], run_name='__main__')
337
if __name__ == '__main__':