~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: Jelmer Vernooij
  • Date: 2009-01-28 18:42:55 UTC
  • mto: This revision was merged to the branch mainline in revision 3968.
  • Revision ID: jelmer@samba.org-20090128184255-bdmklkvm83ltk191
Update NEWS

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
    :return: The functions return value and a stats object.
 
33
    """
28
34
    global _g_threadmap
29
35
    p = Profiler()
30
36
    p.enable(subcalls=True)
31
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
42
    try:
33
 
        ret = f(*args, **kwds)
 
43
        try:
 
44
            ret = f(*args, **kwds)
 
45
        except (KeyboardInterrupt, Exception), e:
 
46
            import bzrlib.trace
 
47
            bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
48
            ret = 3
34
49
    finally:
35
50
        p.disable()
36
51
        for pp in _g_threadmap.values():
104
119
        """Output profiling data in calltree format (for KCacheGrind)."""
105
120
        _CallTreeFilter(self.data).output(file)
106
121
 
 
122
    def save(self, filename, format=None):
 
123
        """Save profiling data to a file.
 
124
 
 
125
        :param filename: the name of the output file
 
126
        :param format: 'txt' for a text representation;
 
127
            'callgrind' for calltree format;
 
128
            otherwise a pickled Python object. A format of None indicates
 
129
            that the format to use is to be found from the filename. If
 
130
            the name starts with callgrind.out, callgrind format is used
 
131
            otherwise the format is given by the filename extension.
 
132
        """
 
133
        if format is None:
 
134
            basename = os.path.basename(filename)
 
135
            if basename.startswith('callgrind.out'):
 
136
                format = "callgrind"
 
137
            else:
 
138
                ext = os.path.splitext(filename)[1]
 
139
                if len(ext) > 1:
 
140
                    format = ext[1:]
 
141
        outfile = open(filename, 'wb')
 
142
        try:
 
143
            if format == "callgrind":
 
144
                self.calltree(outfile)
 
145
            elif format == "txt":
 
146
                self.pprint(file=outfile)
 
147
            else:
 
148
                self.freeze()
 
149
                cPickle.dump(self, outfile, 2)
 
150
        finally:
 
151
            outfile.close()
 
152
 
107
153
 
108
154
class _CallTreeFilter(object):
 
155
    """Converter of a Stats object to input suitable for KCacheGrind.
 
156
 
 
157
    This code is taken from http://ddaa.net/blog/python/lsprof-calltree
 
158
    with the changes made by J.P. Calderone and Itamar applied. Note that
 
159
    isinstance(code, str) needs to be used at times to determine if the code 
 
160
    object is actually an external code object (with a filename, etc.) or
 
161
    a Python built-in.
 
162
    """
109
163
 
110
164
    def __init__(self, data):
111
165
        self.data = data
113
167
 
114
168
    def output(self, out_file):
115
169
        self.out_file = out_file        
116
 
        print >> out_file, 'events: Ticks'
 
170
        out_file.write('events: Ticks\n')
117
171
        self._print_summary()
118
172
        for entry in self.data:
119
173
            self._entry(entry)
123
177
        for entry in self.data:
124
178
            totaltime = int(entry.totaltime * 1000)
125
179
            max_cost = max(max_cost, totaltime)
126
 
        print >> self.out_file, 'summary: %d' % (max_cost,)
 
180
        self.out_file.write('summary: %d\n' % (max_cost,))
127
181
 
128
182
    def _entry(self, entry):
129
183
        out_file = self.out_file
130
184
        code = entry.code
131
185
        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)
 
186
        #out_file.write('ob=%s\n' % (code.co_filename,))
 
187
        if isinstance(code, str):
 
188
            out_file.write('fi=~\n')
 
189
        else:
 
190
            out_file.write('fi=%s\n' % (code.co_filename,))
 
191
        out_file.write('fn=%s\n' % (label(code, True),))
 
192
        if isinstance(code, str):
 
193
            out_file.write('0  %s\n' % (inlinetime,))
 
194
        else:
 
195
            out_file.write('%d %d\n' % (code.co_firstlineno, inlinetime))
136
196
        # recursive calls are counted in entry.calls
137
197
        if entry.calls:
138
198
            calls = entry.calls
139
199
        else:
140
200
            calls = []
 
201
        if isinstance(code, str):
 
202
            lineno = 0
 
203
        else:
 
204
            lineno = code.co_firstlineno
141
205
        for subentry in calls:
142
 
            self._subentry(code.co_firstlineno, subentry)
143
 
        print >> out_file
 
206
            self._subentry(lineno, subentry)
 
207
        out_file.write('\n')
144
208
 
145
209
    def _subentry(self, lineno, subentry):
146
210
        out_file = self.out_file
147
211
        code = subentry.code
148
212
        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
 
 
 
213
        #out_file.write('cob=%s\n' % (code.co_filename,))
 
214
        out_file.write('cfn=%s\n' % (label(code, True),))
 
215
        if isinstance(code, str):
 
216
            out_file.write('cfi=~\n')
 
217
            out_file.write('calls=%d 0\n' % (subentry.callcount,))
 
218
        else:
 
219
            out_file.write('cfi=%s\n' % (code.co_filename,))
 
220
            out_file.write('calls=%d %d\n' % (
 
221
                subentry.callcount, code.co_firstlineno))
 
222
        out_file.write('%d %d\n' % (lineno, totaltime))
156
223
 
157
224
_fn2mod = {}
158
225
 
184
251
    import os
185
252
    sys.argv = sys.argv[1:]
186
253
    if not sys.argv:
187
 
        print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
 
254
        sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
188
255
        sys.exit(2)
189
256
    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
190
257
    stats = profile(execfile, sys.argv[0], globals(), locals())