summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Lib/bdb.py
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Lib/bdb.py')
-rw-r--r--contrib/tools/python3/Lib/bdb.py65
1 files changed, 59 insertions, 6 deletions
diff --git a/contrib/tools/python3/Lib/bdb.py b/contrib/tools/python3/Lib/bdb.py
index 085c17ce05d..f256b56daaa 100644
--- a/contrib/tools/python3/Lib/bdb.py
+++ b/contrib/tools/python3/Lib/bdb.py
@@ -33,8 +33,12 @@ class Bdb:
self.skip = set(skip) if skip else None
self.breaks = {}
self.fncache = {}
+ self.frame_trace_lines_opcodes = {}
self.frame_returning = None
+ self.trace_opcodes = False
self.enterframe = None
+ self.cmdframe = None
+ self.cmdlineno = None
self._load_breaks()
@@ -110,6 +114,8 @@ class Bdb:
return self.trace_dispatch
if event == 'c_return':
return self.trace_dispatch
+ if event == 'opcode':
+ return self.dispatch_opcode(frame, arg)
print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
return self.trace_dispatch
@@ -120,7 +126,12 @@ class Bdb:
self.user_line(). Raise BdbQuit if self.quitting is set.
Return self.trace_dispatch to continue tracing in this scope.
"""
- if self.stop_here(frame) or self.break_here(frame):
+ # GH-136057
+ # For line events, we don't want to stop at the same line where
+ # the latest next/step command was issued.
+ if (self.stop_here(frame) or self.break_here(frame)) and not (
+ self.cmdframe == frame and self.cmdlineno == frame.f_lineno
+ ):
self.user_line(frame)
if self.quitting: raise BdbQuit
return self.trace_dispatch
@@ -201,6 +212,17 @@ class Bdb:
return self.trace_dispatch
+ def dispatch_opcode(self, frame, arg):
+ """Invoke user function and return trace function for opcode event.
+ If the debugger stops on the current opcode, invoke
+ self.user_opcode(). Raise BdbQuit if self.quitting is set.
+ Return self.trace_dispatch to continue tracing in this scope.
+ """
+ if self.stop_here(frame) or self.break_here(frame):
+ self.user_opcode(frame)
+ if self.quitting: raise BdbQuit
+ return self.trace_dispatch
+
# Normally derived classes don't override the following
# methods, but they may if they want to redefine the
# definition of stopping and breakpoints.
@@ -287,7 +309,22 @@ class Bdb:
"""Called when we stop on an exception."""
pass
- def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
+ def user_opcode(self, frame):
+ """Called when we are about to execute an opcode."""
+ pass
+
+ def _set_trace_opcodes(self, trace_opcodes):
+ if trace_opcodes != self.trace_opcodes:
+ self.trace_opcodes = trace_opcodes
+ frame = self.enterframe
+ while frame is not None:
+ frame.f_trace_opcodes = trace_opcodes
+ if frame is self.botframe:
+ break
+ frame = frame.f_back
+
+ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False,
+ cmdframe=None, cmdlineno=None):
"""Set the attributes for stopping.
If stoplineno is greater than or equal to 0, then stop at line
@@ -300,6 +337,11 @@ class Bdb:
# stoplineno >= 0 means: stop at line >= the stoplineno
# stoplineno -1 means: don't stop at all
self.stoplineno = stoplineno
+ # cmdframe/cmdlineno is the frame/line number when the user issued
+ # step/next commands.
+ self.cmdframe = cmdframe
+ self.cmdlineno = cmdlineno
+ self._set_trace_opcodes(opcode)
def _set_caller_tracefunc(self, current_frame):
# Issue #13183: pdb skips frames after hitting a breakpoint and running
@@ -324,11 +366,17 @@ class Bdb:
def set_step(self):
"""Stop after one line of code."""
- self._set_stopinfo(None, None)
+ # set_step() could be called from signal handler so enterframe might be None
+ self._set_stopinfo(None, None, cmdframe=self.enterframe,
+ cmdlineno=getattr(self.enterframe, 'f_lineno', None))
+
+ def set_stepinstr(self):
+ """Stop before the next instruction."""
+ self._set_stopinfo(None, None, opcode=True)
def set_next(self, frame):
"""Stop on the next line in or below the given frame."""
- self._set_stopinfo(frame, None)
+ self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno)
def set_return(self, frame):
"""Stop when returning from the given frame."""
@@ -342,7 +390,6 @@ class Bdb:
If frame is not specified, debugging starts from caller's frame.
"""
- sys.settrace(None)
if frame is None:
frame = sys._getframe().f_back
self.reset()
@@ -350,8 +397,11 @@ class Bdb:
while frame:
frame.f_trace = self.trace_dispatch
self.botframe = frame
+ self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes)
+ # We need f_trace_lines == True for the debugger to work
+ frame.f_trace_lines = True
frame = frame.f_back
- self.set_step()
+ self.set_stepinstr()
sys.settrace(self.trace_dispatch)
def set_continue(self):
@@ -368,6 +418,9 @@ class Bdb:
while frame and frame is not self.botframe:
del frame.f_trace
frame = frame.f_back
+ for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items():
+ frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes
+ self.frame_trace_lines_opcodes = {}
def set_quit(self):
"""Set quitting attribute to True.