~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: 2008-09-02 03:59:07 UTC
  • mfrom: (3653.2.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080902035907-3b81euge0gyypozk
Remove obsolete dev formats.

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
 
from _lsprof import Profiler, profiler_entry, profiler_subentry
 
9
import thread
 
10
import threading
 
11
from _lsprof import Profiler, profiler_entry
 
12
 
8
13
 
9
14
__all__ = ['profile', 'Stats']
10
15
 
 
16
_g_threadmap = {}
 
17
 
 
18
 
 
19
def _thread_profile(f, *args, **kwds):
 
20
    # we lose the first profile point for a new thread in order to trampoline
 
21
    # a new Profile object into place
 
22
    global _g_threadmap
 
23
    thr = thread.get_ident()
 
24
    _g_threadmap[thr] = p = Profiler()
 
25
    # this overrides our sys.setprofile hook:
 
26
    p.enable(subcalls=True, builtins=True)
 
27
 
 
28
 
11
29
def profile(f, *args, **kwds):
12
30
    """XXX docstring"""
 
31
    global _g_threadmap
13
32
    p = Profiler()
14
33
    p.enable(subcalls=True)
 
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.
15
39
    try:
16
 
        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
17
46
    finally:
18
47
        p.disable()
19
 
    return ret,Stats(p.getstats())
 
48
        for pp in _g_threadmap.values():
 
49
            pp.disable()
 
50
        threading.setprofile(None)
 
51
    
 
52
    threads = {}
 
53
    for tid, pp in _g_threadmap.items():
 
54
        threads[tid] = Stats(pp.getstats(), {})
 
55
    _g_threadmap = {}
 
56
    return ret, Stats(p.getstats(), threads)
20
57
 
21
58
 
22
59
class Stats(object):
23
60
    """XXX docstring"""
24
61
 
25
 
    def __init__(self, data):
 
62
    def __init__(self, data, threads):
26
63
        self.data = data
 
64
        self.threads = threads
27
65
 
28
66
    def sort(self, crit="inlinetime"):
29
67
        """XXX docstring"""
66
104
            e = self.data[i]
67
105
            if not isinstance(e.code, str):
68
106
                self.data[i] = type(e)((label(e.code),) + e[1:])
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:])
 
107
            if e.calls:
 
108
                for j in range(len(e.calls)):
 
109
                    se = e.calls[j]
 
110
                    if not isinstance(se.code, str):
 
111
                        e.calls[j] = type(se)((label(se.code),) + se[1:])
 
112
        for s in self.threads.values():
 
113
            s.freeze()
 
114
 
 
115
    def calltree(self, file):
 
116
        """Output profiling data in calltree format (for KCacheGrind)."""
 
117
        _CallTreeFilter(self.data).output(file)
 
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
 
 
150
 
 
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
    """
 
160
 
 
161
    def __init__(self, data):
 
162
        self.data = data
 
163
        self.out_file = None
 
164
 
 
165
    def output(self, out_file):
 
166
        self.out_file = out_file        
 
167
        out_file.write('events: Ticks\n')
 
168
        self._print_summary()
 
169
        for entry in self.data:
 
170
            self._entry(entry)
 
171
 
 
172
    def _print_summary(self):
 
173
        max_cost = 0
 
174
        for entry in self.data:
 
175
            totaltime = int(entry.totaltime * 1000)
 
176
            max_cost = max(max_cost, totaltime)
 
177
        self.out_file.write('summary: %d\n' % (max_cost,))
 
178
 
 
179
    def _entry(self, entry):
 
180
        out_file = self.out_file
 
181
        code = entry.code
 
182
        inlinetime = int(entry.inlinetime * 1000)
 
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))
 
193
        # recursive calls are counted in entry.calls
 
194
        if entry.calls:
 
195
            calls = entry.calls
 
196
        else:
 
197
            calls = []
 
198
        if isinstance(code, str):
 
199
            lineno = 0
 
200
        else:
 
201
            lineno = code.co_firstlineno
 
202
        for subentry in calls:
 
203
            self._subentry(lineno, subentry)
 
204
        out_file.write('\n')
 
205
 
 
206
    def _subentry(self, lineno, subentry):
 
207
        out_file = self.out_file
 
208
        code = subentry.code
 
209
        totaltime = int(subentry.totaltime * 1000)
 
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))
74
220
 
75
221
_fn2mod = {}
76
222
 
77
 
def label(code):
 
223
def label(code, calltree=False):
78
224
    if isinstance(code, str):
79
225
        return code
80
226
    try:
81
227
        mname = _fn2mod[code.co_filename]
82
228
    except KeyError:
83
 
        for k, v in sys.modules.iteritems():
 
229
        for k, v in sys.modules.items():
84
230
            if v is None:
85
231
                continue
86
 
            if not hasattr(v, '__file__'):
 
232
            if getattr(v, '__file__', None) is None:
87
233
                continue
88
234
            if not isinstance(v.__file__, str):
89
235
                continue
92
238
                break
93
239
        else:
94
240
            mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
95
 
    
96
 
    return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
 
241
    if calltree:
 
242
        return '%s %s:%d' % (code.co_name, mname, code.co_firstlineno)
 
243
    else:
 
244
        return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
97
245
 
98
246
 
99
247
if __name__ == '__main__':
100
248
    import os
101
249
    sys.argv = sys.argv[1:]
102
250
    if not sys.argv:
103
 
        print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
 
251
        sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
104
252
        sys.exit(2)
105
253
    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
106
254
    stats = profile(execfile, sys.argv[0], globals(), locals())