blob: 1253b66ac7b5d521605e2d0c8935bce800568c46 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossum81762581992-04-21 15:36:23 +00002#
Guido van Rossumb6775db1994-08-01 11:34:53 +00003# Class for profiling python code. rev 1.0 6/2/94
Guido van Rossum81762581992-04-21 15:36:23 +00004#
Guido van Rossumb6775db1994-08-01 11:34:53 +00005# Based on prior profile module by Sjoerd Mullender...
6# which was hacked somewhat by: Guido van Rossum
7#
8# See profile.doc for more information
9
Guido van Rossum54f22ed2000-02-04 15:10:34 +000010"""Class for profiling Python code."""
Guido van Rossumb6775db1994-08-01 11:34:53 +000011
12# Copyright 1994, by InfoSeek Corporation, all rights reserved.
13# Written by James Roskind
Tim Peters2344fae2001-01-15 00:50:52 +000014#
Guido van Rossumb6775db1994-08-01 11:34:53 +000015# Permission to use, copy, modify, and distribute this Python software
16# and its associated documentation for any purpose (subject to the
17# restriction in the following sentence) without fee is hereby granted,
18# provided that the above copyright notice appears in all copies, and
19# that both that copyright notice and this permission notice appear in
20# supporting documentation, and that the name of InfoSeek not be used in
21# advertising or publicity pertaining to distribution of the software
22# without specific, written prior permission. This permission is
23# explicitly restricted to the copying and modification of the software
24# to remain in Python, compiled Python, or other languages (such as C)
25# wherein the modified or derived code is exclusively imported into a
26# Python module.
Tim Peters2344fae2001-01-15 00:50:52 +000027#
Guido van Rossumb6775db1994-08-01 11:34:53 +000028# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
29# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
30# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
31# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
32# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
33# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
34# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35
36
Guido van Rossum81762581992-04-21 15:36:23 +000037
38import sys
Guido van Rossum4e160981992-09-02 20:43:20 +000039import os
Guido van Rossumb6775db1994-08-01 11:34:53 +000040import time
Guido van Rossum4e160981992-09-02 20:43:20 +000041import marshal
Guido van Rossum81762581992-04-21 15:36:23 +000042
Skip Montanaroc62c81e2001-02-12 02:00:42 +000043__all__ = ["run","help","Profile"]
Guido van Rossum81762581992-04-21 15:36:23 +000044
Tim Peters2344fae2001-01-15 00:50:52 +000045# Sample timer for use with
Guido van Rossumb6775db1994-08-01 11:34:53 +000046#i_count = 0
47#def integer_timer():
Tim Peters2344fae2001-01-15 00:50:52 +000048# global i_count
49# i_count = i_count + 1
50# return i_count
Guido van Rossumb6775db1994-08-01 11:34:53 +000051#itimes = integer_timer # replace with C coded timer returning integers
Guido van Rossum81762581992-04-21 15:36:23 +000052
Guido van Rossumb6775db1994-08-01 11:34:53 +000053#**************************************************************************
54# The following are the static member functions for the profiler class
55# Note that an instance of Profile() is *not* needed to call them.
56#**************************************************************************
Guido van Rossum81762581992-04-21 15:36:23 +000057
Jeremy Hyltonadcf8a02001-03-14 20:01:19 +000058def run(statement, filename=None):
59 """Run statement under profiler optionally saving results in filename
Guido van Rossum4e160981992-09-02 20:43:20 +000060
Jeremy Hyltonadcf8a02001-03-14 20:01:19 +000061 This function takes a single argument that can be passed to the
62 "exec" statement, and an optional file name. In all cases this
63 routine attempts to "exec" its first argument and gather profiling
64 statistics from the execution. If no file name is present, then this
65 function automatically prints a simple profiling report, sorted by the
66 standard name string (file/line/function-name) that is presented in
67 each line.
68 """
Tim Peters2344fae2001-01-15 00:50:52 +000069 prof = Profile()
70 try:
71 prof = prof.run(statement)
72 except SystemExit:
73 pass
Jeremy Hyltonadcf8a02001-03-14 20:01:19 +000074 if filename is not None:
75 prof.dump_stats(filename)
Tim Peters2344fae2001-01-15 00:50:52 +000076 else:
77 return prof.print_stats()
Guido van Rossume61fa0a1993-10-22 13:56:35 +000078
79# print help
80def help():
Tim Peters2344fae2001-01-15 00:50:52 +000081 for dirname in sys.path:
82 fullname = os.path.join(dirname, 'profile.doc')
83 if os.path.exists(fullname):
84 sts = os.system('${PAGER-more} '+fullname)
85 if sts: print '*** Pager exit status:', sts
86 break
87 else:
88 print 'Sorry, can\'t find the help file "profile.doc"',
89 print 'along the Python search path'
Guido van Rossumb6775db1994-08-01 11:34:53 +000090
91
Fred Drakeedb5ffb2001-06-08 04:25:24 +000092if os.name == "mac":
Jack Jansen1bdcadd2001-06-19 20:11:36 +000093 import MacOS
Fred Drakeedb5ffb2001-06-08 04:25:24 +000094 def _get_time_mac(timer=MacOS.GetTicks):
95 return timer() / 60.0
96
97if hasattr(os, "times"):
98 def _get_time_times(timer=os.times):
99 t = timer()
100 return t[0] + t[1]
101
102
Guido van Rossumb6775db1994-08-01 11:34:53 +0000103class Profile:
Tim Peters2344fae2001-01-15 00:50:52 +0000104 """Profiler class.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000105
Tim Peters2344fae2001-01-15 00:50:52 +0000106 self.cur is always a tuple. Each such tuple corresponds to a stack
107 frame that is currently active (self.cur[-2]). The following are the
108 definitions of its members. We use this external "parallel stack" to
109 avoid contaminating the program that we are profiling. (old profiler
110 used to write into the frames local dictionary!!) Derived classes
111 can change the definition of some entries, as long as they leave
Tim Petersdf5cfd82001-10-05 23:15:10 +0000112 [-2:] intact (frame and previous tuple). In case an internal error is
113 detected, the -3 element is used as the function name.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000114
Tim Petersdf5cfd82001-10-05 23:15:10 +0000115 [ 0] = Time that needs to be charged to the parent frame's function.
116 It is used so that a function call will not have to access the
117 timing data for the parent frame.
118 [ 1] = Total time spent in this frame's function, excluding time in
119 subfunctions
120 [ 2] = Cumulative time spent in this frame's function, including time in
121 all subfunctions to this frame.
122 [-3] = Name of the function that corresponds to this frame.
123 [-2] = Actual frame that we correspond to (used to sync exception handling)
124 [-1] = Our parent 6-tuple (corresponds to frame.f_back)
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000125
Tim Peters2344fae2001-01-15 00:50:52 +0000126 Timing data for each function is stored as a 5-tuple in the dictionary
Tim Petersdf5cfd82001-10-05 23:15:10 +0000127 self.timings[]. The index is always the name stored in self.cur[-3].
Tim Peters2344fae2001-01-15 00:50:52 +0000128 The following are the definitions of the members:
Guido van Rossumb6775db1994-08-01 11:34:53 +0000129
Tim Peters2344fae2001-01-15 00:50:52 +0000130 [0] = The number of times this function was called, not counting direct
131 or indirect recursion,
132 [1] = Number of times this function appears on the stack, minus one
133 [2] = Total time spent internal to this function
134 [3] = Cumulative time that this function was present on the stack. In
135 non-recursive functions, this is the total execution time from start
136 to finish of each invocation of a function, including time spent in
137 all subfunctions.
Tim Peters6e221492001-10-07 04:02:36 +0000138 [4] = A dictionary indicating for each function name, the number of times
Tim Peters2344fae2001-01-15 00:50:52 +0000139 it was called by us.
140 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000141
Tim Peters2344fae2001-01-15 00:50:52 +0000142 def __init__(self, timer=None):
143 self.timings = {}
144 self.cur = None
145 self.cmd = ""
Guido van Rossumb6775db1994-08-01 11:34:53 +0000146
Tim Peters2344fae2001-01-15 00:50:52 +0000147 if not timer:
148 if os.name == 'mac':
Tim Peters2344fae2001-01-15 00:50:52 +0000149 self.timer = MacOS.GetTicks
150 self.dispatcher = self.trace_dispatch_mac
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000151 self.get_time = _get_time_mac
Tim Peters2344fae2001-01-15 00:50:52 +0000152 elif hasattr(time, 'clock'):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000153 self.timer = self.get_time = time.clock
Tim Peters2344fae2001-01-15 00:50:52 +0000154 self.dispatcher = self.trace_dispatch_i
155 elif hasattr(os, 'times'):
156 self.timer = os.times
157 self.dispatcher = self.trace_dispatch
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000158 self.get_time = _get_time_times
Tim Peters2344fae2001-01-15 00:50:52 +0000159 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000160 self.timer = self.get_time = time.time
Tim Peters2344fae2001-01-15 00:50:52 +0000161 self.dispatcher = self.trace_dispatch_i
162 else:
163 self.timer = timer
164 t = self.timer() # test out timer function
165 try:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000166 length = len(t)
167 except TypeError:
168 self.get_time = timer
169 self.dispatcher = self.trace_dispatch_i
170 else:
171 if length == 2:
Tim Peters2344fae2001-01-15 00:50:52 +0000172 self.dispatcher = self.trace_dispatch
173 else:
174 self.dispatcher = self.trace_dispatch_l
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000175 # This get_time() implementation needs to be defined
176 # here to capture the passed-in timer in the parameter
177 # list (for performance). Note that we can't assume
178 # the timer() result contains two values in all
179 # cases.
Guido van Rossume4deb952001-08-09 21:22:15 +0000180 import operator
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000181 def get_time_timer(timer=timer,
182 reduce=reduce, reducer=operator.add):
183 return reduce(reducer, timer(), 0)
184 self.get_time = get_time_timer
Tim Peters2344fae2001-01-15 00:50:52 +0000185 self.t = self.get_time()
186 self.simulate_call('profiler')
Guido van Rossumb6775db1994-08-01 11:34:53 +0000187
Tim Peters2344fae2001-01-15 00:50:52 +0000188 # Heavily optimized dispatch routine for os.times() timer
Guido van Rossumb6775db1994-08-01 11:34:53 +0000189
Tim Peters2344fae2001-01-15 00:50:52 +0000190 def trace_dispatch(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000191 timer = self.timer
192 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000193 t = t[0] + t[1] - self.t # No Calibration constant
194 # t = t[0] + t[1] - self.t - .00053 # Calibration constant
195
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000196 if self.dispatch[event](self, frame,t):
197 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000198 self.t = t[0] + t[1]
199 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000200 r = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000201 self.t = r[0] + r[1] - t # put back unrecorded delta
202 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000203
204
Tim Peters6e221492001-10-07 04:02:36 +0000205 # Dispatch routine for best timer program (return = scalar, fastest if
206 # an integer but float works too -- and time.clock() relies on that).
Guido van Rossumb6775db1994-08-01 11:34:53 +0000207
Tim Peters2344fae2001-01-15 00:50:52 +0000208 def trace_dispatch_i(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000209 timer = self.timer
Tim Peters6e221492001-10-07 04:02:36 +0000210 t = timer() - self.t # - 1 # calibration constant
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000211 if self.dispatch[event](self, frame,t):
212 self.t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000213 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000214 self.t = timer() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000215 return
Guido van Rossumcbf3dd51997-10-08 15:23:02 +0000216
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000217 # Dispatch routine for macintosh (timer returns time in ticks of
218 # 1/60th second)
Tim Peters2344fae2001-01-15 00:50:52 +0000219
220 def trace_dispatch_mac(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000221 timer = self.timer
222 t = timer()/60.0 - self.t # - 1 # Integer calibration constant
223 if self.dispatch[event](self, frame,t):
224 self.t = timer()/60.0
Tim Peters2344fae2001-01-15 00:50:52 +0000225 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000226 self.t = timer()/60.0 - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000227 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000228
229
Tim Peters2344fae2001-01-15 00:50:52 +0000230 # SLOW generic dispatch routine for timer returning lists of numbers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000231
Tim Peters2344fae2001-01-15 00:50:52 +0000232 def trace_dispatch_l(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000233 get_time = self.get_time
234 t = get_time() - self.t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000235
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000236 if self.dispatch[event](self, frame,t):
237 self.t = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000238 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000239 self.t = get_time() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000240 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000241
242
Tim Peters2344fae2001-01-15 00:50:52 +0000243 def trace_dispatch_exception(self, frame, t):
244 rt, rtt, rct, rfn, rframe, rcur = self.cur
Fred Drakea0bc9992001-10-03 21:12:32 +0000245 if (rframe is not frame) and rcur:
Tim Peters2344fae2001-01-15 00:50:52 +0000246 return self.trace_dispatch_return(rframe, t)
Guido van Rossumf137f752001-10-04 00:58:24 +0000247 self.cur = rt, rtt+t, rct, rfn, rframe, rcur
248 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000249
250
Tim Peters2344fae2001-01-15 00:50:52 +0000251 def trace_dispatch_call(self, frame, t):
Tim Petersdf5cfd82001-10-05 23:15:10 +0000252 if self.cur and frame.f_back is not self.cur[-2]:
Guido van Rossumf137f752001-10-04 00:58:24 +0000253 rt, rtt, rct, rfn, rframe, rcur = self.cur
254 if not isinstance(rframe, Profile.fake_frame):
Tim Petersdb1ed2a2001-10-07 04:30:53 +0000255 assert rframe.f_back is frame.f_back, ("Bad call", rfn,
256 rframe, rframe.f_back,
257 frame, frame.f_back)
Guido van Rossumf137f752001-10-04 00:58:24 +0000258 self.trace_dispatch_return(rframe, 0)
Tim Petersdb1ed2a2001-10-07 04:30:53 +0000259 assert (self.cur is None or \
260 frame.f_back is self.cur[-2]), ("Bad call",
261 self.cur[-3])
Tim Peters2344fae2001-01-15 00:50:52 +0000262 fcode = frame.f_code
263 fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
264 self.cur = (t, 0, 0, fn, frame, self.cur)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000265 timings = self.timings
266 if timings.has_key(fn):
267 cc, ns, tt, ct, callers = timings[fn]
268 timings[fn] = cc, ns + 1, tt, ct, callers
Tim Peters2344fae2001-01-15 00:50:52 +0000269 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000270 timings[fn] = 0, 0, 0, 0, {}
Tim Peters2344fae2001-01-15 00:50:52 +0000271 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000272
Tim Peters2344fae2001-01-15 00:50:52 +0000273 def trace_dispatch_return(self, frame, t):
Tim Petersdf5cfd82001-10-05 23:15:10 +0000274 if frame is not self.cur[-2]:
Tim Petersdb1ed2a2001-10-07 04:30:53 +0000275 assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
276 self.trace_dispatch_return(self.cur[-2], 0)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000277
Tim Peters2344fae2001-01-15 00:50:52 +0000278 # Prefix "r" means part of the Returning or exiting frame
279 # Prefix "p" means part of the Previous or older frame
Guido van Rossumb6775db1994-08-01 11:34:53 +0000280
Tim Peters2344fae2001-01-15 00:50:52 +0000281 rt, rtt, rct, rfn, frame, rcur = self.cur
282 rtt = rtt + t
283 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000284
Tim Peters2344fae2001-01-15 00:50:52 +0000285 pt, ptt, pct, pfn, pframe, pcur = rcur
286 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000287
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000288 timings = self.timings
289 cc, ns, tt, ct, callers = timings[rfn]
Tim Peters2344fae2001-01-15 00:50:52 +0000290 if not ns:
291 ct = ct + sft
292 cc = cc + 1
293 if callers.has_key(pfn):
294 callers[pfn] = callers[pfn] + 1 # hack: gather more
295 # stats such as the amount of time added to ct courtesy
296 # of this specific call, and the contribution to cc
297 # courtesy of this call.
298 else:
299 callers[pfn] = 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000300 timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000301
Tim Peters2344fae2001-01-15 00:50:52 +0000302 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000303
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000304
305 dispatch = {
306 "call": trace_dispatch_call,
307 "exception": trace_dispatch_exception,
308 "return": trace_dispatch_return,
309 }
310
311
Tim Petersdb1ed2a2001-10-07 04:30:53 +0000312 # The next few functions play with self.cmd. By carefully preloading
Tim Peters2344fae2001-01-15 00:50:52 +0000313 # our parallel stack, we can force the profiled result to include
314 # an arbitrary string as the name of the calling function.
315 # We use self.cmd as that string, and the resulting stats look
316 # very nice :-).
Guido van Rossumb6775db1994-08-01 11:34:53 +0000317
Tim Peters2344fae2001-01-15 00:50:52 +0000318 def set_cmd(self, cmd):
Tim Petersdf5cfd82001-10-05 23:15:10 +0000319 if self.cur[-1]: return # already set
Tim Peters2344fae2001-01-15 00:50:52 +0000320 self.cmd = cmd
321 self.simulate_call(cmd)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000322
Tim Peters2344fae2001-01-15 00:50:52 +0000323 class fake_code:
324 def __init__(self, filename, line, name):
325 self.co_filename = filename
326 self.co_line = line
327 self.co_name = name
328 self.co_firstlineno = 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000329
Tim Peters2344fae2001-01-15 00:50:52 +0000330 def __repr__(self):
331 return repr((self.co_filename, self.co_line, self.co_name))
Guido van Rossumb6775db1994-08-01 11:34:53 +0000332
Tim Peters2344fae2001-01-15 00:50:52 +0000333 class fake_frame:
334 def __init__(self, code, prior):
335 self.f_code = code
336 self.f_back = prior
Guido van Rossumb6775db1994-08-01 11:34:53 +0000337
Tim Peters2344fae2001-01-15 00:50:52 +0000338 def simulate_call(self, name):
339 code = self.fake_code('profile', 0, name)
340 if self.cur:
Tim Petersdf5cfd82001-10-05 23:15:10 +0000341 pframe = self.cur[-2]
Tim Peters2344fae2001-01-15 00:50:52 +0000342 else:
343 pframe = None
344 frame = self.fake_frame(code, pframe)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000345 a = self.dispatch['call'](self, frame, 0)
Tim Peters2344fae2001-01-15 00:50:52 +0000346 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000347
Tim Peters2344fae2001-01-15 00:50:52 +0000348 # collect stats from pending stack, including getting final
349 # timings for self.cmd frame.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000350
Tim Peters2344fae2001-01-15 00:50:52 +0000351 def simulate_cmd_complete(self):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000352 get_time = self.get_time
353 t = get_time() - self.t
Tim Petersdf5cfd82001-10-05 23:15:10 +0000354 while self.cur[-1]:
Tim Peters2344fae2001-01-15 00:50:52 +0000355 # We *can* cause assertion errors here if
356 # dispatch_trace_return checks for a frame match!
Tim Petersdf5cfd82001-10-05 23:15:10 +0000357 a = self.dispatch['return'](self, self.cur[-2], t)
Tim Peters2344fae2001-01-15 00:50:52 +0000358 t = 0
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000359 self.t = get_time() - t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000360
361
Tim Peters2344fae2001-01-15 00:50:52 +0000362 def print_stats(self):
363 import pstats
364 pstats.Stats(self).strip_dirs().sort_stats(-1). \
365 print_stats()
Guido van Rossumb6775db1994-08-01 11:34:53 +0000366
Tim Peters2344fae2001-01-15 00:50:52 +0000367 def dump_stats(self, file):
368 f = open(file, 'wb')
369 self.create_stats()
370 marshal.dump(self.stats, f)
371 f.close()
372
373 def create_stats(self):
374 self.simulate_cmd_complete()
375 self.snapshot_stats()
376
377 def snapshot_stats(self):
378 self.stats = {}
379 for func in self.timings.keys():
380 cc, ns, tt, ct, callers = self.timings[func]
381 callers = callers.copy()
382 nc = 0
383 for func_caller in callers.keys():
384 nc = nc + callers[func_caller]
385 self.stats[func] = cc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000386
387
Tim Peters2344fae2001-01-15 00:50:52 +0000388 # The following two methods can be called by clients to use
389 # a profiler to profile a statement, given as a string.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000390
Tim Peters2344fae2001-01-15 00:50:52 +0000391 def run(self, cmd):
392 import __main__
393 dict = __main__.__dict__
394 return self.runctx(cmd, dict, dict)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000395
Tim Peters2344fae2001-01-15 00:50:52 +0000396 def runctx(self, cmd, globals, locals):
397 self.set_cmd(cmd)
398 sys.setprofile(self.dispatcher)
399 try:
400 exec cmd in globals, locals
401 finally:
402 sys.setprofile(None)
403 return self
Guido van Rossumb6775db1994-08-01 11:34:53 +0000404
Tim Peters2344fae2001-01-15 00:50:52 +0000405 # This method is more useful to profile a single function call.
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000406 def runcall(self, func, *args, **kw):
Tim Peters2344fae2001-01-15 00:50:52 +0000407 self.set_cmd(`func`)
408 sys.setprofile(self.dispatcher)
409 try:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000410 return apply(func, args, kw)
Tim Peters2344fae2001-01-15 00:50:52 +0000411 finally:
412 sys.setprofile(None)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000413
Tim Peters2344fae2001-01-15 00:50:52 +0000414
415 #******************************************************************
416 # The following calculates the overhead for using a profiler. The
417 # problem is that it takes a fair amount of time for the profiler
418 # to stop the stopwatch (from the time it receives an event).
419 # Similarly, there is a delay from the time that the profiler
420 # re-starts the stopwatch before the user's code really gets to
421 # continue. The following code tries to measure the difference on
422 # a per-event basis. The result can the be placed in the
423 # Profile.dispatch_event() routine for the given platform. Note
424 # that this difference is only significant if there are a lot of
425 # events, and relatively little user code per event. For example,
426 # code with small functions will typically benefit from having the
427 # profiler calibrated for the current platform. This *could* be
428 # done on the fly during init() time, but it is not worth the
429 # effort. Also note that if too large a value specified, then
430 # execution time on some functions will actually appear as a
431 # negative number. It is *normal* for some functions (with very
432 # low call counts) to have such negative stats, even if the
433 # calibration figure is "correct."
434 #
435 # One alternative to profile-time calibration adjustments (i.e.,
436 # adding in the magic little delta during each event) is to track
437 # more carefully the number of events (and cumulatively, the number
438 # of events during sub functions) that are seen. If this were
439 # done, then the arithmetic could be done after the fact (i.e., at
440 # display time). Currently, we track only call/return events.
441 # These values can be deduced by examining the callees and callers
442 # vectors for each functions. Hence we *can* almost correct the
443 # internal time figure at print time (note that we currently don't
444 # track exception event processing counts). Unfortunately, there
445 # is currently no similar information for cumulative sub-function
446 # time. It would not be hard to "get all this info" at profiler
447 # time. Specifically, we would have to extend the tuples to keep
448 # counts of this in each frame, and then extend the defs of timing
449 # tuples to include the significant two figures. I'm a bit fearful
450 # that this additional feature will slow the heavily optimized
451 # event/time ratio (i.e., the profiler would run slower, fur a very
452 # low "value added" feature.)
453 #
454 # Plugging in the calibration constant doesn't slow down the
455 # profiler very much, and the accuracy goes way up.
456 #**************************************************************
457
458 def calibrate(self, m):
459 # Modified by Tim Peters
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000460 get_time = self.get_time
Tim Peters2344fae2001-01-15 00:50:52 +0000461 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000462 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000463 while n:
464 self.simple()
465 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000466 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000467 my_simple = f - s
468 #print "Simple =", my_simple,
469
470 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000471 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000472 while n:
473 self.instrumented()
474 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000475 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000476 my_inst = f - s
477 # print "Instrumented =", my_inst
478 avg_cost = (my_inst - my_simple)/m
479 #print "Delta/call =", avg_cost, "(profiler fixup constant)"
480 return avg_cost
481
482 # simulate a program with no profiler activity
483 def simple(self):
484 a = 1
485 pass
486
487 # simulate a program with call/return event processing
488 def instrumented(self):
489 a = 1
490 self.profiler_simulation(a, a, a)
491
492 # simulate an event processing activity (from user's perspective)
493 def profiler_simulation(self, x, y, z):
494 t = self.timer()
495 ## t = t[0] + t[1]
496 self.ut = t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000497
Guido van Rossumb6775db1994-08-01 11:34:53 +0000498#****************************************************************************
499def Stats(*args):
Tim Peters2344fae2001-01-15 00:50:52 +0000500 print 'Report generating functions are in the "pstats" module\a'
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000501
502
503# When invoked as main program, invoke the profiler on a script
504if __name__ == '__main__':
Tim Peters2344fae2001-01-15 00:50:52 +0000505 if not sys.argv[1:]:
506 print "usage: profile.py scriptfile [arg] ..."
507 sys.exit(2)
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000508
Tim Peters2344fae2001-01-15 00:50:52 +0000509 filename = sys.argv[1] # Get script filename
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000510
Tim Peters2344fae2001-01-15 00:50:52 +0000511 del sys.argv[0] # Hide "profile.py" from argument list
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000512
Tim Peters2344fae2001-01-15 00:50:52 +0000513 # Insert script directory in front of module search path
514 sys.path.insert(0, os.path.dirname(filename))
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000515
Tim Peters2344fae2001-01-15 00:50:52 +0000516 run('execfile(' + `filename` + ')')