blob: 73aeff9fe86d29db84f099a8e2006d533a68cbc0 [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
Guido van Rossumf137f752001-10-04 00:58:24 +0000112 [3:] intact.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000113
Guido van Rossumf137f752001-10-04 00:58:24 +0000114 [0] = Time that needs to be charged to the parent frame's function.
115 It is used so that a function call will not have to access the
116 timing data for the parent frame.
117 [1] = Total time spent in this frame's function, excluding time in
118 subfunctions
119 [2] = Cumulative time spent in this frame's function, including time in
120 all subfunctions to this frame (but excluding this frame!).
121 [3] = Name of the function that corresponds to this frame.
122 [4] = Actual frame that we correspond to (used to sync exception handling)
123 [5] = Our parent 6-tuple (corresponds to frame.f_back)
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000124
Tim Peters2344fae2001-01-15 00:50:52 +0000125 Timing data for each function is stored as a 5-tuple in the dictionary
126 self.timings[]. The index is always the name stored in self.cur[4].
127 The following are the definitions of the members:
Guido van Rossumb6775db1994-08-01 11:34:53 +0000128
Tim Peters2344fae2001-01-15 00:50:52 +0000129 [0] = The number of times this function was called, not counting direct
130 or indirect recursion,
131 [1] = Number of times this function appears on the stack, minus one
132 [2] = Total time spent internal to this function
133 [3] = Cumulative time that this function was present on the stack. In
134 non-recursive functions, this is the total execution time from start
135 to finish of each invocation of a function, including time spent in
136 all subfunctions.
137 [5] = A dictionary indicating for each function name, the number of times
138 it was called by us.
139 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000140
Tim Peters2344fae2001-01-15 00:50:52 +0000141 def __init__(self, timer=None):
142 self.timings = {}
143 self.cur = None
144 self.cmd = ""
Guido van Rossumb6775db1994-08-01 11:34:53 +0000145
Tim Peters2344fae2001-01-15 00:50:52 +0000146 if not timer:
147 if os.name == 'mac':
Tim Peters2344fae2001-01-15 00:50:52 +0000148 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.
Guido van Rossume4deb952001-08-09 21:22:15 +0000179 import operator
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000180 def get_time_timer(timer=timer,
181 reduce=reduce, reducer=operator.add):
182 return reduce(reducer, timer(), 0)
183 self.get_time = get_time_timer
Tim Peters2344fae2001-01-15 00:50:52 +0000184 self.t = self.get_time()
185 self.simulate_call('profiler')
Guido van Rossumb6775db1994-08-01 11:34:53 +0000186
Tim Peters2344fae2001-01-15 00:50:52 +0000187 # Heavily optimized dispatch routine for os.times() timer
Guido van Rossumb6775db1994-08-01 11:34:53 +0000188
Tim Peters2344fae2001-01-15 00:50:52 +0000189 def trace_dispatch(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000190 timer = self.timer
191 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000192 t = t[0] + t[1] - self.t # No Calibration constant
193 # t = t[0] + t[1] - self.t - .00053 # Calibration constant
194
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000195 if self.dispatch[event](self, frame,t):
196 t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000197 self.t = t[0] + t[1]
198 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000199 r = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000200 self.t = r[0] + r[1] - t # put back unrecorded delta
201 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000202
203
204
Tim Peters2344fae2001-01-15 00:50:52 +0000205 # Dispatch routine for best timer program (return = scalar integer)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000206
Tim Peters2344fae2001-01-15 00:50:52 +0000207 def trace_dispatch_i(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000208 timer = self.timer
209 t = timer() - self.t # - 1 # Integer calibration constant
210 if self.dispatch[event](self, frame,t):
211 self.t = timer()
Tim Peters2344fae2001-01-15 00:50:52 +0000212 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000213 self.t = timer() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000214 return
Guido van Rossumcbf3dd51997-10-08 15:23:02 +0000215
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000216 # Dispatch routine for macintosh (timer returns time in ticks of
217 # 1/60th second)
Tim Peters2344fae2001-01-15 00:50:52 +0000218
219 def trace_dispatch_mac(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000220 timer = self.timer
221 t = timer()/60.0 - self.t # - 1 # Integer calibration constant
222 if self.dispatch[event](self, frame,t):
223 self.t = timer()/60.0
Tim Peters2344fae2001-01-15 00:50:52 +0000224 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000225 self.t = timer()/60.0 - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000226 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000227
228
Tim Peters2344fae2001-01-15 00:50:52 +0000229 # SLOW generic dispatch routine for timer returning lists of numbers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000230
Tim Peters2344fae2001-01-15 00:50:52 +0000231 def trace_dispatch_l(self, frame, event, arg):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000232 get_time = self.get_time
233 t = get_time() - self.t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000234
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000235 if self.dispatch[event](self, frame,t):
236 self.t = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000237 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000238 self.t = get_time() - t # put back unrecorded delta
Tim Peters2344fae2001-01-15 00:50:52 +0000239 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000240
241
Tim Peters2344fae2001-01-15 00:50:52 +0000242 def trace_dispatch_exception(self, frame, t):
243 rt, rtt, rct, rfn, rframe, rcur = self.cur
Fred Drakea0bc9992001-10-03 21:12:32 +0000244 if (rframe is not frame) and rcur:
Tim Peters2344fae2001-01-15 00:50:52 +0000245 return self.trace_dispatch_return(rframe, t)
Guido van Rossumf137f752001-10-04 00:58:24 +0000246 self.cur = rt, rtt+t, rct, rfn, rframe, rcur
247 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000248
249
Tim Peters2344fae2001-01-15 00:50:52 +0000250 def trace_dispatch_call(self, frame, t):
Guido van Rossumf137f752001-10-04 00:58:24 +0000251 if self.cur and frame.f_back is not self.cur[4]:
252 rt, rtt, rct, rfn, rframe, rcur = self.cur
253 if not isinstance(rframe, Profile.fake_frame):
254 if rframe.f_back is not frame.f_back:
255 print rframe, rframe.f_back
256 print frame, frame.f_back
257 raise "Bad call", self.cur[3]
258 self.trace_dispatch_return(rframe, 0)
259 if self.cur and frame.f_back is not self.cur[4]:
260 raise "Bad call[2]", self.cur[3]
Tim Peters2344fae2001-01-15 00:50:52 +0000261 fcode = frame.f_code
262 fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
263 self.cur = (t, 0, 0, fn, frame, self.cur)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000264 timings = self.timings
265 if timings.has_key(fn):
266 cc, ns, tt, ct, callers = timings[fn]
267 timings[fn] = cc, ns + 1, tt, ct, callers
Tim Peters2344fae2001-01-15 00:50:52 +0000268 else:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000269 timings[fn] = 0, 0, 0, 0, {}
Tim Peters2344fae2001-01-15 00:50:52 +0000270 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000271
Tim Peters2344fae2001-01-15 00:50:52 +0000272 def trace_dispatch_return(self, frame, t):
Guido van Rossumf137f752001-10-04 00:58:24 +0000273 if frame is not self.cur[4]:
274 if frame is self.cur[4].f_back:
275 self.trace_dispatch_return(self.cur[4], 0)
276 else:
277 raise "Bad return", self.cur[3]
Guido van Rossumb6775db1994-08-01 11:34:53 +0000278
Tim Peters2344fae2001-01-15 00:50:52 +0000279 # Prefix "r" means part of the Returning or exiting frame
280 # Prefix "p" means part of the Previous or older frame
Guido van Rossumb6775db1994-08-01 11:34:53 +0000281
Tim Peters2344fae2001-01-15 00:50:52 +0000282 rt, rtt, rct, rfn, frame, rcur = self.cur
283 rtt = rtt + t
284 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000285
Tim Peters2344fae2001-01-15 00:50:52 +0000286 pt, ptt, pct, pfn, pframe, pcur = rcur
287 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000288
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000289 timings = self.timings
290 cc, ns, tt, ct, callers = timings[rfn]
Tim Peters2344fae2001-01-15 00:50:52 +0000291 if not ns:
292 ct = ct + sft
293 cc = cc + 1
294 if callers.has_key(pfn):
295 callers[pfn] = callers[pfn] + 1 # hack: gather more
296 # stats such as the amount of time added to ct courtesy
297 # of this specific call, and the contribution to cc
298 # courtesy of this call.
299 else:
300 callers[pfn] = 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000301 timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000302
Tim Peters2344fae2001-01-15 00:50:52 +0000303 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000304
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000305
306 dispatch = {
307 "call": trace_dispatch_call,
308 "exception": trace_dispatch_exception,
309 "return": trace_dispatch_return,
310 }
311
312
Tim Peters2344fae2001-01-15 00:50:52 +0000313 # The next few function play with self.cmd. By carefully preloading
314 # our parallel stack, we can force the profiled result to include
315 # an arbitrary string as the name of the calling function.
316 # We use self.cmd as that string, and the resulting stats look
317 # very nice :-).
Guido van Rossumb6775db1994-08-01 11:34:53 +0000318
Tim Peters2344fae2001-01-15 00:50:52 +0000319 def set_cmd(self, cmd):
Guido van Rossumf137f752001-10-04 00:58:24 +0000320 if self.cur[5]: return # already set
Tim Peters2344fae2001-01-15 00:50:52 +0000321 self.cmd = cmd
322 self.simulate_call(cmd)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000323
Tim Peters2344fae2001-01-15 00:50:52 +0000324 class fake_code:
325 def __init__(self, filename, line, name):
326 self.co_filename = filename
327 self.co_line = line
328 self.co_name = name
329 self.co_firstlineno = 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000330
Tim Peters2344fae2001-01-15 00:50:52 +0000331 def __repr__(self):
332 return repr((self.co_filename, self.co_line, self.co_name))
Guido van Rossumb6775db1994-08-01 11:34:53 +0000333
Tim Peters2344fae2001-01-15 00:50:52 +0000334 class fake_frame:
335 def __init__(self, code, prior):
336 self.f_code = code
337 self.f_back = prior
Guido van Rossumb6775db1994-08-01 11:34:53 +0000338
Tim Peters2344fae2001-01-15 00:50:52 +0000339 def simulate_call(self, name):
340 code = self.fake_code('profile', 0, name)
341 if self.cur:
Guido van Rossumf137f752001-10-04 00:58:24 +0000342 pframe = self.cur[4]
Tim Peters2344fae2001-01-15 00:50:52 +0000343 else:
344 pframe = None
345 frame = self.fake_frame(code, pframe)
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000346 a = self.dispatch['call'](self, frame, 0)
Tim Peters2344fae2001-01-15 00:50:52 +0000347 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000348
Tim Peters2344fae2001-01-15 00:50:52 +0000349 # collect stats from pending stack, including getting final
350 # timings for self.cmd frame.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000351
Tim Peters2344fae2001-01-15 00:50:52 +0000352 def simulate_cmd_complete(self):
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000353 get_time = self.get_time
354 t = get_time() - self.t
Guido van Rossumf137f752001-10-04 00:58:24 +0000355 while self.cur[5]:
Tim Peters2344fae2001-01-15 00:50:52 +0000356 # We *can* cause assertion errors here if
357 # dispatch_trace_return checks for a frame match!
Guido van Rossumf137f752001-10-04 00:58:24 +0000358 a = self.dispatch['return'](self, self.cur[4], t)
Tim Peters2344fae2001-01-15 00:50:52 +0000359 t = 0
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000360 self.t = get_time() - t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000361
362
Tim Peters2344fae2001-01-15 00:50:52 +0000363 def print_stats(self):
364 import pstats
365 pstats.Stats(self).strip_dirs().sort_stats(-1). \
366 print_stats()
Guido van Rossumb6775db1994-08-01 11:34:53 +0000367
Tim Peters2344fae2001-01-15 00:50:52 +0000368 def dump_stats(self, file):
369 f = open(file, 'wb')
370 self.create_stats()
371 marshal.dump(self.stats, f)
372 f.close()
373
374 def create_stats(self):
375 self.simulate_cmd_complete()
376 self.snapshot_stats()
377
378 def snapshot_stats(self):
379 self.stats = {}
380 for func in self.timings.keys():
381 cc, ns, tt, ct, callers = self.timings[func]
382 callers = callers.copy()
383 nc = 0
384 for func_caller in callers.keys():
385 nc = nc + callers[func_caller]
386 self.stats[func] = cc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000387
388
Tim Peters2344fae2001-01-15 00:50:52 +0000389 # The following two methods can be called by clients to use
390 # a profiler to profile a statement, given as a string.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000391
Tim Peters2344fae2001-01-15 00:50:52 +0000392 def run(self, cmd):
393 import __main__
394 dict = __main__.__dict__
395 return self.runctx(cmd, dict, dict)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000396
Tim Peters2344fae2001-01-15 00:50:52 +0000397 def runctx(self, cmd, globals, locals):
398 self.set_cmd(cmd)
399 sys.setprofile(self.dispatcher)
400 try:
401 exec cmd in globals, locals
402 finally:
403 sys.setprofile(None)
404 return self
Guido van Rossumb6775db1994-08-01 11:34:53 +0000405
Tim Peters2344fae2001-01-15 00:50:52 +0000406 # This method is more useful to profile a single function call.
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000407 def runcall(self, func, *args, **kw):
Tim Peters2344fae2001-01-15 00:50:52 +0000408 self.set_cmd(`func`)
409 sys.setprofile(self.dispatcher)
410 try:
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000411 return apply(func, args, kw)
Tim Peters2344fae2001-01-15 00:50:52 +0000412 finally:
413 sys.setprofile(None)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000414
Tim Peters2344fae2001-01-15 00:50:52 +0000415
416 #******************************************************************
417 # The following calculates the overhead for using a profiler. The
418 # problem is that it takes a fair amount of time for the profiler
419 # to stop the stopwatch (from the time it receives an event).
420 # Similarly, there is a delay from the time that the profiler
421 # re-starts the stopwatch before the user's code really gets to
422 # continue. The following code tries to measure the difference on
423 # a per-event basis. The result can the be placed in the
424 # Profile.dispatch_event() routine for the given platform. Note
425 # that this difference is only significant if there are a lot of
426 # events, and relatively little user code per event. For example,
427 # code with small functions will typically benefit from having the
428 # profiler calibrated for the current platform. This *could* be
429 # done on the fly during init() time, but it is not worth the
430 # effort. Also note that if too large a value specified, then
431 # execution time on some functions will actually appear as a
432 # negative number. It is *normal* for some functions (with very
433 # low call counts) to have such negative stats, even if the
434 # calibration figure is "correct."
435 #
436 # One alternative to profile-time calibration adjustments (i.e.,
437 # adding in the magic little delta during each event) is to track
438 # more carefully the number of events (and cumulatively, the number
439 # of events during sub functions) that are seen. If this were
440 # done, then the arithmetic could be done after the fact (i.e., at
441 # display time). Currently, we track only call/return events.
442 # These values can be deduced by examining the callees and callers
443 # vectors for each functions. Hence we *can* almost correct the
444 # internal time figure at print time (note that we currently don't
445 # track exception event processing counts). Unfortunately, there
446 # is currently no similar information for cumulative sub-function
447 # time. It would not be hard to "get all this info" at profiler
448 # time. Specifically, we would have to extend the tuples to keep
449 # counts of this in each frame, and then extend the defs of timing
450 # tuples to include the significant two figures. I'm a bit fearful
451 # that this additional feature will slow the heavily optimized
452 # event/time ratio (i.e., the profiler would run slower, fur a very
453 # low "value added" feature.)
454 #
455 # Plugging in the calibration constant doesn't slow down the
456 # profiler very much, and the accuracy goes way up.
457 #**************************************************************
458
459 def calibrate(self, m):
460 # Modified by Tim Peters
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000461 get_time = self.get_time
Tim Peters2344fae2001-01-15 00:50:52 +0000462 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000463 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000464 while n:
465 self.simple()
466 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000467 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000468 my_simple = f - s
469 #print "Simple =", my_simple,
470
471 n = m
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000472 s = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000473 while n:
474 self.instrumented()
475 n = n - 1
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000476 f = get_time()
Tim Peters2344fae2001-01-15 00:50:52 +0000477 my_inst = f - s
478 # print "Instrumented =", my_inst
479 avg_cost = (my_inst - my_simple)/m
480 #print "Delta/call =", avg_cost, "(profiler fixup constant)"
481 return avg_cost
482
483 # simulate a program with no profiler activity
484 def simple(self):
485 a = 1
486 pass
487
488 # simulate a program with call/return event processing
489 def instrumented(self):
490 a = 1
491 self.profiler_simulation(a, a, a)
492
493 # simulate an event processing activity (from user's perspective)
494 def profiler_simulation(self, x, y, z):
495 t = self.timer()
496 ## t = t[0] + t[1]
497 self.ut = t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000498
499
500
Guido van Rossumb6775db1994-08-01 11:34:53 +0000501class OldProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000502 """A derived profiler that simulates the old style profile, providing
503 errant results on recursive functions. The reason for the usefulness of
504 this profiler is that it runs faster (i.e., less overhead). It still
505 creates all the caller stats, and is quite useful when there is *no*
506 recursion in the user's code.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000507
Tim Peters2344fae2001-01-15 00:50:52 +0000508 This code also shows how easy it is to create a modified profiler.
509 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000510
Tim Peters2344fae2001-01-15 00:50:52 +0000511 def trace_dispatch_exception(self, frame, t):
512 rt, rtt, rct, rfn, rframe, rcur = self.cur
513 if rcur and not rframe is frame:
514 return self.trace_dispatch_return(rframe, t)
515 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000516
Tim Peters2344fae2001-01-15 00:50:52 +0000517 def trace_dispatch_call(self, frame, t):
518 fn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000519
Tim Peters2344fae2001-01-15 00:50:52 +0000520 self.cur = (t, 0, 0, fn, frame, self.cur)
521 if self.timings.has_key(fn):
522 tt, ct, callers = self.timings[fn]
523 self.timings[fn] = tt, ct, callers
524 else:
525 self.timings[fn] = 0, 0, {}
526 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000527
Tim Peters2344fae2001-01-15 00:50:52 +0000528 def trace_dispatch_return(self, frame, t):
529 rt, rtt, rct, rfn, frame, rcur = self.cur
530 rtt = rtt + t
531 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000532
Tim Peters2344fae2001-01-15 00:50:52 +0000533 pt, ptt, pct, pfn, pframe, pcur = rcur
534 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
535
536 tt, ct, callers = self.timings[rfn]
537 if callers.has_key(pfn):
538 callers[pfn] = callers[pfn] + 1
539 else:
540 callers[pfn] = 1
541 self.timings[rfn] = tt+rtt, ct + sft, callers
542
543 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000544
545
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000546 dispatch = {
547 "call": trace_dispatch_call,
548 "exception": trace_dispatch_exception,
549 "return": trace_dispatch_return,
550 }
551
552
Tim Peters2344fae2001-01-15 00:50:52 +0000553 def snapshot_stats(self):
554 self.stats = {}
555 for func in self.timings.keys():
556 tt, ct, callers = self.timings[func]
557 callers = callers.copy()
558 nc = 0
559 for func_caller in callers.keys():
560 nc = nc + callers[func_caller]
561 self.stats[func] = nc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000562
Tim Peters2344fae2001-01-15 00:50:52 +0000563
Guido van Rossumb6775db1994-08-01 11:34:53 +0000564
Guido van Rossumb6775db1994-08-01 11:34:53 +0000565class HotProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000566 """The fastest derived profile example. It does not calculate
567 caller-callee relationships, and does not calculate cumulative
568 time under a function. It only calculates time spent in a
569 function, so it runs very quickly due to its very low overhead.
570 """
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000571
Tim Peters2344fae2001-01-15 00:50:52 +0000572 def trace_dispatch_exception(self, frame, t):
573 rt, rtt, rfn, rframe, rcur = self.cur
574 if rcur and not rframe is frame:
575 return self.trace_dispatch_return(rframe, t)
576 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000577
Tim Peters2344fae2001-01-15 00:50:52 +0000578 def trace_dispatch_call(self, frame, t):
579 self.cur = (t, 0, frame, self.cur)
580 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000581
Tim Peters2344fae2001-01-15 00:50:52 +0000582 def trace_dispatch_return(self, frame, t):
583 rt, rtt, frame, rcur = self.cur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000584
Tim Peters2344fae2001-01-15 00:50:52 +0000585 rfn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000586
Tim Peters2344fae2001-01-15 00:50:52 +0000587 pt, ptt, pframe, pcur = rcur
588 self.cur = pt, ptt+rt, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000589
Tim Peters2344fae2001-01-15 00:50:52 +0000590 if self.timings.has_key(rfn):
591 nc, tt = self.timings[rfn]
592 self.timings[rfn] = nc + 1, rt + rtt + tt
593 else:
594 self.timings[rfn] = 1, rt + rtt
Guido van Rossumb6775db1994-08-01 11:34:53 +0000595
Tim Peters2344fae2001-01-15 00:50:52 +0000596 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000597
598
Fred Drakeedb5ffb2001-06-08 04:25:24 +0000599 dispatch = {
600 "call": trace_dispatch_call,
601 "exception": trace_dispatch_exception,
602 "return": trace_dispatch_return,
603 }
604
605
Tim Peters2344fae2001-01-15 00:50:52 +0000606 def snapshot_stats(self):
607 self.stats = {}
608 for func in self.timings.keys():
609 nc, tt = self.timings[func]
610 self.stats[func] = nc, nc, tt, 0, {}
Guido van Rossumb6775db1994-08-01 11:34:53 +0000611
Tim Peters2344fae2001-01-15 00:50:52 +0000612
Guido van Rossumb6775db1994-08-01 11:34:53 +0000613
614#****************************************************************************
615def Stats(*args):
Tim Peters2344fae2001-01-15 00:50:52 +0000616 print 'Report generating functions are in the "pstats" module\a'
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000617
618
619# When invoked as main program, invoke the profiler on a script
620if __name__ == '__main__':
Tim Peters2344fae2001-01-15 00:50:52 +0000621 if not sys.argv[1:]:
622 print "usage: profile.py scriptfile [arg] ..."
623 sys.exit(2)
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000624
Tim Peters2344fae2001-01-15 00:50:52 +0000625 filename = sys.argv[1] # Get script filename
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000626
Tim Peters2344fae2001-01-15 00:50:52 +0000627 del sys.argv[0] # Hide "profile.py" from argument list
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000628
Tim Peters2344fae2001-01-15 00:50:52 +0000629 # Insert script directory in front of module search path
630 sys.path.insert(0, os.path.dirname(filename))
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000631
Tim Peters2344fae2001-01-15 00:50:52 +0000632 run('execfile(' + `filename` + ')')