~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-10 17:52:08 UTC
  • mfrom: (5021 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5023.
  • Revision ID: john@arbash-meinel.com-20100210175208-bubuwav4uqigu291
Merge bzr.dev 5021 to resolve NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
import threading
11
11
from _lsprof import Profiler, profiler_entry
12
12
 
13
 
from bzrlib import errors
14
13
 
15
14
__all__ = ['profile', 'Stats']
16
15
 
21
20
    raised, pass in a closure that will catch the exceptions and transform them
22
21
    appropriately for your driver function.
23
22
 
24
 
    Important caveat: only one profile can execute at a time. See BzrProfiler
25
 
    for details.
26
 
 
27
23
    :return: The functions return value and a stats object.
28
24
    """
29
25
    profiler = BzrProfiler()
45
41
    To use it, create a BzrProfiler and call start() on it. Some arbitrary
46
42
    time later call stop() to stop profiling and retrieve the statistics
47
43
    from the code executed in the interim.
48
 
 
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.
55
44
    """
56
45
 
57
 
    profiler_block = 1
58
 
    """Serialise rather than failing to profile concurrent profile requests."""
59
 
 
60
 
    profiler_lock = threading.Lock()
61
 
    """Global lock used to serialise profiles."""
62
 
 
63
46
    def start(self):
64
47
        """Start profiling.
65
48
        
68
51
        """
69
52
        self._g_threadmap = {}
70
53
        self.p = Profiler()
71
 
        permitted = self.__class__.profiler_lock.acquire(
72
 
            self.__class__.profiler_block)
73
 
        if not permitted:
74
 
            raise errors.InternalBzrError(msg="Already profiling something")
75
 
        try:
76
 
            self.p.enable(subcalls=True)
77
 
            threading.setprofile(self._thread_profile)
78
 
        except:
79
 
            self.__class__.profiler_lock.release()
80
 
            raise
 
54
        self.p.enable(subcalls=True)
 
55
        threading.setprofile(self._thread_profile)
81
56
 
82
57
    def stop(self):
83
58
        """Stop profiling.
87
62
 
88
63
        :return: A bzrlib.lsprof.Stats object.
89
64
        """
90
 
        try:
91
 
            self.p.disable()
92
 
            for pp in self._g_threadmap.values():
93
 
                pp.disable()
94
 
            threading.setprofile(None)
95
 
            p = self.p
96
 
            self.p = None
97
 
            threads = {}
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)
102
 
        finally:
103
 
            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)
104
76
 
105
77
    def _thread_profile(self, f, *args, **kwds):
106
78
        # we lose the first profile point for a new thread in order to
112
84
 
113
85
 
114
86
class Stats(object):
115
 
    """Wrapper around the collected data.
116
 
 
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.
120
 
    """
 
87
    """XXX docstring"""
121
88
 
122
89
    def __init__(self, data, threads):
123
90
        self.data = data
124
91
        self.threads = threads
125
92
 
126
93
    def sort(self, crit="inlinetime"):
127
 
        """Sort the data by the supplied critera.
128
 
 
129
 
        :param crit: the data attribute used as the sort key."""
 
94
        """XXX docstring"""
130
95
        if crit not in profiler_entry.__dict__:
131
96
            raise ValueError, "Can't sort by %s" % crit
132
97
        self.data.sort(lambda b, a: cmp(getattr(a, crit),
137
102
                                              getattr(b, crit)))
138
103
 
139
104
    def pprint(self, top=None, file=None):
140
 
        """Pretty-print the data as plain text for human consumption.
141
 
 
142
 
        :param top: only output the top n entries.
143
 
            The default value of None means output all data.
144
 
        :param file: the output file; if None, output will
145
 
            default to stdout."""
 
105
        """XXX docstring"""
146
106
        if file is None:
147
107
            file = sys.stdout
148
108
        d = self.data
275
235
        code = subentry.code
276
236
        totaltime = int(subentry.totaltime * 1000)
277
237
        #out_file.write('cob=%s\n' % (code.co_filename,))
 
238
        out_file.write('cfn=%s\n' % (label(code, True),))
278
239
        if isinstance(code, str):
279
240
            out_file.write('cfi=~\n')
280
 
            out_file.write('cfn=%s\n' % (label(code, True),))
281
241
            out_file.write('calls=%d 0\n' % (subentry.callcount,))
282
242
        else:
283
243
            out_file.write('cfi=%s\n' % (code.co_filename,))
284
 
            out_file.write('cfn=%s\n' % (label(code, True),))
285
244
            out_file.write('calls=%d %d\n' % (
286
245
                subentry.callcount, code.co_firstlineno))
287
246
        out_file.write('%d %d\n' % (lineno, totaltime))