121
|
1 #! /usr/bin/env python
|
|
2
|
|
3 import sys
|
|
4 from _lsprof import Profiler, profiler_entry
|
|
5
|
|
6 __all__ = ['profile', 'Stats']
|
|
7
|
|
8 def profile(f, *args, **kwds):
|
|
9 """XXX docstring"""
|
|
10 p = Profiler()
|
|
11 p.enable(subcalls=True, builtins=True)
|
|
12 try:
|
|
13 f(*args, **kwds)
|
|
14 finally:
|
|
15 p.disable()
|
|
16 return Stats(p.getstats())
|
|
17
|
|
18
|
|
19 class Stats(object):
|
|
20 """XXX docstring"""
|
|
21
|
|
22 def __init__(self, data):
|
|
23 self.data = data
|
|
24
|
|
25 def sort(self, crit="inlinetime"):
|
|
26 """XXX docstring"""
|
|
27 if crit not in profiler_entry.__dict__:
|
|
28 raise ValueError("Can't sort by %s" % crit)
|
|
29 self.data.sort(lambda b, a: cmp(getattr(a, crit),
|
|
30 getattr(b, crit)))
|
|
31 for e in self.data:
|
|
32 if e.calls:
|
|
33 e.calls.sort(lambda b, a: cmp(getattr(a, crit),
|
|
34 getattr(b, crit)))
|
|
35
|
|
36 def pprint(self, top=None, file=None, limit=None, climit=None):
|
|
37 """XXX docstring"""
|
|
38 if file is None:
|
|
39 file = sys.stdout
|
|
40 d = self.data
|
|
41 if top is not None:
|
|
42 d = d[:top]
|
|
43 cols = "% 12s %12s %11.4f %11.4f %s\n"
|
|
44 hcols = "% 12s %12s %12s %12s %s\n"
|
|
45 file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
|
|
46 "Inline(ms)", "module:lineno(function)"))
|
|
47 count = 0
|
|
48 for e in d:
|
|
49 file.write(cols % (e.callcount, e.reccallcount, e.totaltime,
|
|
50 e.inlinetime, label(e.code)))
|
|
51 count += 1
|
|
52 if limit is not None and count == limit:
|
|
53 return
|
|
54 ccount = 0
|
|
55 if e.calls:
|
|
56 for se in e.calls:
|
|
57 file.write(cols % ("+%s" % se.callcount, se.reccallcount,
|
|
58 se.totaltime, se.inlinetime,
|
|
59 "+%s" % label(se.code)))
|
|
60 count += 1
|
|
61 ccount += 1
|
|
62 if limit is not None and count == limit:
|
|
63 return
|
|
64 if climit is not None and ccount == climit:
|
|
65 break
|
|
66
|
|
67 def freeze(self):
|
|
68 """Replace all references to code objects with string
|
|
69 descriptions; this makes it possible to pickle the instance."""
|
|
70
|
|
71 # this code is probably rather ickier than it needs to be!
|
|
72 for i in range(len(self.data)):
|
|
73 e = self.data[i]
|
|
74 if not isinstance(e.code, str):
|
|
75 self.data[i] = type(e)((label(e.code),) + e[1:])
|
|
76 if e.calls:
|
|
77 for j in range(len(e.calls)):
|
|
78 se = e.calls[j]
|
|
79 if not isinstance(se.code, str):
|
|
80 e.calls[j] = type(se)((label(se.code),) + se[1:])
|
|
81
|
|
82 _fn2mod = {}
|
|
83
|
|
84 def label(code):
|
|
85 if isinstance(code, str):
|
|
86 return code
|
|
87 try:
|
|
88 mname = _fn2mod[code.co_filename]
|
|
89 except KeyError:
|
|
90 for k, v in sys.modules.iteritems():
|
|
91 if v is None:
|
|
92 continue
|
|
93 if not hasattr(v, '__file__'):
|
|
94 continue
|
|
95 if not isinstance(v.__file__, str):
|
|
96 continue
|
|
97 if v.__file__.startswith(code.co_filename):
|
|
98 mname = _fn2mod[code.co_filename] = k
|
|
99 break
|
|
100 else:
|
|
101 mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
|
|
102
|
|
103 return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
|
|
104
|
|
105
|
|
106 if __name__ == '__main__':
|
|
107 import os
|
|
108 sys.argv = sys.argv[1:]
|
|
109 if not sys.argv:
|
|
110 print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
|
|
111 sys.exit(2)
|
|
112 sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
|
|
113 stats = profile(execfile, sys.argv[0], globals(), locals())
|
|
114 stats.sort()
|
|
115 stats.pprint()
|