|
36459
|
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()
|