~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: 2006-05-05 08:29:29 UTC
  • mfrom: (1697.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060505082929-a037ee137f1ff240
Merge break-lock command.

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
 
import cPickle
7
 
import os
8
6
import sys
9
 
import thread
10
 
import threading
11
 
from _lsprof import Profiler, profiler_entry
 
7
from _lsprof import Profiler, profiler_entry, profiler_subentry
12
8
 
13
9
__all__ = ['profile', 'Stats']
14
10
 
15
 
_g_threadmap = {}
16
 
 
17
 
 
18
 
def _thread_profile(f, *args, **kwds):
19
 
    # we lose the first profile point for a new thread in order to trampoline
20
 
    # a new Profile object into place
21
 
    global _g_threadmap
22
 
    thr = thread.get_ident()
23
 
    _g_threadmap[thr] = p = Profiler()
24
 
    # this overrides our sys.setprofile hook:
25
 
    p.enable(subcalls=True, builtins=True)
26
 
 
27
 
 
28
11
def profile(f, *args, **kwds):
29
12
    """XXX docstring"""
30
 
    global _g_threadmap
31
13
    p = Profiler()
32
14
    p.enable(subcalls=True)
33
 
    threading.setprofile(_thread_profile)
34
15
    try:
35
16
        ret = f(*args, **kwds)
36
17
    finally:
37
18
        p.disable()
38
 
        for pp in _g_threadmap.values():
39
 
            pp.disable()
40
 
        threading.setprofile(None)
41
 
    
42
 
    threads = {}
43
 
    for tid, pp in _g_threadmap.items():
44
 
        threads[tid] = Stats(pp.getstats(), {})
45
 
    _g_threadmap = {}
46
 
    return ret, Stats(p.getstats(), threads)
 
19
    return ret,Stats(p.getstats())
47
20
 
48
21
 
49
22
class Stats(object):
50
23
    """XXX docstring"""
51
24
 
52
 
    def __init__(self, data, threads):
 
25
    def __init__(self, data):
53
26
        self.data = data
54
 
        self.threads = threads
55
27
 
56
28
    def sort(self, crit="inlinetime"):
57
29
        """XXX docstring"""
94
66
            e = self.data[i]
95
67
            if not isinstance(e.code, str):
96
68
                self.data[i] = type(e)((label(e.code),) + e[1:])
97
 
            if e.calls:
98
 
                for j in range(len(e.calls)):
99
 
                    se = e.calls[j]
100
 
                    if not isinstance(se.code, str):
101
 
                        e.calls[j] = type(se)((label(se.code),) + se[1:])
102
 
        for s in self.threads.values():
103
 
            s.freeze()
104
 
 
105
 
    def calltree(self, file):
106
 
        """Output profiling data in calltree format (for KCacheGrind)."""
107
 
        _CallTreeFilter(self.data).output(file)
108
 
 
109
 
    def save(self, filename, format=None):
110
 
        """Save profiling data to a file.
111
 
 
112
 
        :param filename: the name of the output file
113
 
        :param format: 'txt' for a text representation;
114
 
            'callgrind' for calltree format;
115
 
            otherwise a pickled Python object. A format of None indicates
116
 
            that the format to use is to be found from the extension of
117
 
            filename.
118
 
        """
119
 
        if format is None:
120
 
            ext = os.path.splitext(filename)[1]
121
 
            if len(ext) > 1:
122
 
                format = ext[1:]
123
 
        outfile = open(filename, 'wb')
124
 
        try:
125
 
            if format == "callgrind":
126
 
                self.calltree(outfile)
127
 
            elif format == "txt":
128
 
                self.pprint(file=outfile)
129
 
            else:
130
 
                self.freeze()
131
 
                cPickle.dump(self, outfile, 2)
132
 
        finally:
133
 
            outfile.close()
134
 
 
135
 
 
136
 
class _CallTreeFilter(object):
137
 
    """Converter of a Stats object to input suitable for KCacheGrind.
138
 
 
139
 
    This code is taken from http://ddaa.net/blog/python/lsprof-calltree
140
 
    with the changes made by J.P. Calderone and Itamar applied. Note that
141
 
    isinstance(code, str) needs to be used at times to determine if the code 
142
 
    object is actually an external code object (with a filename, etc.) or
143
 
    a Python built-in.
144
 
    """
145
 
 
146
 
    def __init__(self, data):
147
 
        self.data = data
148
 
        self.out_file = None
149
 
 
150
 
    def output(self, out_file):
151
 
        self.out_file = out_file        
152
 
        print >> out_file, 'events: Ticks'
153
 
        self._print_summary()
154
 
        for entry in self.data:
155
 
            self._entry(entry)
156
 
 
157
 
    def _print_summary(self):
158
 
        max_cost = 0
159
 
        for entry in self.data:
160
 
            totaltime = int(entry.totaltime * 1000)
161
 
            max_cost = max(max_cost, totaltime)
162
 
        print >> self.out_file, 'summary: %d' % (max_cost,)
163
 
 
164
 
    def _entry(self, entry):
165
 
        out_file = self.out_file
166
 
        code = entry.code
167
 
        inlinetime = int(entry.inlinetime * 1000)
168
 
        #print >> out_file, 'ob=%s' % (code.co_filename,)
169
 
        if isinstance(code, str):
170
 
            print >> out_file, 'fi=~'
171
 
        else:
172
 
            print >> out_file, 'fi=%s' % (code.co_filename,)
173
 
        print >> out_file, 'fn=%s' % (label(code, True),)
174
 
        if isinstance(code, str):
175
 
            print >> out_file, '0 ', inlinetime
176
 
        else:
177
 
            print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
178
 
        # recursive calls are counted in entry.calls
179
 
        if entry.calls:
180
 
            calls = entry.calls
181
 
        else:
182
 
            calls = []
183
 
        if isinstance(code, str):
184
 
            lineno = 0
185
 
        else:
186
 
            lineno = code.co_firstlineno
187
 
        for subentry in calls:
188
 
            self._subentry(lineno, subentry)
189
 
        print >> out_file
190
 
 
191
 
    def _subentry(self, lineno, subentry):
192
 
        out_file = self.out_file
193
 
        code = subentry.code
194
 
        totaltime = int(subentry.totaltime * 1000)
195
 
        #print >> out_file, 'cob=%s' % (code.co_filename,)
196
 
        print >> out_file, 'cfn=%s' % (label(code, True),)
197
 
        if isinstance(code, str):
198
 
            print >> out_file, 'cfi=~'
199
 
            print >> out_file, 'calls=%d 0' % (subentry.callcount,)
200
 
        else:
201
 
            print >> out_file, 'cfi=%s' % (code.co_filename,)
202
 
            print >> out_file, 'calls=%d %d' % (
203
 
                subentry.callcount, code.co_firstlineno)
204
 
        print >> out_file, '%d %d' % (lineno, totaltime)
 
69
                if e.calls:
 
70
                    for j in range(len(e.calls)):
 
71
                        se = e.calls[j]
 
72
                        if not isinstance(se.code, str):
 
73
                            e.calls[j] = type(se)((label(se.code),) + se[1:])
205
74
 
206
75
_fn2mod = {}
207
76
 
208
 
def label(code, calltree=False):
 
77
def label(code):
209
78
    if isinstance(code, str):
210
79
        return code
211
80
    try:
212
81
        mname = _fn2mod[code.co_filename]
213
82
    except KeyError:
214
 
        for k, v in sys.modules.items():
 
83
        for k, v in sys.modules.iteritems():
215
84
            if v is None:
216
85
                continue
217
 
            if getattr(v, '__file__', None) is None:
 
86
            if not hasattr(v, '__file__'):
218
87
                continue
219
88
            if not isinstance(v.__file__, str):
220
89
                continue
223
92
                break
224
93
        else:
225
94
            mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
226
 
    if calltree:
227
 
        return '%s %s:%d' % (code.co_name, mname, code.co_firstlineno)
228
 
    else:
229
 
        return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
 
95
    
 
96
    return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
230
97
 
231
98
 
232
99
if __name__ == '__main__':