blob: 3e2b2edb25290945d5b7d1ee7b2489772f9249fa [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Armin Rigoa871ef22006-02-08 12:53:56 +00002
3"""Python interface for the 'lsprof' profiler.
4 Compatible with the 'profile' module.
5"""
6
Georg Brandlb6b13242009-09-04 17:15:16 +00007__all__ = ["run", "runctx", "Profile"]
Armin Rigoa871ef22006-02-08 12:53:56 +00008
9import _lsprof
10
11# ____________________________________________________________
12# Simple interface
13
14def 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
39def 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
Armin Rigoa871ef22006-02-08 12:53:56 +000059# ____________________________________________________________
60
61class Profile(_lsprof.Profiler):
62 """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True)
63
64 Builds a profiler object using the specified timer function.
65 The default timer is a fast built-in one based on real time.
66 For custom timer functions returning integers, time_unit can
67 be a float specifying a scale (i.e. how long each integer unit
68 is, in seconds).
69 """
70
71 # Most of the functionality is in the base class.
72 # This subclass only adds convenient and backward-compatible methods.
73
74 def print_stats(self, sort=-1):
75 import pstats
76 pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
77
78 def dump_stats(self, file):
79 import marshal
80 f = open(file, 'wb')
81 self.create_stats()
82 marshal.dump(self.stats, f)
83 f.close()
84
85 def create_stats(self):
86 self.disable()
87 self.snapshot_stats()
88
89 def snapshot_stats(self):
90 entries = self.getstats()
91 self.stats = {}
92 callersdicts = {}
93 # call information
94 for entry in entries:
95 func = label(entry.code)
96 nc = entry.callcount # ncalls column of pstats (before '/')
97 cc = nc - entry.reccallcount # ncalls column of pstats (after '/')
98 tt = entry.inlinetime # tottime column of pstats
99 ct = entry.totaltime # cumtime column of pstats
100 callers = {}
101 callersdicts[id(entry.code)] = callers
102 self.stats[func] = cc, nc, tt, ct, callers
103 # subcall information
104 for entry in entries:
105 if entry.calls:
106 func = label(entry.code)
107 for subentry in entry.calls:
108 try:
109 callers = callersdicts[id(subentry.code)]
110 except KeyError:
111 continue
112 nc = subentry.callcount
113 cc = nc - subentry.reccallcount
114 tt = subentry.inlinetime
115 ct = subentry.totaltime
116 if func in callers:
117 prev = callers[func]
118 nc += prev[0]
119 cc += prev[1]
120 tt += prev[2]
121 ct += prev[3]
122 callers[func] = nc, cc, tt, ct
123
124 # The following two methods can be called by clients to use
125 # a profiler to profile a statement, given as a string.
126
127 def run(self, cmd):
128 import __main__
129 dict = __main__.__dict__
130 return self.runctx(cmd, dict, dict)
131
132 def runctx(self, cmd, globals, locals):
133 self.enable()
134 try:
Georg Brandl7cae87c2006-09-06 06:51:57 +0000135 exec(cmd, globals, locals)
Armin Rigoa871ef22006-02-08 12:53:56 +0000136 finally:
137 self.disable()
138 return self
139
140 # This method is more useful to profile a single function call.
141 def runcall(self, func, *args, **kw):
142 self.enable()
143 try:
144 return func(*args, **kw)
145 finally:
146 self.disable()
147
148# ____________________________________________________________
149
150def label(code):
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000151 if isinstance(code, str):
Armin Rigoa871ef22006-02-08 12:53:56 +0000152 return ('~', 0, code) # built-in functions ('~' sorts at the end)
153 else:
154 return (code.co_filename, code.co_firstlineno, code.co_name)
155
156# ____________________________________________________________
157
158def main():
159 import os, sys
160 from optparse import OptionParser
161 usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
162 parser = OptionParser(usage=usage)
163 parser.allow_interspersed_args = False
164 parser.add_option('-o', '--outfile', dest="outfile",
165 help="Save stats to <outfile>", default=None)
166 parser.add_option('-s', '--sort', dest="sort",
167 help="Sort order when printing to stdout, based on pstats.Stats class", default=-1)
168
169 if not sys.argv[1:]:
170 parser.print_usage()
171 sys.exit(2)
172
173 (options, args) = parser.parse_args()
174 sys.argv[:] = args
175
176 if (len(sys.argv) > 0):
177 sys.path.insert(0, os.path.dirname(sys.argv[0]))
Neal Norwitz01688022007-08-12 00:43:29 +0000178 fp = open(sys.argv[0])
179 try:
180 script = fp.read()
181 finally:
182 fp.close()
183 run('exec(%r)' % script, options.outfile, options.sort)
Armin Rigoa871ef22006-02-08 12:53:56 +0000184 else:
185 parser.print_usage()
186 return parser
187
188# When invoked as main program, invoke the profiler on a script
189if __name__ == '__main__':
190 main()