blob: 5d04341d7121115c6eb5941fb0a1ae365e29fe74 [file] [log] [blame]
Armin Rigoa871ef22006-02-08 12:53:56 +00001#! /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
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
59# Backwards compatibility.
60def help():
Guido van Rossumbe19ed72007-02-09 05:37:30 +000061 print("Documentation for the profile/cProfile modules can be found ")
62 print("in the Python Library Reference, section 'The Python Profiler'.")
Armin Rigoa871ef22006-02-08 12:53:56 +000063
64# ____________________________________________________________
65
66class 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:
Georg Brandl7cae87c2006-09-06 06:51:57 +0000140 exec(cmd, globals, locals)
Armin Rigoa871ef22006-02-08 12:53:56 +0000141 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
155def label(code):
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000156 if isinstance(code, str):
Armin Rigoa871ef22006-02-08 12:53:56 +0000157 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
163def 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]))
Neal Norwitz01688022007-08-12 00:43:29 +0000183 fp = open(sys.argv[0])
184 try:
185 script = fp.read()
186 finally:
187 fp.close()
188 run('exec(%r)' % script, options.outfile, options.sort)
Armin Rigoa871ef22006-02-08 12:53:56 +0000189 else:
190 parser.print_usage()
191 return parser
192
193# When invoked as main program, invoke the profiler on a script
194if __name__ == '__main__':
195 main()