11
11
from _lsprof import Profiler, profiler_entry
13
from bzrlib import errors
15
17
__all__ = ['profile', 'Stats']
22
def _thread_profile(f, *args, **kwds):
23
# we lose the first profile point for a new thread in order to trampoline
24
# a new Profile object into place
26
thr = thread.get_ident()
27
_g_threadmap[thr] = p = Profiler()
28
# this overrides our sys.setprofile hook:
29
p.enable(subcalls=True, builtins=True)
17
32
def profile(f, *args, **kwds):
18
"""Run a function profile.
20
Exceptions are not caught: If you need stats even when exceptions are to be
21
raised, pass in a closure that will catch the exceptions and transform them
22
appropriately for your driver function.
24
Important caveat: only one profile can execute at a time. See BzrProfiler
27
:return: The functions return value and a stats object.
29
profiler = BzrProfiler()
36
p.enable(subcalls=True)
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.
32
ret = f(*args, **kwds)
44
ret = f(*args, **kwds)
45
except (KeyboardInterrupt, Exception), e:
47
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
34
stats = profiler.stop()
38
class BzrProfiler(object):
39
"""Bzr utility wrapper around Profiler.
51
for pp in _g_threadmap.values():
53
threading.setprofile(None)
41
For most uses the module level 'profile()' function will be suitable.
42
However profiling when a simple wrapped function isn't available may
43
be easier to accomplish using this class.
45
To use it, create a BzrProfiler and call start() on it. Some arbitrary
46
time later call stop() to stop profiling and retrieve the statistics
47
from the code executed in the interim.
49
Note that profiling involves a threading.Lock around the actual profiling.
50
This is needed because profiling involves global manipulation of the python
51
interpreter state. As such you cannot perform multiple profiles at once.
52
Trying to do so will lock out the second profiler unless the global
53
bzrlib.lsprof.BzrProfiler.profiler_block is set to 0. Setting it to 0 will
54
cause profiling to fail rather than blocking.
58
"""Serialise rather than failing to profile concurrent profile requests."""
60
profiler_lock = threading.Lock()
61
"""Global lock used to serialise profiles."""
66
This hooks into threading and will record all calls made until
69
self._g_threadmap = {}
71
permitted = self.__class__.profiler_lock.acquire(
72
self.__class__.profiler_block)
74
raise errors.InternalBzrError(msg="Already profiling something")
76
self.p.enable(subcalls=True)
77
threading.setprofile(self._thread_profile)
79
self.__class__.profiler_lock.release()
85
This unhooks from threading and cleans up the profiler, returning
86
the gathered Stats object.
88
:return: A bzrlib.lsprof.Stats object.
92
for pp in self._g_threadmap.values():
94
threading.setprofile(None)
98
for tid, pp in self._g_threadmap.items():
99
threads[tid] = Stats(pp.getstats(), {})
100
self._g_threadmap = None
101
return Stats(p.getstats(), threads)
103
self.__class__.profiler_lock.release()
105
def _thread_profile(self, f, *args, **kwds):
106
# we lose the first profile point for a new thread in order to
107
# trampoline a new Profile object into place
108
thr = thread.get_ident()
109
self._g_threadmap[thr] = p = Profiler()
110
# this overrides our sys.setprofile hook:
111
p.enable(subcalls=True, builtins=True)
56
for tid, pp in _g_threadmap.items():
57
threads[tid] = Stats(pp.getstats(), {})
59
return ret, Stats(p.getstats(), threads)
114
62
class Stats(object):
115
"""Wrapper around the collected data.
117
A Stats instance is created when the profiler finishes. Normal
118
usage is to use save() to write out the data to a file, or pprint()
119
to write human-readable information to the command line.
122
65
def __init__(self, data, threads):
124
67
self.threads = threads
126
69
def sort(self, crit="inlinetime"):
127
"""Sort the data by the supplied critera.
129
:param crit: the data attribute used as the sort key."""
130
71
if crit not in profiler_entry.__dict__:
131
72
raise ValueError, "Can't sort by %s" % crit
132
73
self.data.sort(lambda b, a: cmp(getattr(a, crit),
275
211
code = subentry.code
276
212
totaltime = int(subentry.totaltime * 1000)
277
213
#out_file.write('cob=%s\n' % (code.co_filename,))
214
out_file.write('cfn=%s\n' % (label(code, True),))
278
215
if isinstance(code, str):
279
216
out_file.write('cfi=~\n')
280
out_file.write('cfn=%s\n' % (label(code, True),))
281
217
out_file.write('calls=%d 0\n' % (subentry.callcount,))
283
219
out_file.write('cfi=%s\n' % (code.co_filename,))
284
out_file.write('cfn=%s\n' % (label(code, True),))
285
220
out_file.write('calls=%d %d\n' % (
286
221
subentry.callcount, code.co_firstlineno))
287
222
out_file.write('%d %d\n' % (lineno, totaltime))