blob: a6d56376c03c06ef5af6fa25bc5a77d4e18d573e [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":
93 def _get_time_mac(timer=MacOS.GetTicks):
94 return timer() / 60.0
95
96if hasattr(os, "times"):
97 def _get_time_times(timer=os.times):
98 t = timer()
99 return t[0] + t[1]
100
101
Guido van Rossumb6775db1994-08-01 11:34:53 +0000102class Profile:
Tim Peters2344fae2001-01-15 00:50:52 +0000103 """Profiler class.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000104
Tim Peters2344fae2001-01-15 00:50:52 +0000105 self.cur is always a tuple. Each such tuple corresponds to a stack
106 frame that is currently active (self.cur[-2]). The following are the
107 definitions of its members. We use this external "parallel stack" to
108 avoid contaminating the program that we are profiling. (old profiler
109 used to write into the frames local dictionary!!) Derived classes
110 can change the definition of some entries, as long as they leave
111 [-2:] intact.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000112
Tim Peters2344fae2001-01-15 00:50:52 +0000113 [ 0] = Time that needs to be charged to the parent frame's function.
114 It is used so that a function call will not have to access the
115 timing data for the parent frame.
116 [ 1] = Total time spent in this frame's function, excluding time in
117 subfunctions
118 [ 2] = Cumulative time spent in this frame's function, including time in
119 all subfunctions to this frame.
120 [-3] = Name of the function that corresponds to this frame.
121 [-2] = Actual frame that we correspond to (used to sync exception handling)
122 [-1] = Our parent 6-tuple (corresponds to frame.f_back)
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000123
Tim Peters2344fae2001-01-15 00:50:52 +0000124 Timing data for each function is stored as a 5-tuple in the dictionary
125 self.timings[]. The index is always the name stored in self.cur[4].
126 The following are the definitions of the members:
Guido van Rossumb6775db1994-08-01 11:34:53 +0000127
Tim Peters2344fae2001-01-15 00:50:52 +0000128 [0] = The number of times this function was called, not counting direct
129 or indirect recursion,
130 [1] = Number of times this function appears on the stack, minus one
131 [2] = Total time spent internal to this function
132 [3] = Cumulative time that this function was present on the stack. In
133 non-recursive functions, this is the total execution time from start
134 to finish of each invocation of a function, including time spent in
135 all subfunctions.
136 [5] = A dictionary indicating for each function name, the number of times
137 it was called by us.
138 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000139
Tim Peters2344fae2001-01-15 00:50:52 +0000140 def __init__(self, timer=None):
141 self.timings = {}
142 self.cur = None
143 self.cmd = ""
Guido van Rossumb6775db1994-08-01 11:34:53 +0000144
Tim Peters2344fae2001-01-15 00:50:52 +0000145 if not timer:
146 if os.name == 'mac':
147 import MacOS
148 self.timer = MacOS.GetTicks
149 self.dispatcher = self.trace_dispatch_mac
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000150 self.get_time = _get_time_mac
Tim Peters2344fae2001-01-15 00:50:52 +0000151 elif hasattr(time, 'clock'):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000152 self.timer = self.get_time = time.clock
Tim Peters2344fae2001-01-15 00:50:52 +0000153 self.dispatcher = self.trace_dispatch_i
154 elif hasattr(os, 'times'):
155 self.timer = os.times
156 self.dispatcher = self.trace_dispatch
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000157 self.get_time = _get_time_times
Tim Peters2344fae2001-01-15 00:50:52 +0000158 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000159 self.timer = self.get_time = time.time
Tim Peters2344fae2001-01-15 00:50:52 +0000160 self.dispatcher = self.trace_dispatch_i
161 else:
162 self.timer = timer
163 t = self.timer() # test out timer function
164 try:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000165 length = len(t)
166 except TypeError:
167 self.get_time = timer
168 self.dispatcher = self.trace_dispatch_i
169 else:
170 if length == 2:
Tim Peters2344fae2001-01-15 00:50:52 +0000171 self.dispatcher = self.trace_dispatch
172 else:
173 self.dispatcher = self.trace_dispatch_l
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000174 # This get_time() implementation needs to be defined
175 # here to capture the passed-in timer in the parameter
176 # list (for performance). Note that we can't assume
177 # the timer() result contains two values in all
178 # cases.
179 def get_time_timer(timer=timer,
180 reduce=reduce, reducer=operator.add):
181 return reduce(reducer, timer(), 0)
182 self.get_time = get_time_timer
Tim Peters2344fae2001-01-15 00:50:52 +0000183 self.t = self.get_time()
184 self.simulate_call('profiler')
Guido van Rossumb6775db1994-08-01 11:34:53 +0000185
Tim Peters2344fae2001-01-15 00:50:52 +0000186 # Heavily optimized dispatch routine for os.times() timer
Guido van Rossumb6775db1994-08-01 11:34:53 +0000187
Tim Peters2344fae2001-01-15 00:50:52 +0000188 def trace_dispatch(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000189 timer = self.timer
190 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000191 t = t[0] + t[1] - self.t # No Calibration constant
192 # t = t[0] + t[1] - self.t - .00053 # Calibration constant
193
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000194 if self.dispatch[event](self, frame,t):
195 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000196 self.t = t[0] + t[1]
197 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000198 r = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000199 self.t = r[0] + r[1] - t # put back unrecorded delta
200 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000201
202
203
Tim Peters2344fae2001-01-15 00:50:52 +0000204 # Dispatch routine for best timer program (return = scalar integer)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000205
Tim Peters2344fae2001-01-15 00:50:52 +0000206 def trace_dispatch_i(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000207 timer = self.timer
208 t = timer() - self.t # - 1 # Integer calibration constant
209 if self.dispatch[event](self, frame,t):
210 self.t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000211 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000212 self.t = timer() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000213 return
Guido van Rossumcbf3dd51997-10-08 15:23:02 +0000214
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000215 # Dispatch routine for macintosh (timer returns time in ticks of
216 # 1/60th second)
Tim Peters2344fae2001-01-15 00:50:52 +0000217
218 def trace_dispatch_mac(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000219 timer = self.timer
220 t = timer()/60.0 - self.t # - 1 # Integer calibration constant
221 if self.dispatch[event](self, frame,t):
222 self.t = timer()/60.0
Tim Peters2344fae2001-01-15 00:50:52 +0000223 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000224 self.t = timer()/60.0 - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000225 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000226
227
Tim Peters2344fae2001-01-15 00:50:52 +0000228 # SLOW generic dispatch routine for timer returning lists of numbers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000229
Tim Peters2344fae2001-01-15 00:50:52 +0000230 def trace_dispatch_l(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000231 get_time = self.get_time
232 t = get_time() - self.t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000233
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000234 if self.dispatch[event](self, frame,t):
235 self.t = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000236 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000237 self.t = get_time() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000238 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000239
240
Tim Peters2344fae2001-01-15 00:50:52 +0000241 def trace_dispatch_exception(self, frame, t):
242 rt, rtt, rct, rfn, rframe, rcur = self.cur
243 if (not rframe is frame) and rcur:
244 return self.trace_dispatch_return(rframe, t)
245 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000246
247
Tim Peters2344fae2001-01-15 00:50:52 +0000248 def trace_dispatch_call(self, frame, t):
249 fcode = frame.f_code
250 fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
251 self.cur = (t, 0, 0, fn, frame, self.cur)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000252 timings = self.timings
253 if timings.has_key(fn):
254 cc, ns, tt, ct, callers = timings[fn]
255 timings[fn] = cc, ns + 1, tt, ct, callers
Tim Peters2344fae2001-01-15 00:50:52 +0000256 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000257 timings[fn] = 0, 0, 0, 0, {}
Tim Peters2344fae2001-01-15 00:50:52 +0000258 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000259
Tim Peters2344fae2001-01-15 00:50:52 +0000260 def trace_dispatch_return(self, frame, t):
261 # if not frame is self.cur[-2]: raise "Bad return", self.cur[3]
Guido van Rossumb6775db1994-08-01 11:34:53 +0000262
Tim Peters2344fae2001-01-15 00:50:52 +0000263 # Prefix "r" means part of the Returning or exiting frame
264 # Prefix "p" means part of the Previous or older frame
Guido van Rossumb6775db1994-08-01 11:34:53 +0000265
Tim Peters2344fae2001-01-15 00:50:52 +0000266 rt, rtt, rct, rfn, frame, rcur = self.cur
267 rtt = rtt + t
268 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000269
Tim Peters2344fae2001-01-15 00:50:52 +0000270 pt, ptt, pct, pfn, pframe, pcur = rcur
271 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000272
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000273 timings = self.timings
274 cc, ns, tt, ct, callers = timings[rfn]
Tim Peters2344fae2001-01-15 00:50:52 +0000275 if not ns:
276 ct = ct + sft
277 cc = cc + 1
278 if callers.has_key(pfn):
279 callers[pfn] = callers[pfn] + 1 # hack: gather more
280 # stats such as the amount of time added to ct courtesy
281 # of this specific call, and the contribution to cc
282 # courtesy of this call.
283 else:
284 callers[pfn] = 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000285 timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000286
Tim Peters2344fae2001-01-15 00:50:52 +0000287 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000288
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000289
290 dispatch = {
291 "call": trace_dispatch_call,
292 "exception": trace_dispatch_exception,
293 "return": trace_dispatch_return,
294 }
295
296
Tim Peters2344fae2001-01-15 00:50:52 +0000297 # The next few function play with self.cmd. By carefully preloading
298 # our parallel stack, we can force the profiled result to include
299 # an arbitrary string as the name of the calling function.
300 # We use self.cmd as that string, and the resulting stats look
301 # very nice :-).
Guido van Rossumb6775db1994-08-01 11:34:53 +0000302
Tim Peters2344fae2001-01-15 00:50:52 +0000303 def set_cmd(self, cmd):
304 if self.cur[-1]: return # already set
305 self.cmd = cmd
306 self.simulate_call(cmd)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000307
Tim Peters2344fae2001-01-15 00:50:52 +0000308 class fake_code:
309 def __init__(self, filename, line, name):
310 self.co_filename = filename
311 self.co_line = line
312 self.co_name = name
313 self.co_firstlineno = 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000314
Tim Peters2344fae2001-01-15 00:50:52 +0000315 def __repr__(self):
316 return repr((self.co_filename, self.co_line, self.co_name))
Guido van Rossumb6775db1994-08-01 11:34:53 +0000317
Tim Peters2344fae2001-01-15 00:50:52 +0000318 class fake_frame:
319 def __init__(self, code, prior):
320 self.f_code = code
321 self.f_back = prior
Guido van Rossumb6775db1994-08-01 11:34:53 +0000322
Tim Peters2344fae2001-01-15 00:50:52 +0000323 def simulate_call(self, name):
324 code = self.fake_code('profile', 0, name)
325 if self.cur:
326 pframe = self.cur[-2]
327 else:
328 pframe = None
329 frame = self.fake_frame(code, pframe)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000330 a = self.dispatch['call'](self, frame, 0)
Tim Peters2344fae2001-01-15 00:50:52 +0000331 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000332
Tim Peters2344fae2001-01-15 00:50:52 +0000333 # collect stats from pending stack, including getting final
334 # timings for self.cmd frame.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000335
Tim Peters2344fae2001-01-15 00:50:52 +0000336 def simulate_cmd_complete(self):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000337 get_time = self.get_time
338 t = get_time() - self.t
Tim Peters2344fae2001-01-15 00:50:52 +0000339 while self.cur[-1]:
340 # We *can* cause assertion errors here if
341 # dispatch_trace_return checks for a frame match!
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000342 a = self.dispatch['return'](self, self.cur[-2], t)
Tim Peters2344fae2001-01-15 00:50:52 +0000343 t = 0
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000344 self.t = get_time() - t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000345
346
Tim Peters2344fae2001-01-15 00:50:52 +0000347 def print_stats(self):
348 import pstats
349 pstats.Stats(self).strip_dirs().sort_stats(-1). \
350 print_stats()
Guido van Rossumb6775db1994-08-01 11:34:53 +0000351
Tim Peters2344fae2001-01-15 00:50:52 +0000352 def dump_stats(self, file):
353 f = open(file, 'wb')
354 self.create_stats()
355 marshal.dump(self.stats, f)
356 f.close()
357
358 def create_stats(self):
359 self.simulate_cmd_complete()
360 self.snapshot_stats()
361
362 def snapshot_stats(self):
363 self.stats = {}
364 for func in self.timings.keys():
365 cc, ns, tt, ct, callers = self.timings[func]
366 callers = callers.copy()
367 nc = 0
368 for func_caller in callers.keys():
369 nc = nc + callers[func_caller]
370 self.stats[func] = cc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000371
372
Tim Peters2344fae2001-01-15 00:50:52 +0000373 # The following two methods can be called by clients to use
374 # a profiler to profile a statement, given as a string.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000375
Tim Peters2344fae2001-01-15 00:50:52 +0000376 def run(self, cmd):
377 import __main__
378 dict = __main__.__dict__
379 return self.runctx(cmd, dict, dict)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000380
Tim Peters2344fae2001-01-15 00:50:52 +0000381 def runctx(self, cmd, globals, locals):
382 self.set_cmd(cmd)
383 sys.setprofile(self.dispatcher)
384 try:
385 exec cmd in globals, locals
386 finally:
387 sys.setprofile(None)
388 return self
Guido van Rossumb6775db1994-08-01 11:34:53 +0000389
Tim Peters2344fae2001-01-15 00:50:52 +0000390 # This method is more useful to profile a single function call.
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000391 def runcall(self, func, *args, **kw):
Tim Peters2344fae2001-01-15 00:50:52 +0000392 self.set_cmd(`func`)
393 sys.setprofile(self.dispatcher)
394 try:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000395 return apply(func, args, kw)
Tim Peters2344fae2001-01-15 00:50:52 +0000396 finally:
397 sys.setprofile(None)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000398
Tim Peters2344fae2001-01-15 00:50:52 +0000399
400 #******************************************************************
401 # The following calculates the overhead for using a profiler. The
402 # problem is that it takes a fair amount of time for the profiler
403 # to stop the stopwatch (from the time it receives an event).
404 # Similarly, there is a delay from the time that the profiler
405 # re-starts the stopwatch before the user's code really gets to
406 # continue. The following code tries to measure the difference on
407 # a per-event basis. The result can the be placed in the
408 # Profile.dispatch_event() routine for the given platform. Note
409 # that this difference is only significant if there are a lot of
410 # events, and relatively little user code per event. For example,
411 # code with small functions will typically benefit from having the
412 # profiler calibrated for the current platform. This *could* be
413 # done on the fly during init() time, but it is not worth the
414 # effort. Also note that if too large a value specified, then
415 # execution time on some functions will actually appear as a
416 # negative number. It is *normal* for some functions (with very
417 # low call counts) to have such negative stats, even if the
418 # calibration figure is "correct."
419 #
420 # One alternative to profile-time calibration adjustments (i.e.,
421 # adding in the magic little delta during each event) is to track
422 # more carefully the number of events (and cumulatively, the number
423 # of events during sub functions) that are seen. If this were
424 # done, then the arithmetic could be done after the fact (i.e., at
425 # display time). Currently, we track only call/return events.
426 # These values can be deduced by examining the callees and callers
427 # vectors for each functions. Hence we *can* almost correct the
428 # internal time figure at print time (note that we currently don't
429 # track exception event processing counts). Unfortunately, there
430 # is currently no similar information for cumulative sub-function
431 # time. It would not be hard to "get all this info" at profiler
432 # time. Specifically, we would have to extend the tuples to keep
433 # counts of this in each frame, and then extend the defs of timing
434 # tuples to include the significant two figures. I'm a bit fearful
435 # that this additional feature will slow the heavily optimized
436 # event/time ratio (i.e., the profiler would run slower, fur a very
437 # low "value added" feature.)
438 #
439 # Plugging in the calibration constant doesn't slow down the
440 # profiler very much, and the accuracy goes way up.
441 #**************************************************************
442
443 def calibrate(self, m):
444 # Modified by Tim Peters
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000445 get_time = self.get_time
Tim Peters2344fae2001-01-15 00:50:52 +0000446 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000447 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000448 while n:
449 self.simple()
450 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000451 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000452 my_simple = f - s
453 #print "Simple =", my_simple,
454
455 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000456 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000457 while n:
458 self.instrumented()
459 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000460 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000461 my_inst = f - s
462 # print "Instrumented =", my_inst
463 avg_cost = (my_inst - my_simple)/m
464 #print "Delta/call =", avg_cost, "(profiler fixup constant)"
465 return avg_cost
466
467 # simulate a program with no profiler activity
468 def simple(self):
469 a = 1
470 pass
471
472 # simulate a program with call/return event processing
473 def instrumented(self):
474 a = 1
475 self.profiler_simulation(a, a, a)
476
477 # simulate an event processing activity (from user's perspective)
478 def profiler_simulation(self, x, y, z):
479 t = self.timer()
480 ## t = t[0] + t[1]
481 self.ut = t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000482
483
484
Guido van Rossumb6775db1994-08-01 11:34:53 +0000485class OldProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000486 """A derived profiler that simulates the old style profile, providing
487 errant results on recursive functions. The reason for the usefulness of
488 this profiler is that it runs faster (i.e., less overhead). It still
489 creates all the caller stats, and is quite useful when there is *no*
490 recursion in the user's code.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000491
Tim Peters2344fae2001-01-15 00:50:52 +0000492 This code also shows how easy it is to create a modified profiler.
493 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000494
Tim Peters2344fae2001-01-15 00:50:52 +0000495 def trace_dispatch_exception(self, frame, t):
496 rt, rtt, rct, rfn, rframe, rcur = self.cur
497 if rcur and not rframe is frame:
498 return self.trace_dispatch_return(rframe, t)
499 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000500
Tim Peters2344fae2001-01-15 00:50:52 +0000501 def trace_dispatch_call(self, frame, t):
502 fn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000503
Tim Peters2344fae2001-01-15 00:50:52 +0000504 self.cur = (t, 0, 0, fn, frame, self.cur)
505 if self.timings.has_key(fn):
506 tt, ct, callers = self.timings[fn]
507 self.timings[fn] = tt, ct, callers
508 else:
509 self.timings[fn] = 0, 0, {}
510 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000511
Tim Peters2344fae2001-01-15 00:50:52 +0000512 def trace_dispatch_return(self, frame, t):
513 rt, rtt, rct, rfn, frame, rcur = self.cur
514 rtt = rtt + t
515 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000516
Tim Peters2344fae2001-01-15 00:50:52 +0000517 pt, ptt, pct, pfn, pframe, pcur = rcur
518 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
519
520 tt, ct, callers = self.timings[rfn]
521 if callers.has_key(pfn):
522 callers[pfn] = callers[pfn] + 1
523 else:
524 callers[pfn] = 1
525 self.timings[rfn] = tt+rtt, ct + sft, callers
526
527 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000528
529
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000530 dispatch = {
531 "call": trace_dispatch_call,
532 "exception": trace_dispatch_exception,
533 "return": trace_dispatch_return,
534 }
535
536
Tim Peters2344fae2001-01-15 00:50:52 +0000537 def snapshot_stats(self):
538 self.stats = {}
539 for func in self.timings.keys():
540 tt, ct, callers = self.timings[func]
541 callers = callers.copy()
542 nc = 0
543 for func_caller in callers.keys():
544 nc = nc + callers[func_caller]
545 self.stats[func] = nc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000546
Tim Peters2344fae2001-01-15 00:50:52 +0000547
Guido van Rossumb6775db1994-08-01 11:34:53 +0000548
Guido van Rossumb6775db1994-08-01 11:34:53 +0000549class HotProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000550 """The fastest derived profile example. It does not calculate
551 caller-callee relationships, and does not calculate cumulative
552 time under a function. It only calculates time spent in a
553 function, so it runs very quickly due to its very low overhead.
554 """
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000555
Tim Peters2344fae2001-01-15 00:50:52 +0000556 def trace_dispatch_exception(self, frame, t):
557 rt, rtt, rfn, rframe, rcur = self.cur
558 if rcur and not rframe is frame:
559 return self.trace_dispatch_return(rframe, t)
560 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000561
Tim Peters2344fae2001-01-15 00:50:52 +0000562 def trace_dispatch_call(self, frame, t):
563 self.cur = (t, 0, frame, self.cur)
564 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000565
Tim Peters2344fae2001-01-15 00:50:52 +0000566 def trace_dispatch_return(self, frame, t):
567 rt, rtt, frame, rcur = self.cur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000568
Tim Peters2344fae2001-01-15 00:50:52 +0000569 rfn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000570
Tim Peters2344fae2001-01-15 00:50:52 +0000571 pt, ptt, pframe, pcur = rcur
572 self.cur = pt, ptt+rt, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000573
Tim Peters2344fae2001-01-15 00:50:52 +0000574 if self.timings.has_key(rfn):
575 nc, tt = self.timings[rfn]
576 self.timings[rfn] = nc + 1, rt + rtt + tt
577 else:
578 self.timings[rfn] = 1, rt + rtt
Guido van Rossumb6775db1994-08-01 11:34:53 +0000579
Tim Peters2344fae2001-01-15 00:50:52 +0000580 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000581
582
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000583 dispatch = {
584 "call": trace_dispatch_call,
585 "exception": trace_dispatch_exception,
586 "return": trace_dispatch_return,
587 }
588
589
Tim Peters2344fae2001-01-15 00:50:52 +0000590 def snapshot_stats(self):
591 self.stats = {}
592 for func in self.timings.keys():
593 nc, tt = self.timings[func]
594 self.stats[func] = nc, nc, tt, 0, {}
Guido van Rossumb6775db1994-08-01 11:34:53 +0000595
Tim Peters2344fae2001-01-15 00:50:52 +0000596
Guido van Rossumb6775db1994-08-01 11:34:53 +0000597
598#****************************************************************************
599def Stats(*args):
Tim Peters2344fae2001-01-15 00:50:52 +0000600 print 'Report generating functions are in the "pstats" module\a'
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000601
602
603# When invoked as main program, invoke the profiler on a script
604if __name__ == '__main__':
Tim Peters2344fae2001-01-15 00:50:52 +0000605 if not sys.argv[1:]:
606 print "usage: profile.py scriptfile [arg] ..."
607 sys.exit(2)
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000608
Tim Peters2344fae2001-01-15 00:50:52 +0000609 filename = sys.argv[1] # Get script filename
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000610
Tim Peters2344fae2001-01-15 00:50:52 +0000611 del sys.argv[0] # Hide "profile.py" from argument list
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000612
Tim Peters2344fae2001-01-15 00:50:52 +0000613 # Insert script directory in front of module search path
614 sys.path.insert(0, os.path.dirname(filename))
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000615
Tim Peters2344fae2001-01-15 00:50:52 +0000616 run('execfile(' + `filename` + ')')