~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

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
6
8
import sys
7
9
import thread
8
10
import threading
9
11
from _lsprof import Profiler, profiler_entry
10
12
 
 
13
 
11
14
__all__ = ['profile', 'Stats']
12
15
 
13
16
_g_threadmap = {}
24
27
 
25
28
 
26
29
def profile(f, *args, **kwds):
27
 
    """XXX docstring"""
 
30
    """Run a function profile.
 
31
 
 
32
    Exceptions are not caught: If you need stats even when exceptions are to be
 
33
    raised, passing in a closure that will catch the exceptions and transform
 
34
    them appropriately for your driver function.
 
35
 
 
36
    :return: The functions return value and a stats object.
 
37
    """
28
38
    global _g_threadmap
29
39
    p = Profiler()
30
40
    p.enable(subcalls=True)
36
46
        for pp in _g_threadmap.values():
37
47
            pp.disable()
38
48
        threading.setprofile(None)
39
 
    
 
49
 
40
50
    threads = {}
41
51
    for tid, pp in _g_threadmap.items():
42
52
        threads[tid] = Stats(pp.getstats(), {})
104
114
        """Output profiling data in calltree format (for KCacheGrind)."""
105
115
        _CallTreeFilter(self.data).output(file)
106
116
 
 
117
    def save(self, filename, format=None):
 
118
        """Save profiling data to a file.
 
119
 
 
120
        :param filename: the name of the output file
 
121
        :param format: 'txt' for a text representation;
 
122
            'callgrind' for calltree format;
 
123
            otherwise a pickled Python object. A format of None indicates
 
124
            that the format to use is to be found from the filename. If
 
125
            the name starts with callgrind.out, callgrind format is used
 
126
            otherwise the format is given by the filename extension.
 
127
        """
 
128
        if format is None:
 
129
            basename = os.path.basename(filename)
 
130
            if basename.startswith('callgrind.out'):
 
131
                format = "callgrind"
 
132
            else:
 
133
                ext = os.path.splitext(filename)[1]
 
134
                if len(ext) > 1:
 
135
                    format = ext[1:]
 
136
        outfile = open(filename, 'wb')
 
137
        try:
 
138
            if format == "callgrind":
 
139
                self.calltree(outfile)
 
140
            elif format == "txt":
 
141
                self.pprint(file=outfile)
 
142
            else:
 
143
                self.freeze()
 
144
                cPickle.dump(self, outfile, 2)
 
145
        finally:
 
146
            outfile.close()
 
147
 
107
148
 
108
149
class _CallTreeFilter(object):
 
150
    """Converter of a Stats object to input suitable for KCacheGrind.
 
151
 
 
152
    This code is taken from http://ddaa.net/blog/python/lsprof-calltree
 
153
    with the changes made by J.P. Calderone and Itamar applied. Note that
 
154
    isinstance(code, str) needs to be used at times to determine if the code
 
155
    object is actually an external code object (with a filename, etc.) or
 
156
    a Python built-in.
 
157
    """
109
158
 
110
159
    def __init__(self, data):
111
160
        self.data = data
112
161
        self.out_file = None
113
162
 
114
163
    def output(self, out_file):
115
 
        self.out_file = out_file        
116
 
        print >> out_file, 'events: Ticks'
 
164
        self.out_file = out_file
 
165
        out_file.write('events: Ticks\n')
117
166
        self._print_summary()
118
167
        for entry in self.data:
119
168
            self._entry(entry)
123
172
        for entry in self.data:
124
173
            totaltime = int(entry.totaltime * 1000)
125
174
            max_cost = max(max_cost, totaltime)
126
 
        print >> self.out_file, 'summary: %d' % (max_cost,)
 
175
        self.out_file.write('summary: %d\n' % (max_cost,))
127
176
 
128
177
    def _entry(self, entry):
129
178
        out_file = self.out_file
130
179
        code = entry.code
131
180
        inlinetime = int(entry.inlinetime * 1000)
132
 
        #print >> out_file, 'ob=%s' % (code.co_filename,)
133
 
        print >> out_file, 'fi=%s' % (code.co_filename,)
134
 
        print >> out_file, 'fn=%s' % (label(code, True),)
135
 
        print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
 
181
        #out_file.write('ob=%s\n' % (code.co_filename,))
 
182
        if isinstance(code, str):
 
183
            out_file.write('fi=~\n')
 
184
        else:
 
185
            out_file.write('fi=%s\n' % (code.co_filename,))
 
186
        out_file.write('fn=%s\n' % (label(code, True),))
 
187
        if isinstance(code, str):
 
188
            out_file.write('0  %s\n' % (inlinetime,))
 
189
        else:
 
190
            out_file.write('%d %d\n' % (code.co_firstlineno, inlinetime))
136
191
        # recursive calls are counted in entry.calls
137
192
        if entry.calls:
138
193
            calls = entry.calls
139
194
        else:
140
195
            calls = []
 
196
        if isinstance(code, str):
 
197
            lineno = 0
 
198
        else:
 
199
            lineno = code.co_firstlineno
141
200
        for subentry in calls:
142
 
            self._subentry(code.co_firstlineno, subentry)
143
 
        print >> out_file
 
201
            self._subentry(lineno, subentry)
 
202
        out_file.write('\n')
144
203
 
145
204
    def _subentry(self, lineno, subentry):
146
205
        out_file = self.out_file
147
206
        code = subentry.code
148
207
        totaltime = int(subentry.totaltime * 1000)
149
 
        #print >> out_file, 'cob=%s' % (code.co_filename,)
150
 
        print >> out_file, 'cfn=%s' % (label(code, True),)
151
 
        print >> out_file, 'cfi=%s' % (code.co_filename,)
152
 
        print >> out_file, 'calls=%d %d' % (
153
 
            subentry.callcount, code.co_firstlineno)
154
 
        print >> out_file, '%d %d' % (lineno, totaltime)
155
 
 
 
208
        #out_file.write('cob=%s\n' % (code.co_filename,))
 
209
        out_file.write('cfn=%s\n' % (label(code, True),))
 
210
        if isinstance(code, str):
 
211
            out_file.write('cfi=~\n')
 
212
            out_file.write('calls=%d 0\n' % (subentry.callcount,))
 
213
        else:
 
214
            out_file.write('cfi=%s\n' % (code.co_filename,))
 
215
            out_file.write('calls=%d %d\n' % (
 
216
                subentry.callcount, code.co_firstlineno))
 
217
        out_file.write('%d %d\n' % (lineno, totaltime))
156
218
 
157
219
_fn2mod = {}
158
220
 
184
246
    import os
185
247
    sys.argv = sys.argv[1:]
186
248
    if not sys.argv:
187
 
        print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
 
249
        sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
188
250
        sys.exit(2)
189
251
    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
190
252
    stats = profile(execfile, sys.argv[0], globals(), locals())