10 """Class for profiling Python code."""
43 __all__ = [
"run",
"help",
"Profile"]
58 def run(statement, filename=None):
59 """Run statement under profiler optionally saving results in filename
61 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
71 prof = prof.run(statement)
74 if filename
is not None:
75 prof.dump_stats(filename)
77 return prof.print_stats()
81 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
88 print 'Sorry, can\'t find the help file "profile.doc"',
89 print 'along the Python search path.'
94 def _get_time_mac(timer=MacOS.GetTicks):
97 if hasattr(os,
"times"):
98 def _get_time_times(timer=os.times):
106 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
112 [-2:] intact (frame and previous tuple). In case an internal error is
113 detected, the -3 element is used as the function name.
115 [ 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 (this latter is tallied in cur[2]).
120 [ 2] = Total time spent in subfunctions, excluding time executing the
121 frame's function (this latter is tallied in cur[1]).
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).
126 Timing data for each function is stored as a 5-tuple in the dictionary
127 self.timings[]. The index is always the name stored in self.cur[-3].
128 The following are the definitions of the members:
130 [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
138 [4] = A dictionary indicating for each function name, the number of times
158 elif hasattr(time,
'clock'):
161 elif hasattr(os,
'times'):
162 self.
timer = os.times
187 def get_time_timer(timer=timer,
188 reduce=reduce, reducer=operator.add):
189 return reduce(reducer,
timer(), 0)
199 t = t[0] + t[1] - self.
t - self.
bias
201 if self.
dispatch[event](self, frame,t):
206 self.
t = r[0] + r[1] - t
214 if self.
dispatch[event](self, frame,t):
225 if self.
dispatch[event](self, frame, t):
236 if self.
dispatch[event](self, frame, t):
249 rpt, rit, ret, rfn, rframe, rcur = self.
cur
250 if (rframe
is not frame)
and rcur:
252 self.
cur = rpt, rit+t, ret, rfn, rframe, rcur
257 if self.
cur and frame.f_back
is not self.
cur[-2]:
258 rpt, rit, ret, rfn, rframe, rcur = self.
cur
260 assert rframe.f_back
is frame.f_back, (
"Bad call", rfn,
261 rframe, rframe.f_back,
264 assert (self.
cur is None or \
265 frame.f_back
is self.
cur[-2]), (
"Bad call",
268 fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
269 self.
cur = (t, 0, 0, fn, frame, self.
cur)
271 if timings.has_key(fn):
272 cc, ns, tt, ct, callers = timings[fn]
273 timings[fn] = cc, ns + 1, tt, ct, callers
275 timings[fn] = 0, 0, 0, 0, {}
279 if frame
is not self.
cur[-2]:
280 assert frame
is self.
cur[-2].f_back, (
"Bad return", self.
cur[-3])
286 rpt, rit, ret, rfn, frame, rcur = self.
cur
288 frame_total = rit + ret
290 ppt, pit, pet, pfn, pframe, pcur = rcur
291 self.
cur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur
294 cc, ns, tt, ct, callers = timings[rfn]
300 ct = ct + frame_total
303 if callers.has_key(pfn):
304 callers[pfn] = callers[pfn] + 1
311 timings[rfn] = cc, ns - 1, tt + rit, ct, callers
317 "call": trace_dispatch_call,
318 "exception": trace_dispatch_exception,
319 "return": trace_dispatch_return,
330 if self.
cur[-1]:
return
350 code = self.
fake_code(
'profile', 0, name)
352 pframe = self.
cur[-2]
356 self.
dispatch[
'call'](self, frame, 0)
380 marshal.dump(self.
stats, f)
389 for func
in self.timings.keys():
390 cc, ns, tt, ct, callers = self.
timings[func]
391 callers = callers.copy()
393 for func_caller
in callers.keys():
394 nc = nc + callers[func_caller]
395 self.
stats[func] = cc, nc, tt, ct, callers
403 dict = __main__.__dict__
404 return self.
runctx(cmd, dict, dict)
410 exec cmd
in globals, locals
420 return apply(func, args, kw)
466 if self.__class__
is not Profile:
467 raise TypeError(
"Subclasses must override .calibrate().")
469 saved_bias = self.
bias
474 self.
bias = saved_bias
476 def _calibrate_inner(self, m, verbose):
499 elapsed_noprofile = t1 - t0
501 print "elapsed time without profiling =", elapsed_noprofile
508 p.runctx(
'f(m)', globals(), locals())
510 elapsed_profile = t1 - t0
512 print "elapsed time with profiling =", elapsed_profile
517 for (filename, line, funcname), (cc, ns, tt, ct, callers)
in \
519 if funcname
in (
"f",
"f1"):
524 print "'CPU seconds' profiler reported =", reported_time
525 print "total # calls =", total_calls
526 if total_calls != m + 1:
527 raise ValueError(
"internal error: total calls = %d" % total_calls)
533 mean = (reported_time - elapsed_noprofile) / 2.0 / total_calls
535 print "mean stopwatch overhead per profile event =", mean
540 print 'Report generating functions are in the "pstats" module\a'
544 if __name__ ==
'__main__':
546 print "usage: profile.py scriptfile [arg] ..."
549 filename = sys.argv[1]
554 sys.path.insert(0, os.path.dirname(filename))
556 run(
'execfile(' + `filename` +
')')