55
41
To use it, create a BzrProfiler and call start() on it. Some arbitrary
56
42
time later call stop() to stop profiling and retrieve the statistics
57
43
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."""
74
47
"""Start profiling.
95
60
This unhooks from threading and cleans up the profiler, returning
96
61
the gathered Stats object.
98
:return: A breezy.lsprof.Stats object.
63
:return: A bzrlib.lsprof.Stats object.
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()
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)
115
77
def _thread_profile(self, f, *args, **kwds):
116
78
# we lose the first profile point for a new thread in order to
117
79
# trampoline a new Profile object into place
118
thr = _thread.get_ident()
80
thr = thread.get_ident()
119
81
self._g_threadmap[thr] = p = Profiler()
120
82
# this overrides our sys.setprofile hook:
121
83
p.enable(subcalls=True, builtins=True)
124
86
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.
132
89
def __init__(self, data, threads):
134
91
self.threads = threads
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)
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),
146
99
for e in self.data:
148
e.calls.sort(key=key_func, reverse=reverse)
101
e.calls.sort(lambda b, a: cmp(getattr(a, crit),
150
104
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."""
158
107
file = sys.stdout
213
162
ext = os.path.splitext(filename)[1]
216
with open(filename, 'wb') as outfile:
165
outfile = open(filename, 'wb')
217
167
if format == "callgrind":
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))
168
self.calltree(outfile)
222
169
elif format == "txt":
223
self.pprint(file=codecs.getwriter('utf-8')(outfile))
170
self.pprint(file=outfile)
226
pickle.dump(self, outfile, 2)
173
cPickle.dump(self, outfile, 2)
229
178
class _CallTreeFilter(object):
286
235
code = subentry.code
287
236
totaltime = int(subentry.totaltime * 1000)
288
237
#out_file.write('cob=%s\n' % (code.co_filename,))
238
out_file.write('cfn=%s\n' % (label(code, True),))
289
239
if isinstance(code, str):
290
240
out_file.write('cfi=~\n')
291
out_file.write('cfn=%s\n' % (label(code, True),))
292
241
out_file.write('calls=%d 0\n' % (subentry.callcount,))
294
243
out_file.write('cfi=%s\n' % (code.co_filename,))
295
out_file.write('cfn=%s\n' % (label(code, True),))
296
244
out_file.write('calls=%d %d\n' % (
297
245
subentry.callcount, code.co_firstlineno))
298
246
out_file.write('%d %d\n' % (lineno, totaltime))
323
271
return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
274
if __name__ == '__main__':
327
276
sys.argv = sys.argv[1:]
329
278
sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
332
result, stats = profile(runpy.run_path, sys.argv[0], run_name='__main__')
280
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
281
stats = profile(execfile, sys.argv[0], globals(), locals())
337
if __name__ == '__main__':