/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add 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
 
from __future__ import absolute_import
7
 
 
8
 
import codecs
9
 
try:
10
 
    import cPickle as pickle
11
 
except ImportError:
12
 
    import pickle
13
 
import operator
 
6
import cPickle
14
7
import os
15
8
import sys
16
 
try:
17
 
    import _thread
18
 
except ImportError:
19
 
    import thread as _thread
 
9
import thread
20
10
import threading
21
11
from _lsprof import Profiler, profiler_entry
22
12
 
23
 
from . import errors
 
13
from bzrlib import errors
24
14
 
25
15
__all__ = ['profile', 'Stats']
26
16
 
27
 
 
28
17
def profile(f, *args, **kwds):
29
18
    """Run a function profile.
30
19
 
48
37
 
49
38
class BzrProfiler(object):
50
39
    """Bzr utility wrapper around Profiler.
51
 
 
 
40
    
52
41
    For most uses the module level 'profile()' function will be suitable.
53
42
    However profiling when a simple wrapped function isn't available may
54
43
    be easier to accomplish using this class.
60
49
    Note that profiling involves a threading.Lock around the actual profiling.
61
50
    This is needed because profiling involves global manipulation of the python
62
51
    interpreter state. As such you cannot perform multiple profiles at once.
63
 
    Trying to do so will lock out the second profiler unless the global
64
 
    breezy.lsprof.BzrProfiler.profiler_block is set to 0. Setting it to 0 will
 
52
    Trying to do so will lock out the second profiler unless the global 
 
53
    bzrlib.lsprof.BzrProfiler.profiler_block is set to 0. Setting it to 0 will
65
54
    cause profiling to fail rather than blocking.
66
55
    """
67
56
 
73
62
 
74
63
    def start(self):
75
64
        """Start profiling.
76
 
 
 
65
        
77
66
        This hooks into threading and will record all calls made until
78
67
        stop() is called.
79
68
        """
86
75
        try:
87
76
            self.p.enable(subcalls=True)
88
77
            threading.setprofile(self._thread_profile)
89
 
        except BaseException:
 
78
        except:
90
79
            self.__class__.profiler_lock.release()
91
80
            raise
92
81
 
96
85
        This unhooks from threading and cleans up the profiler, returning
97
86
        the gathered Stats object.
98
87
 
99
 
        :return: A breezy.lsprof.Stats object.
 
88
        :return: A bzrlib.lsprof.Stats object.
100
89
        """
101
90
        try:
102
91
            self.p.disable()
116
105
    def _thread_profile(self, f, *args, **kwds):
117
106
        # we lose the first profile point for a new thread in order to
118
107
        # trampoline a new Profile object into place
119
 
        thr = _thread.get_ident()
 
108
        thr = thread.get_ident()
120
109
        self._g_threadmap[thr] = p = Profiler()
121
110
        # this overrides our sys.setprofile hook:
122
111
        p.enable(subcalls=True, builtins=True)
123
112
 
124
113
 
125
114
class Stats(object):
126
 
    """Wrapper around the collected data.
127
 
 
128
 
    A Stats instance is created when the profiler finishes. Normal
129
 
    usage is to use save() to write out the data to a file, or pprint()
130
 
    to write human-readable information to the command line.
131
 
    """
 
115
    """XXX docstring"""
132
116
 
133
117
    def __init__(self, data, threads):
134
118
        self.data = data
135
119
        self.threads = threads
136
120
 
137
 
    def sort(self, crit="inlinetime", reverse=True):
138
 
        """Sort the data by the supplied critera.
139
 
 
140
 
        :param crit: the data attribute used as the sort key."""
141
 
        if crit not in profiler_entry.__dict__ or crit == 'code':
142
 
            raise ValueError("Can't sort by %s" % crit)
143
 
 
144
 
        key_func = operator.attrgetter(crit)
145
 
        self.data.sort(key=key_func, reverse=reverse)
146
 
 
 
121
    def sort(self, crit="inlinetime"):
 
122
        """XXX docstring"""
 
123
        if crit not in profiler_entry.__dict__:
 
124
            raise ValueError, "Can't sort by %s" % crit
 
125
        self.data.sort(lambda b, a: cmp(getattr(a, crit),
 
126
                                        getattr(b, crit)))
147
127
        for e in self.data:
148
128
            if e.calls:
149
 
                e.calls.sort(key=key_func, reverse=reverse)
 
129
                e.calls.sort(lambda b, a: cmp(getattr(a, crit),
 
130
                                              getattr(b, crit)))
150
131
 
151
132
    def pprint(self, top=None, file=None):
152
 
        """Pretty-print the data as plain text for human consumption.
153
 
 
154
 
        :param top: only output the top n entries.
155
 
            The default value of None means output all data.
156
 
        :param file: the output file; if None, output will
157
 
            default to stdout."""
 
133
        """XXX docstring"""
158
134
        if file is None:
159
135
            file = sys.stdout
160
136
        d = self.data
162
138
            d = d[:top]
163
139
        cols = "% 12s %12s %11.4f %11.4f   %s\n"
164
140
        hcols = "% 12s %12s %12s %12s %s\n"
 
141
        cols2 = "+%12s %12s %11.4f %11.4f +  %s\n"
165
142
        file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
166
143
                            "Inline(ms)", "module:lineno(function)"))
167
144
        for e in d:
213
190
                ext = os.path.splitext(filename)[1]
214
191
                if len(ext) > 1:
215
192
                    format = ext[1:]
216
 
        with open(filename, 'wb') as outfile:
 
193
        outfile = open(filename, 'wb')
 
194
        try:
217
195
            if format == "callgrind":
218
 
                # The callgrind format states it is 'ASCII based':
219
 
                # <http://valgrind.org/docs/manual/cl-format.html>
220
 
                # But includes filenames so lets ignore and use UTF-8.
221
 
                self.calltree(codecs.getwriter('utf-8')(outfile))
 
196
                self.calltree(outfile)
222
197
            elif format == "txt":
223
 
                self.pprint(file=codecs.getwriter('utf-8')(outfile))
 
198
                self.pprint(file=outfile)
224
199
            else:
225
200
                self.freeze()
226
 
                pickle.dump(self, outfile, 2)
 
201
                cPickle.dump(self, outfile, 2)
 
202
        finally:
 
203
            outfile.close()
227
204
 
228
205
 
229
206
class _CallTreeFilter(object):
258
235
        out_file = self.out_file
259
236
        code = entry.code
260
237
        inlinetime = int(entry.inlinetime * 1000)
 
238
        #out_file.write('ob=%s\n' % (code.co_filename,))
261
239
        if isinstance(code, str):
262
240
            out_file.write('fi=~\n')
263
241
        else:
284
262
        out_file = self.out_file
285
263
        code = subentry.code
286
264
        totaltime = int(subentry.totaltime * 1000)
 
265
        #out_file.write('cob=%s\n' % (code.co_filename,))
 
266
        out_file.write('cfn=%s\n' % (label(code, True),))
287
267
        if isinstance(code, str):
288
268
            out_file.write('cfi=~\n')
289
 
            out_file.write('cfn=%s\n' % (label(code, True),))
290
269
            out_file.write('calls=%d 0\n' % (subentry.callcount,))
291
270
        else:
292
271
            out_file.write('cfi=%s\n' % (code.co_filename,))
293
 
            out_file.write('cfn=%s\n' % (label(code, True),))
294
272
            out_file.write('calls=%d %d\n' % (
295
273
                subentry.callcount, code.co_firstlineno))
296
274
        out_file.write('%d %d\n' % (lineno, totaltime))
297
275
 
298
 
 
299
276
_fn2mod = {}
300
277
 
301
 
 
302
278
def label(code, calltree=False):
303
279
    if isinstance(code, str):
304
280
        return code
316
292
                mname = _fn2mod[code.co_filename] = k
317
293
                break
318
294
        else:
319
 
            mname = _fn2mod[code.co_filename] = '<%s>' % code.co_filename
 
295
            mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
320
296
    if calltree:
321
297
        return '%s %s:%d' % (code.co_name, mname, code.co_firstlineno)
322
298
    else:
323
299
        return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
324
300
 
325
301
 
326
 
def main():
 
302
if __name__ == '__main__':
 
303
    import os
327
304
    sys.argv = sys.argv[1:]
328
305
    if not sys.argv:
329
306
        sys.stderr.write("usage: lsprof.py <script> <arguments...>\n")
330
307
        sys.exit(2)
331
 
    import runpy
332
 
    result, stats = profile(runpy.run_path, sys.argv[0], run_name='__main__')
 
308
    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
 
309
    stats = profile(execfile, sys.argv[0], globals(), locals())
333
310
    stats.sort()
334
311
    stats.pprint()
335
 
 
336
 
 
337
 
if __name__ == '__main__':
338
 
    main()