|
1 #! /usr/bin/env python |
|
2 |
|
3 """Python interface for the 'lsprof' profiler. |
|
4 Compatible with the 'profile' module. |
|
5 """ |
|
6 |
|
7 __all__ = ["run", "runctx", "help", "Profile"] |
|
8 |
|
9 import _lsprof |
|
10 |
|
11 # ____________________________________________________________ |
|
12 # Simple interface |
|
13 |
|
14 def run(statement, filename=None, sort=-1): |
|
15 """Run statement under profiler optionally saving results in filename |
|
16 |
|
17 This function takes a single argument that can be passed to the |
|
18 "exec" statement, and an optional file name. In all cases this |
|
19 routine attempts to "exec" its first argument and gather profiling |
|
20 statistics from the execution. If no file name is present, then this |
|
21 function automatically prints a simple profiling report, sorted by the |
|
22 standard name string (file/line/function-name) that is presented in |
|
23 each line. |
|
24 """ |
|
25 prof = Profile() |
|
26 result = None |
|
27 try: |
|
28 try: |
|
29 prof = prof.run(statement) |
|
30 except SystemExit: |
|
31 pass |
|
32 finally: |
|
33 if filename is not None: |
|
34 prof.dump_stats(filename) |
|
35 else: |
|
36 result = prof.print_stats(sort) |
|
37 return result |
|
38 |
|
39 def runctx(statement, globals, locals, filename=None): |
|
40 """Run statement under profiler, supplying your own globals and locals, |
|
41 optionally saving results in filename. |
|
42 |
|
43 statement and filename have the same semantics as profile.run |
|
44 """ |
|
45 prof = Profile() |
|
46 result = None |
|
47 try: |
|
48 try: |
|
49 prof = prof.runctx(statement, globals, locals) |
|
50 except SystemExit: |
|
51 pass |
|
52 finally: |
|
53 if filename is not None: |
|
54 prof.dump_stats(filename) |
|
55 else: |
|
56 result = prof.print_stats() |
|
57 return result |
|
58 |
|
59 # Backwards compatibility. |
|
60 def help(): |
|
61 print "Documentation for the profile/cProfile modules can be found " |
|
62 print "in the Python Library Reference, section 'The Python Profiler'." |
|
63 |
|
64 # ____________________________________________________________ |
|
65 |
|
66 class Profile(_lsprof.Profiler): |
|
67 """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True) |
|
68 |
|
69 Builds a profiler object using the specified timer function. |
|
70 The default timer is a fast built-in one based on real time. |
|
71 For custom timer functions returning integers, time_unit can |
|
72 be a float specifying a scale (i.e. how long each integer unit |
|
73 is, in seconds). |
|
74 """ |
|
75 |
|
76 # Most of the functionality is in the base class. |
|
77 # This subclass only adds convenient and backward-compatible methods. |
|
78 |
|
79 def print_stats(self, sort=-1): |
|
80 import pstats |
|
81 pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats() |
|
82 |
|
83 def dump_stats(self, file): |
|
84 import marshal |
|
85 f = open(file, 'wb') |
|
86 self.create_stats() |
|
87 marshal.dump(self.stats, f) |
|
88 f.close() |
|
89 |
|
90 def create_stats(self): |
|
91 self.disable() |
|
92 self.snapshot_stats() |
|
93 |
|
94 def snapshot_stats(self): |
|
95 entries = self.getstats() |
|
96 self.stats = {} |
|
97 callersdicts = {} |
|
98 # call information |
|
99 for entry in entries: |
|
100 func = label(entry.code) |
|
101 nc = entry.callcount # ncalls column of pstats (before '/') |
|
102 cc = nc - entry.reccallcount # ncalls column of pstats (after '/') |
|
103 tt = entry.inlinetime # tottime column of pstats |
|
104 ct = entry.totaltime # cumtime column of pstats |
|
105 callers = {} |
|
106 callersdicts[id(entry.code)] = callers |
|
107 self.stats[func] = cc, nc, tt, ct, callers |
|
108 # subcall information |
|
109 for entry in entries: |
|
110 if entry.calls: |
|
111 func = label(entry.code) |
|
112 for subentry in entry.calls: |
|
113 try: |
|
114 callers = callersdicts[id(subentry.code)] |
|
115 except KeyError: |
|
116 continue |
|
117 nc = subentry.callcount |
|
118 cc = nc - subentry.reccallcount |
|
119 tt = subentry.inlinetime |
|
120 ct = subentry.totaltime |
|
121 if func in callers: |
|
122 prev = callers[func] |
|
123 nc += prev[0] |
|
124 cc += prev[1] |
|
125 tt += prev[2] |
|
126 ct += prev[3] |
|
127 callers[func] = nc, cc, tt, ct |
|
128 |
|
129 # The following two methods can be called by clients to use |
|
130 # a profiler to profile a statement, given as a string. |
|
131 |
|
132 def run(self, cmd): |
|
133 import __main__ |
|
134 dict = __main__.__dict__ |
|
135 return self.runctx(cmd, dict, dict) |
|
136 |
|
137 def runctx(self, cmd, globals, locals): |
|
138 self.enable() |
|
139 try: |
|
140 exec cmd in globals, locals |
|
141 finally: |
|
142 self.disable() |
|
143 return self |
|
144 |
|
145 # This method is more useful to profile a single function call. |
|
146 def runcall(self, func, *args, **kw): |
|
147 self.enable() |
|
148 try: |
|
149 return func(*args, **kw) |
|
150 finally: |
|
151 self.disable() |
|
152 |
|
153 # ____________________________________________________________ |
|
154 |
|
155 def label(code): |
|
156 if isinstance(code, str): |
|
157 return ('~', 0, code) # built-in functions ('~' sorts at the end) |
|
158 else: |
|
159 return (code.co_filename, code.co_firstlineno, code.co_name) |
|
160 |
|
161 # ____________________________________________________________ |
|
162 |
|
163 def main(): |
|
164 import os, sys |
|
165 from optparse import OptionParser |
|
166 usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..." |
|
167 parser = OptionParser(usage=usage) |
|
168 parser.allow_interspersed_args = False |
|
169 parser.add_option('-o', '--outfile', dest="outfile", |
|
170 help="Save stats to <outfile>", default=None) |
|
171 parser.add_option('-s', '--sort', dest="sort", |
|
172 help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) |
|
173 |
|
174 if not sys.argv[1:]: |
|
175 parser.print_usage() |
|
176 sys.exit(2) |
|
177 |
|
178 (options, args) = parser.parse_args() |
|
179 sys.argv[:] = args |
|
180 |
|
181 if (len(sys.argv) > 0): |
|
182 sys.path.insert(0, os.path.dirname(sys.argv[0])) |
|
183 run('execfile(%r)' % (sys.argv[0],), options.outfile, options.sort) |
|
184 else: |
|
185 parser.print_usage() |
|
186 return parser |
|
187 |
|
188 # When invoked as main program, invoke the profiler on a script |
|
189 if __name__ == '__main__': |
|
190 main() |