~mhammond/bzr/update-r

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: Mark Hammond
  • Date: 2008-08-19 01:09:37 UTC
  • mfrom: (2009.1.1629 +trunk)
  • Revision ID: mhammond@skippinet.com.au-20080819010937-aohklatall0s6m3z
First attempt to merge .dev and resolve the conflicts (but tests are 
failing)

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