[Python-checkins] CVS: python/dist/src/Lib profile.py,1.32,1.33
Guido van Rossum
gvanrossum@users.sourceforge.net
Wed, 03 Oct 2001 17:58:26 -0700
Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv14852
Modified Files:
profile.py
Log Message:
Hopefully fix the profiler right. Add a test suite that checks that
it deals correctly with some anomalous cases; according to this test
suite I've fixed it right.
The anomalous cases had to do with 'exception' events: these aren't
generated when they would be most helpful, and the profiler has to
work hard to recover the right information. The problems occur when C
code (such as hasattr(), which is used as the example here) calls back
into Python code and clears an exception raised by that Python code.
Consider this example:
def foo():
hasattr(obj, "bar")
Where obj is an instance from a class like this:
class C:
def __getattr__(self, name):
raise AttributeError
The profiler sees the following sequence of events:
call (foo)
call (__getattr__)
exception (in __getattr__)
return (from foo)
Previously, the profiler would assume the return event returned from
__getattr__. An if statement checking for this condition and raising
an exception was commented out... This version does the right thing.
Index: profile.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/profile.py,v
retrieving revision 1.32
retrieving revision 1.33
diff -C2 -d -r1.32 -r1.33
*** profile.py 2001/10/03 21:12:32 1.32
--- profile.py 2001/10/04 00:58:24 1.33
***************
*** 110,125 ****
used to write into the frames local dictionary!!) Derived classes
can change the definition of some entries, as long as they leave
! [-2:] intact.
! [ 0] = Time that needs to be charged to the parent frame's function.
! It is used so that a function call will not have to access the
! timing data for the parent frame.
! [ 1] = Total time spent in this frame's function, excluding time in
! subfunctions
! [ 2] = Cumulative time spent in this frame's function, including time in
! all subfunctions to this frame.
! [-3] = Name of the function that corresponds to this frame.
! [-2] = Actual frame that we correspond to (used to sync exception handling)
! [-1] = Our parent 6-tuple (corresponds to frame.f_back)
Timing data for each function is stored as a 5-tuple in the dictionary
--- 110,125 ----
used to write into the frames local dictionary!!) Derived classes
can change the definition of some entries, as long as they leave
! [3:] intact.
! [0] = Time that needs to be charged to the parent frame's function.
! It is used so that a function call will not have to access the
! timing data for the parent frame.
! [1] = Total time spent in this frame's function, excluding time in
! subfunctions
! [2] = Cumulative time spent in this frame's function, including time in
! all subfunctions to this frame (but excluding this frame!).
! [3] = Name of the function that corresponds to this frame.
! [4] = Actual frame that we correspond to (used to sync exception handling)
! [5] = Our parent 6-tuple (corresponds to frame.f_back)
Timing data for each function is stored as a 5-tuple in the dictionary
***************
*** 244,251 ****
if (rframe is not frame) and rcur:
return self.trace_dispatch_return(rframe, t)
! return 0
def trace_dispatch_call(self, frame, t):
fcode = frame.f_code
fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
--- 244,262 ----
if (rframe is not frame) and rcur:
return self.trace_dispatch_return(rframe, t)
! self.cur = rt, rtt+t, rct, rfn, rframe, rcur
! return 1
def trace_dispatch_call(self, frame, t):
+ if self.cur and frame.f_back is not self.cur[4]:
+ rt, rtt, rct, rfn, rframe, rcur = self.cur
+ if not isinstance(rframe, Profile.fake_frame):
+ if rframe.f_back is not frame.f_back:
+ print rframe, rframe.f_back
+ print frame, frame.f_back
+ raise "Bad call", self.cur[3]
+ self.trace_dispatch_return(rframe, 0)
+ if self.cur and frame.f_back is not self.cur[4]:
+ raise "Bad call[2]", self.cur[3]
fcode = frame.f_code
fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
***************
*** 260,264 ****
def trace_dispatch_return(self, frame, t):
! # if not frame is self.cur[-2]: raise "Bad return", self.cur[3]
# Prefix "r" means part of the Returning or exiting frame
--- 271,279 ----
def trace_dispatch_return(self, frame, t):
! if frame is not self.cur[4]:
! if frame is self.cur[4].f_back:
! self.trace_dispatch_return(self.cur[4], 0)
! else:
! raise "Bad return", self.cur[3]
# Prefix "r" means part of the Returning or exiting frame
***************
*** 303,307 ****
def set_cmd(self, cmd):
! if self.cur[-1]: return # already set
self.cmd = cmd
self.simulate_call(cmd)
--- 318,322 ----
def set_cmd(self, cmd):
! if self.cur[5]: return # already set
self.cmd = cmd
self.simulate_call(cmd)
***************
*** 325,329 ****
code = self.fake_code('profile', 0, name)
if self.cur:
! pframe = self.cur[-2]
else:
pframe = None
--- 340,344 ----
code = self.fake_code('profile', 0, name)
if self.cur:
! pframe = self.cur[4]
else:
pframe = None
***************
*** 338,345 ****
get_time = self.get_time
t = get_time() - self.t
! while self.cur[-1]:
# We *can* cause assertion errors here if
# dispatch_trace_return checks for a frame match!
! a = self.dispatch['return'](self, self.cur[-2], t)
t = 0
self.t = get_time() - t
--- 353,360 ----
get_time = self.get_time
t = get_time() - self.t
! while self.cur[5]:
# We *can* cause assertion errors here if
# dispatch_trace_return checks for a frame match!
! a = self.dispatch['return'](self, self.cur[4], t)
t = 0
self.t = get_time() - t