@@ -32,8 +32,10 @@ def __init__(self, skip=None):
3232self .skip = set (skip )if skip else None
3333self .breaks = {}
3434self .fncache = {}
35- self .frame_trace_lines = {}
35+ self .frame_trace_lines_opcodes = {}
3636self .frame_returning = None
37+ self .trace_opcodes = False
38+ self .enterframe = None
3739
3840self ._load_breaks ()
3941
@@ -85,6 +87,9 @@ def trace_dispatch(self, frame, event, arg):
8587
8688 The arg parameter depends on the previous event.
8789 """
90+
91+ self .enterframe = frame
92+
8893if self .quitting :
8994return # None
9095if event == 'line' :
@@ -101,6 +106,8 @@ def trace_dispatch(self, frame, event, arg):
101106return self .trace_dispatch
102107if event == 'c_return' :
103108return self .trace_dispatch
109+ if event == 'opcode' :
110+ return self .dispatch_opcode (frame ,arg )
104111print ('bdb.Bdb.dispatch: unknown debugging event:' ,repr (event ))
105112return self .trace_dispatch
106113
@@ -187,6 +194,17 @@ def dispatch_exception(self, frame, arg):
187194
188195return self .trace_dispatch
189196
197+ def dispatch_opcode (self ,frame ,arg ):
198+ """Invoke user function and return trace function for opcode event.
199+ If the debugger stops on the current opcode, invoke
200+ self.user_opcode(). Raise BdbQuit if self.quitting is set.
201+ Return self.trace_dispatch to continue tracing in this scope.
202+ """
203+ if self .stop_here (frame )or self .break_here (frame ):
204+ self .user_opcode (frame )
205+ if self .quitting :raise BdbQuit
206+ return self .trace_dispatch
207+
190208# Normally derived classes don't override the following
191209# methods, but they may if they want to redefine the
192210# definition of stopping and breakpoints.
@@ -273,7 +291,21 @@ def user_exception(self, frame, exc_info):
273291"""Called when we stop on an exception."""
274292pass
275293
276- def _set_stopinfo (self ,stopframe ,returnframe ,stoplineno = 0 ):
294+ def user_opcode (self ,frame ):
295+ """Called when we are about to execute an opcode."""
296+ pass
297+
298+ def _set_trace_opcodes (self ,trace_opcodes ):
299+ if trace_opcodes != self .trace_opcodes :
300+ self .trace_opcodes = trace_opcodes
301+ frame = self .enterframe
302+ while frame is not None :
303+ frame .f_trace_opcodes = trace_opcodes
304+ if frame is self .botframe :
305+ break
306+ frame = frame .f_back
307+
308+ def _set_stopinfo (self ,stopframe ,returnframe ,stoplineno = 0 ,opcode = False ):
277309"""Set the attributes for stopping.
278310
279311 If stoplineno is greater than or equal to 0, then stop at line
@@ -286,6 +318,17 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
286318# stoplineno >= 0 means: stop at line >= the stoplineno
287319# stoplineno -1 means: don't stop at all
288320self .stoplineno = stoplineno
321+ self ._set_trace_opcodes (opcode )
322+
323+ def _set_caller_tracefunc (self ):
324+ # Issue #13183: pdb skips frames after hitting a breakpoint and running
325+ # step commands.
326+ # Restore the trace function in the caller (that may not have been set
327+ # for performance reasons) when returning from the current frame.
328+ if self .frame_returning :
329+ caller_frame = self .frame_returning .f_back
330+ if caller_frame and not caller_frame .f_trace :
331+ caller_frame .f_trace = self .trace_dispatch
289332
290333# Derived classes and clients can call the following methods
291334# to affect the stepping state.
@@ -300,16 +343,14 @@ def set_until(self, frame, lineno=None):
300343
301344def set_step (self ):
302345"""Stop after one line of code."""
303- # Issue #13183: pdb skips frames after hitting a breakpoint and running
304- # step commands.
305- # Restore the trace function in the caller (that may not have been set
306- # for performance reasons) when returning from the current frame.
307- if self .frame_returning :
308- caller_frame = self .frame_returning .f_back
309- if caller_frame and not caller_frame .f_trace :
310- caller_frame .f_trace = self .trace_dispatch
346+ self ._set_caller_tracefunc ()
311347self ._set_stopinfo (None ,None )
312348
349+ def set_stepinstr (self ):
350+ """Stop before the next instruction."""
351+ self ._set_caller_tracefunc ()
352+ self ._set_stopinfo (None ,None ,opcode = True )
353+
313354def set_next (self ,frame ):
314355"""Stop on the next line in or below the given frame."""
315356self ._set_stopinfo (frame ,None )
@@ -329,11 +370,12 @@ def set_trace(self, frame=None):
329370if frame is None :
330371frame = sys ._getframe ().f_back
331372self .reset ()
373+ self .enterframe = frame
332374while frame :
333375frame .f_trace = self .trace_dispatch
334376self .botframe = frame
335- # We need f_trace_liens == True for the debugger to work
336- self . frame_trace_lines [ frame ] = frame . f_trace_lines
377+ self . frame_trace_lines_opcodes [ frame ] = ( frame . f_trace_lines , frame . f_trace_opcodes )
378+ # We need f_trace_lines == True for the debugger to work
337379frame .f_trace_lines = True
338380frame = frame .f_back
339381self .set_step ()
@@ -353,9 +395,9 @@ def set_continue(self):
353395while frame and frame is not self .botframe :
354396del frame .f_trace
355397frame = frame .f_back
356- for frame ,prev_trace_lines in self .frame_trace_lines .items ():
357- frame .f_trace_lines = prev_trace_lines
358- self .frame_trace_lines = {}
398+ for frame ,( trace_lines , trace_opcodes ) in self .frame_trace_lines_opcodes .items ():
399+ frame .f_trace_lines , frame . f_trace_opcodes = trace_lines , trace_opcodes
400+ self .frame_trace_lines_opcodes = {}
359401
360402def set_quit (self ):
361403"""Set quitting attribute to True.