blob: afc47eb347ae5adba6fe6e5bb359d2b1148df5fd [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
Guido van Rossum4e160981992-09-02 20:43:20 +000058
59# simplified user interface
60def run(statement, *args):
Tim Peters2344fae2001-01-15 00:50:52 +000061 prof = Profile()
62 try:
63 prof = prof.run(statement)
64 except SystemExit:
65 pass
66 if args:
67 prof.dump_stats(args[0])
68 else:
69 return prof.print_stats()
Guido van Rossume61fa0a1993-10-22 13:56:35 +000070
71# print help
72def help():
Tim Peters2344fae2001-01-15 00:50:52 +000073 for dirname in sys.path:
74 fullname = os.path.join(dirname, 'profile.doc')
75 if os.path.exists(fullname):
76 sts = os.system('${PAGER-more} '+fullname)
77 if sts: print '*** Pager exit status:', sts
78 break
79 else:
80 print 'Sorry, can\'t find the help file "profile.doc"',
81 print 'along the Python search path'
Guido van Rossumb6775db1994-08-01 11:34:53 +000082
83
Guido van Rossumb6775db1994-08-01 11:34:53 +000084class Profile:
Tim Peters2344fae2001-01-15 00:50:52 +000085 """Profiler class.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000086
Tim Peters2344fae2001-01-15 00:50:52 +000087 self.cur is always a tuple. Each such tuple corresponds to a stack
88 frame that is currently active (self.cur[-2]). The following are the
89 definitions of its members. We use this external "parallel stack" to
90 avoid contaminating the program that we are profiling. (old profiler
91 used to write into the frames local dictionary!!) Derived classes
92 can change the definition of some entries, as long as they leave
93 [-2:] intact.
Guido van Rossum54f22ed2000-02-04 15:10:34 +000094
Tim Peters2344fae2001-01-15 00:50:52 +000095 [ 0] = Time that needs to be charged to the parent frame's function.
96 It is used so that a function call will not have to access the
97 timing data for the parent frame.
98 [ 1] = Total time spent in this frame's function, excluding time in
99 subfunctions
100 [ 2] = Cumulative time spent in this frame's function, including time in
101 all subfunctions to this frame.
102 [-3] = Name of the function that corresponds to this frame.
103 [-2] = Actual frame that we correspond to (used to sync exception handling)
104 [-1] = Our parent 6-tuple (corresponds to frame.f_back)
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000105
Tim Peters2344fae2001-01-15 00:50:52 +0000106 Timing data for each function is stored as a 5-tuple in the dictionary
107 self.timings[]. The index is always the name stored in self.cur[4].
108 The following are the definitions of the members:
Guido van Rossumb6775db1994-08-01 11:34:53 +0000109
Tim Peters2344fae2001-01-15 00:50:52 +0000110 [0] = The number of times this function was called, not counting direct
111 or indirect recursion,
112 [1] = Number of times this function appears on the stack, minus one
113 [2] = Total time spent internal to this function
114 [3] = Cumulative time that this function was present on the stack. In
115 non-recursive functions, this is the total execution time from start
116 to finish of each invocation of a function, including time spent in
117 all subfunctions.
118 [5] = A dictionary indicating for each function name, the number of times
119 it was called by us.
120 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000121
Tim Peters2344fae2001-01-15 00:50:52 +0000122 def __init__(self, timer=None):
123 self.timings = {}
124 self.cur = None
125 self.cmd = ""
Guido van Rossumb6775db1994-08-01 11:34:53 +0000126
Tim Peters2344fae2001-01-15 00:50:52 +0000127 self.dispatch = { \
128 'call' : self.trace_dispatch_call, \
129 'return' : self.trace_dispatch_return, \
130 'exception': self.trace_dispatch_exception, \
131 }
132
133 if not timer:
134 if os.name == 'mac':
135 import MacOS
136 self.timer = MacOS.GetTicks
137 self.dispatcher = self.trace_dispatch_mac
138 self.get_time = self.get_time_mac
139 elif hasattr(time, 'clock'):
140 self.timer = time.clock
141 self.dispatcher = self.trace_dispatch_i
142 elif hasattr(os, 'times'):
143 self.timer = os.times
144 self.dispatcher = self.trace_dispatch
145 else:
146 self.timer = time.time
147 self.dispatcher = self.trace_dispatch_i
148 else:
149 self.timer = timer
150 t = self.timer() # test out timer function
151 try:
152 if len(t) == 2:
153 self.dispatcher = self.trace_dispatch
154 else:
155 self.dispatcher = self.trace_dispatch_l
156 except TypeError:
157 self.dispatcher = self.trace_dispatch_i
158 self.t = self.get_time()
159 self.simulate_call('profiler')
Guido van Rossumb6775db1994-08-01 11:34:53 +0000160
161
Tim Peters2344fae2001-01-15 00:50:52 +0000162 def get_time(self): # slow simulation of method to acquire time
163 t = self.timer()
164 if type(t) == type(()) or type(t) == type([]):
165 t = reduce(lambda x,y: x+y, t, 0)
166 return t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000167
Tim Peters2344fae2001-01-15 00:50:52 +0000168 def get_time_mac(self):
169 return self.timer()/60.0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000170
Tim Peters2344fae2001-01-15 00:50:52 +0000171 # Heavily optimized dispatch routine for os.times() timer
Guido van Rossumb6775db1994-08-01 11:34:53 +0000172
Tim Peters2344fae2001-01-15 00:50:52 +0000173 def trace_dispatch(self, frame, event, arg):
174 t = self.timer()
175 t = t[0] + t[1] - self.t # No Calibration constant
176 # t = t[0] + t[1] - self.t - .00053 # Calibration constant
177
178 if self.dispatch[event](frame,t):
179 t = self.timer()
180 self.t = t[0] + t[1]
181 else:
182 r = self.timer()
183 self.t = r[0] + r[1] - t # put back unrecorded delta
184 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000185
186
187
Tim Peters2344fae2001-01-15 00:50:52 +0000188 # Dispatch routine for best timer program (return = scalar integer)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000189
Tim Peters2344fae2001-01-15 00:50:52 +0000190 def trace_dispatch_i(self, frame, event, arg):
191 t = self.timer() - self.t # - 1 # Integer calibration constant
192 if self.dispatch[event](frame,t):
193 self.t = self.timer()
194 else:
195 self.t = self.timer() - t # put back unrecorded delta
196 return
Guido van Rossumcbf3dd51997-10-08 15:23:02 +0000197
Tim Peters2344fae2001-01-15 00:50:52 +0000198 # Dispatch routine for macintosh (timer returns time in ticks of 1/60th second)
199
200 def trace_dispatch_mac(self, frame, event, arg):
201 t = self.timer()/60.0 - self.t # - 1 # Integer calibration constant
202 if self.dispatch[event](frame,t):
203 self.t = self.timer()/60.0
204 else:
205 self.t = self.timer()/60.0 - t # put back unrecorded delta
206 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000207
208
Tim Peters2344fae2001-01-15 00:50:52 +0000209 # SLOW generic dispatch routine for timer returning lists of numbers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000210
Tim Peters2344fae2001-01-15 00:50:52 +0000211 def trace_dispatch_l(self, frame, event, arg):
212 t = self.get_time() - self.t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000213
Tim Peters2344fae2001-01-15 00:50:52 +0000214 if self.dispatch[event](frame,t):
215 self.t = self.get_time()
216 else:
217 self.t = self.get_time()-t # put back unrecorded delta
218 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000219
220
Tim Peters2344fae2001-01-15 00:50:52 +0000221 def trace_dispatch_exception(self, frame, t):
222 rt, rtt, rct, rfn, rframe, rcur = self.cur
223 if (not rframe is frame) and rcur:
224 return self.trace_dispatch_return(rframe, t)
225 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000226
227
Tim Peters2344fae2001-01-15 00:50:52 +0000228 def trace_dispatch_call(self, frame, t):
229 fcode = frame.f_code
230 fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
231 self.cur = (t, 0, 0, fn, frame, self.cur)
232 if self.timings.has_key(fn):
233 cc, ns, tt, ct, callers = self.timings[fn]
234 self.timings[fn] = cc, ns + 1, tt, ct, callers
235 else:
236 self.timings[fn] = 0, 0, 0, 0, {}
237 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000238
Tim Peters2344fae2001-01-15 00:50:52 +0000239 def trace_dispatch_return(self, frame, t):
240 # if not frame is self.cur[-2]: raise "Bad return", self.cur[3]
Guido van Rossumb6775db1994-08-01 11:34:53 +0000241
Tim Peters2344fae2001-01-15 00:50:52 +0000242 # Prefix "r" means part of the Returning or exiting frame
243 # Prefix "p" means part of the Previous or older frame
Guido van Rossumb6775db1994-08-01 11:34:53 +0000244
Tim Peters2344fae2001-01-15 00:50:52 +0000245 rt, rtt, rct, rfn, frame, rcur = self.cur
246 rtt = rtt + t
247 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000248
Tim Peters2344fae2001-01-15 00:50:52 +0000249 pt, ptt, pct, pfn, pframe, pcur = rcur
250 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000251
Tim Peters2344fae2001-01-15 00:50:52 +0000252 cc, ns, tt, ct, callers = self.timings[rfn]
253 if not ns:
254 ct = ct + sft
255 cc = cc + 1
256 if callers.has_key(pfn):
257 callers[pfn] = callers[pfn] + 1 # hack: gather more
258 # stats such as the amount of time added to ct courtesy
259 # of this specific call, and the contribution to cc
260 # courtesy of this call.
261 else:
262 callers[pfn] = 1
263 self.timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000264
Tim Peters2344fae2001-01-15 00:50:52 +0000265 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000266
Tim Peters2344fae2001-01-15 00:50:52 +0000267 # The next few function play with self.cmd. By carefully preloading
268 # our parallel stack, we can force the profiled result to include
269 # an arbitrary string as the name of the calling function.
270 # We use self.cmd as that string, and the resulting stats look
271 # very nice :-).
Guido van Rossumb6775db1994-08-01 11:34:53 +0000272
Tim Peters2344fae2001-01-15 00:50:52 +0000273 def set_cmd(self, cmd):
274 if self.cur[-1]: return # already set
275 self.cmd = cmd
276 self.simulate_call(cmd)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000277
Tim Peters2344fae2001-01-15 00:50:52 +0000278 class fake_code:
279 def __init__(self, filename, line, name):
280 self.co_filename = filename
281 self.co_line = line
282 self.co_name = name
283 self.co_firstlineno = 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000284
Tim Peters2344fae2001-01-15 00:50:52 +0000285 def __repr__(self):
286 return repr((self.co_filename, self.co_line, self.co_name))
Guido van Rossumb6775db1994-08-01 11:34:53 +0000287
Tim Peters2344fae2001-01-15 00:50:52 +0000288 class fake_frame:
289 def __init__(self, code, prior):
290 self.f_code = code
291 self.f_back = prior
Guido van Rossumb6775db1994-08-01 11:34:53 +0000292
Tim Peters2344fae2001-01-15 00:50:52 +0000293 def simulate_call(self, name):
294 code = self.fake_code('profile', 0, name)
295 if self.cur:
296 pframe = self.cur[-2]
297 else:
298 pframe = None
299 frame = self.fake_frame(code, pframe)
300 a = self.dispatch['call'](frame, 0)
301 return
Guido van Rossumb6775db1994-08-01 11:34:53 +0000302
Tim Peters2344fae2001-01-15 00:50:52 +0000303 # collect stats from pending stack, including getting final
304 # timings for self.cmd frame.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000305
Tim Peters2344fae2001-01-15 00:50:52 +0000306 def simulate_cmd_complete(self):
307 t = self.get_time() - self.t
308 while self.cur[-1]:
309 # We *can* cause assertion errors here if
310 # dispatch_trace_return checks for a frame match!
311 a = self.dispatch['return'](self.cur[-2], t)
312 t = 0
313 self.t = self.get_time() - t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000314
315
Tim Peters2344fae2001-01-15 00:50:52 +0000316 def print_stats(self):
317 import pstats
318 pstats.Stats(self).strip_dirs().sort_stats(-1). \
319 print_stats()
Guido van Rossumb6775db1994-08-01 11:34:53 +0000320
Tim Peters2344fae2001-01-15 00:50:52 +0000321 def dump_stats(self, file):
322 f = open(file, 'wb')
323 self.create_stats()
324 marshal.dump(self.stats, f)
325 f.close()
326
327 def create_stats(self):
328 self.simulate_cmd_complete()
329 self.snapshot_stats()
330
331 def snapshot_stats(self):
332 self.stats = {}
333 for func in self.timings.keys():
334 cc, ns, tt, ct, callers = self.timings[func]
335 callers = callers.copy()
336 nc = 0
337 for func_caller in callers.keys():
338 nc = nc + callers[func_caller]
339 self.stats[func] = cc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000340
341
Tim Peters2344fae2001-01-15 00:50:52 +0000342 # The following two methods can be called by clients to use
343 # a profiler to profile a statement, given as a string.
Guido van Rossumb6775db1994-08-01 11:34:53 +0000344
Tim Peters2344fae2001-01-15 00:50:52 +0000345 def run(self, cmd):
346 import __main__
347 dict = __main__.__dict__
348 return self.runctx(cmd, dict, dict)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000349
Tim Peters2344fae2001-01-15 00:50:52 +0000350 def runctx(self, cmd, globals, locals):
351 self.set_cmd(cmd)
352 sys.setprofile(self.dispatcher)
353 try:
354 exec cmd in globals, locals
355 finally:
356 sys.setprofile(None)
357 return self
Guido van Rossumb6775db1994-08-01 11:34:53 +0000358
Tim Peters2344fae2001-01-15 00:50:52 +0000359 # This method is more useful to profile a single function call.
360 def runcall(self, func, *args):
361 self.set_cmd(`func`)
362 sys.setprofile(self.dispatcher)
363 try:
364 return apply(func, args)
365 finally:
366 sys.setprofile(None)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000367
Tim Peters2344fae2001-01-15 00:50:52 +0000368
369 #******************************************************************
370 # The following calculates the overhead for using a profiler. The
371 # problem is that it takes a fair amount of time for the profiler
372 # to stop the stopwatch (from the time it receives an event).
373 # Similarly, there is a delay from the time that the profiler
374 # re-starts the stopwatch before the user's code really gets to
375 # continue. The following code tries to measure the difference on
376 # a per-event basis. The result can the be placed in the
377 # Profile.dispatch_event() routine for the given platform. Note
378 # that this difference is only significant if there are a lot of
379 # events, and relatively little user code per event. For example,
380 # code with small functions will typically benefit from having the
381 # profiler calibrated for the current platform. This *could* be
382 # done on the fly during init() time, but it is not worth the
383 # effort. Also note that if too large a value specified, then
384 # execution time on some functions will actually appear as a
385 # negative number. It is *normal* for some functions (with very
386 # low call counts) to have such negative stats, even if the
387 # calibration figure is "correct."
388 #
389 # One alternative to profile-time calibration adjustments (i.e.,
390 # adding in the magic little delta during each event) is to track
391 # more carefully the number of events (and cumulatively, the number
392 # of events during sub functions) that are seen. If this were
393 # done, then the arithmetic could be done after the fact (i.e., at
394 # display time). Currently, we track only call/return events.
395 # These values can be deduced by examining the callees and callers
396 # vectors for each functions. Hence we *can* almost correct the
397 # internal time figure at print time (note that we currently don't
398 # track exception event processing counts). Unfortunately, there
399 # is currently no similar information for cumulative sub-function
400 # time. It would not be hard to "get all this info" at profiler
401 # time. Specifically, we would have to extend the tuples to keep
402 # counts of this in each frame, and then extend the defs of timing
403 # tuples to include the significant two figures. I'm a bit fearful
404 # that this additional feature will slow the heavily optimized
405 # event/time ratio (i.e., the profiler would run slower, fur a very
406 # low "value added" feature.)
407 #
408 # Plugging in the calibration constant doesn't slow down the
409 # profiler very much, and the accuracy goes way up.
410 #**************************************************************
411
412 def calibrate(self, m):
413 # Modified by Tim Peters
414 n = m
415 s = self.get_time()
416 while n:
417 self.simple()
418 n = n - 1
419 f = self.get_time()
420 my_simple = f - s
421 #print "Simple =", my_simple,
422
423 n = m
424 s = self.get_time()
425 while n:
426 self.instrumented()
427 n = n - 1
428 f = self.get_time()
429 my_inst = f - s
430 # print "Instrumented =", my_inst
431 avg_cost = (my_inst - my_simple)/m
432 #print "Delta/call =", avg_cost, "(profiler fixup constant)"
433 return avg_cost
434
435 # simulate a program with no profiler activity
436 def simple(self):
437 a = 1
438 pass
439
440 # simulate a program with call/return event processing
441 def instrumented(self):
442 a = 1
443 self.profiler_simulation(a, a, a)
444
445 # simulate an event processing activity (from user's perspective)
446 def profiler_simulation(self, x, y, z):
447 t = self.timer()
448 ## t = t[0] + t[1]
449 self.ut = t
Guido van Rossumb6775db1994-08-01 11:34:53 +0000450
451
452
Guido van Rossumb6775db1994-08-01 11:34:53 +0000453class OldProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000454 """A derived profiler that simulates the old style profile, providing
455 errant results on recursive functions. The reason for the usefulness of
456 this profiler is that it runs faster (i.e., less overhead). It still
457 creates all the caller stats, and is quite useful when there is *no*
458 recursion in the user's code.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000459
Tim Peters2344fae2001-01-15 00:50:52 +0000460 This code also shows how easy it is to create a modified profiler.
461 """
Guido van Rossumb6775db1994-08-01 11:34:53 +0000462
Tim Peters2344fae2001-01-15 00:50:52 +0000463 def trace_dispatch_exception(self, frame, t):
464 rt, rtt, rct, rfn, rframe, rcur = self.cur
465 if rcur and not rframe is frame:
466 return self.trace_dispatch_return(rframe, t)
467 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000468
Tim Peters2344fae2001-01-15 00:50:52 +0000469 def trace_dispatch_call(self, frame, t):
470 fn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000471
Tim Peters2344fae2001-01-15 00:50:52 +0000472 self.cur = (t, 0, 0, fn, frame, self.cur)
473 if self.timings.has_key(fn):
474 tt, ct, callers = self.timings[fn]
475 self.timings[fn] = tt, ct, callers
476 else:
477 self.timings[fn] = 0, 0, {}
478 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000479
Tim Peters2344fae2001-01-15 00:50:52 +0000480 def trace_dispatch_return(self, frame, t):
481 rt, rtt, rct, rfn, frame, rcur = self.cur
482 rtt = rtt + t
483 sft = rtt + rct
Guido van Rossumb6775db1994-08-01 11:34:53 +0000484
Tim Peters2344fae2001-01-15 00:50:52 +0000485 pt, ptt, pct, pfn, pframe, pcur = rcur
486 self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
487
488 tt, ct, callers = self.timings[rfn]
489 if callers.has_key(pfn):
490 callers[pfn] = callers[pfn] + 1
491 else:
492 callers[pfn] = 1
493 self.timings[rfn] = tt+rtt, ct + sft, callers
494
495 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000496
497
Tim Peters2344fae2001-01-15 00:50:52 +0000498 def snapshot_stats(self):
499 self.stats = {}
500 for func in self.timings.keys():
501 tt, ct, callers = self.timings[func]
502 callers = callers.copy()
503 nc = 0
504 for func_caller in callers.keys():
505 nc = nc + callers[func_caller]
506 self.stats[func] = nc, nc, tt, ct, callers
Guido van Rossumb6775db1994-08-01 11:34:53 +0000507
Tim Peters2344fae2001-01-15 00:50:52 +0000508
Guido van Rossumb6775db1994-08-01 11:34:53 +0000509
Guido van Rossumb6775db1994-08-01 11:34:53 +0000510class HotProfile(Profile):
Tim Peters2344fae2001-01-15 00:50:52 +0000511 """The fastest derived profile example. It does not calculate
512 caller-callee relationships, and does not calculate cumulative
513 time under a function. It only calculates time spent in a
514 function, so it runs very quickly due to its very low overhead.
515 """
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000516
Tim Peters2344fae2001-01-15 00:50:52 +0000517 def trace_dispatch_exception(self, frame, t):
518 rt, rtt, rfn, rframe, rcur = self.cur
519 if rcur and not rframe is frame:
520 return self.trace_dispatch_return(rframe, t)
521 return 0
Guido van Rossumb6775db1994-08-01 11:34:53 +0000522
Tim Peters2344fae2001-01-15 00:50:52 +0000523 def trace_dispatch_call(self, frame, t):
524 self.cur = (t, 0, frame, self.cur)
525 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000526
Tim Peters2344fae2001-01-15 00:50:52 +0000527 def trace_dispatch_return(self, frame, t):
528 rt, rtt, frame, rcur = self.cur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000529
Tim Peters2344fae2001-01-15 00:50:52 +0000530 rfn = `frame.f_code`
Guido van Rossumb6775db1994-08-01 11:34:53 +0000531
Tim Peters2344fae2001-01-15 00:50:52 +0000532 pt, ptt, pframe, pcur = rcur
533 self.cur = pt, ptt+rt, pframe, pcur
Guido van Rossumb6775db1994-08-01 11:34:53 +0000534
Tim Peters2344fae2001-01-15 00:50:52 +0000535 if self.timings.has_key(rfn):
536 nc, tt = self.timings[rfn]
537 self.timings[rfn] = nc + 1, rt + rtt + tt
538 else:
539 self.timings[rfn] = 1, rt + rtt
Guido van Rossumb6775db1994-08-01 11:34:53 +0000540
Tim Peters2344fae2001-01-15 00:50:52 +0000541 return 1
Guido van Rossumb6775db1994-08-01 11:34:53 +0000542
543
Tim Peters2344fae2001-01-15 00:50:52 +0000544 def snapshot_stats(self):
545 self.stats = {}
546 for func in self.timings.keys():
547 nc, tt = self.timings[func]
548 self.stats[func] = nc, nc, tt, 0, {}
Guido van Rossumb6775db1994-08-01 11:34:53 +0000549
Tim Peters2344fae2001-01-15 00:50:52 +0000550
Guido van Rossumb6775db1994-08-01 11:34:53 +0000551
552#****************************************************************************
553def Stats(*args):
Tim Peters2344fae2001-01-15 00:50:52 +0000554 print 'Report generating functions are in the "pstats" module\a'
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000555
556
557# When invoked as main program, invoke the profiler on a script
558if __name__ == '__main__':
Tim Peters2344fae2001-01-15 00:50:52 +0000559 import sys
560 import os
561 if not sys.argv[1:]:
562 print "usage: profile.py scriptfile [arg] ..."
563 sys.exit(2)
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000564
Tim Peters2344fae2001-01-15 00:50:52 +0000565 filename = sys.argv[1] # Get script filename
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000566
Tim Peters2344fae2001-01-15 00:50:52 +0000567 del sys.argv[0] # Hide "profile.py" from argument list
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000568
Tim Peters2344fae2001-01-15 00:50:52 +0000569 # Insert script directory in front of module search path
570 sys.path.insert(0, os.path.dirname(filename))
Guido van Rossumcc778eb1996-10-01 02:55:54 +0000571
Tim Peters2344fae2001-01-15 00:50:52 +0000572 run('execfile(' + `filename` + ')')