~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-03-25 00:02:51 UTC
  • mfrom: (5106.1.1 version-bump)
  • Revision ID: pqm@pqm.ubuntu.com-20100325000251-bwsv5c5d3l9x3lnn
(Jelmer) Bump API version for 2.2.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
# I made one modification to profile so that it returns a pair
4
4
# instead of just the Stats object
5
5
 
6
 
from __future__ import absolute_import
7
 
 
8
6
import cPickle
9
7
import os
10
8
import sys
12
10
import threading
13
11
from _lsprof import Profiler, profiler_entry
14
12
 
15
 
from bzrlib import errors
16
13
 
17
14
__all__ = ['profile', 'Stats']
18
15
 
23
20
    raised, pass in a closure that will catch the exceptions and transform them
24
21
    appropriately for your driver function.
25
22
 
26
 
    Important caveat: only one profile can execute at a time. See BzrProfiler
27
 
    for details.
28
 
 
29
23
    :return: The functions return value and a stats object.
30
24
    """
31
25
    profiler = BzrProfiler()
47
41
    To use it, create a BzrProfiler and call start() on it. Some arbitrary
48
42
    time later call stop() to stop profiling and retrieve the statistics
49
43
    from the code executed in the interim.
50
 
 
51
 
    Note that profiling involves a threading.Lock around the actual profiling.
52
 
    This is needed because profiling involves global manipulation of the python
53
 
    interpreter state. As such you cannot perform multiple profiles at once.
54
 
    Trying to do so will lock out the second profiler unless the global 
55
 
    bzrlib.lsprof.BzrProfiler.profiler_block is set to 0. Setting it to 0 will
56
 
    cause profiling to fail rather than blocking.
57
44
    """
58
45
 
59
 
    profiler_block = 1
60
 
    """Serialise rather than failing to profile concurrent profile requests."""
61
 
 
62
 
    profiler_lock = threading.Lock()
63
 
    """Global lock used to serialise profiles."""
64
 
 
65
46
    def start(self):
66
47
        """Start profiling.
67
48
        
70
51
        """
71
52
        self._g_threadmap = {}
72
53
        self.p = Profiler()
73
 
        permitted = self.__class__.profiler_lock.acquire(
74
 
            self.__class__.profiler_block)
75
 
        if not permitted:
76
 
            raise errors.InternalBzrError(msg="Already profiling something")
77
 
        try:
78
 
            self.p.enable(subcalls=True)
79
 
            threading.setprofile(self._thread_profile)
80
 
        except:
81
 
            self.__class__.profiler_lock.release()
82
 
            raise
 
54
        self.p.enable(subcalls=True)
 
55
        threading.setprofile(self._thread_profile)
83
56
 
84
57
    def stop(self):
85
58
        """Stop profiling.
89
62
 
90
63
        :return: A bzrlib.lsprof.Stats object.
91
64
        """
92
 
        try:
93
 
            self.p.disable()
94
 
            for pp in self._g_threadmap.values():
95
 
                pp.disable()
96
 
            threading.setprofile(None)
97
 
            p = self.p
98
 
            self.p = None
99
 
            threads = {}
100
 
            for tid, pp in self._g_threadmap.items():
101
 
                threads[tid] = Stats(pp.getstats(), {})
102
 
            self._g_threadmap = None
103
 
            return Stats(p.getstats(), threads)
104
 
        finally:
105
 
            self.__class__.profiler_lock.release()
 
65
        self.p.disable()
 
66
        for pp in self._g_threadmap.values():
 
67
            pp.disable()
 
68
        threading.setprofile(None)
 
69
        p = self.p
 
70
        self.p = None
 
71
        threads = {}
 
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)
106
76
 
107
77
    def _thread_profile(self, f, *args, **kwds):
108
78
        # we lose the first profile point for a new thread in order to
114
84
 
115
85
 
116
86
class Stats(object):
117
 
    """Wrapper around the collected data.
118
 
 
119
 
    A Stats instance is created when the profiler finishes. Normal
120
 
    usage is to use save() to write out the data to a file, or pprint()
121
 
    to write human-readable information to the command line.
122
 
    """
 
87
    """XXX docstring"""
123
88
 
124
89
    def __init__(self, data, threads):
125
90
        self.data = data
126
91
        self.threads = threads
127
92
 
128
93
    def sort(self, crit="inlinetime"):
129
 
        """Sort the data by the supplied critera.
130
 
 
131
 
        :param crit: the data attribute used as the sort key."""
 
94
        """XXX docstring"""
132
95
        if crit not in profiler_entry.__dict__:
133
96
            raise ValueError, "Can't sort by %s" % crit
134
97
        self.data.sort(lambda b, a: cmp(getattr(a, crit),
139
102
                                              getattr(b, crit)))
140
103
 
141
104
    def pprint(self, top=None, file=None):
142
 
        """Pretty-print the data as plain text for human consumption.
143
 
 
144
 
        :param top: only output the top n entries.
145
 
            The default value of None means output all data.
146
 
        :param file: the output file; if None, output will
147
 
            default to stdout."""
 
105
        """XXX docstring"""
148
106
        if file is None:
149
107
            file = sys.stdout
150
108
        d = self.data
277
235
        code = subentry.code
278
236
        totaltime = int(subentry.totaltime * 1000)
279
237
        #out_file.write('cob=%s\n' % (code.co_filename,))
 
238
        out_file.write('cfn=%s\n' % (label(code, True),))
280
239
        if isinstance(code, str):
281
240
            out_file.write('cfi=~\n')
282
 
            out_file.write('cfn=%s\n' % (label(code, True),))
283
241
            out_file.write('calls=%d 0\n' % (subentry.callcount,))
284
242
        else:
285
243
            out_file.write('cfi=%s\n' % (code.co_filename,))
286
 
            out_file.write('cfn=%s\n' % (label(code, True),))
287
244
            out_file.write('calls=%d %d\n' % (
288
245
                subentry.callcount, code.co_firstlineno))
289
246
        out_file.write('%d %d\n' % (lineno, totaltime))