https://github.com/python/cpython/commit/f34e965e52b9bdf157b829371870edfde45b80bf
commit: f34e965e52b9bdf157b829371870edfde45b80bf
branch: main
author: Tian Gao <[email protected]>
committer: brandtbucher <[email protected]>
date: 2024-05-04T07:44:49-07:00
summary:
GH-111744: Support opcode events in bdb (GH-111834)
files:
A Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst
M Lib/bdb.py
M Lib/test/test_bdb.py
M Lib/test/test_pdb.py
diff --git a/Lib/bdb.py b/Lib/bdb.py
index 1acf7957f0d669..675c8ae51df4c3 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -32,8 +32,10 @@ def __init__(self, skip=None):
self.skip = set(skip) if skip else None
self.breaks = {}
self.fncache = {}
- self.frame_trace_lines = {}
+ self.frame_trace_lines_opcodes = {}
self.frame_returning = None
+ self.trace_opcodes = False
+ self.enterframe = None
self._load_breaks()
@@ -85,6 +87,9 @@ def trace_dispatch(self, frame, event, arg):
The arg parameter depends on the previous event.
"""
+
+ self.enterframe = frame
+
if self.quitting:
return # None
if event == 'line':
@@ -101,6 +106,8 @@ def trace_dispatch(self, frame, event, arg):
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
@@ -187,6 +194,17 @@ def dispatch_exception(self, frame, arg):
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.
@@ -273,7 +291,21 @@ def user_exception(self, frame, exc_info):
"""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):
"""Set the attributes for stopping.
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):
# stoplineno >= 0 means: stop at line >= the stoplineno
# stoplineno -1 means: don't stop at all
self.stoplineno = stoplineno
+ self._set_trace_opcodes(opcode)
+
+ def _set_caller_tracefunc(self):
+ # Issue #13183: pdb skips frames after hitting a breakpoint and running
+ # step commands.
+ # Restore the trace function in the caller (that may not have been set
+ # for performance reasons) when returning from the current frame.
+ if self.frame_returning:
+ caller_frame = self.frame_returning.f_back
+ if caller_frame and not caller_frame.f_trace:
+ caller_frame.f_trace = self.trace_dispatch
# Derived classes and clients can call the following methods
# to affect the stepping state.
@@ -300,16 +343,14 @@ def set_until(self, frame, lineno=None):
def set_step(self):
"""Stop after one line of code."""
- # Issue #13183: pdb skips frames after hitting a breakpoint and running
- # step commands.
- # Restore the trace function in the caller (that may not have been set
- # for performance reasons) when returning from the current frame.
- if self.frame_returning:
- caller_frame = self.frame_returning.f_back
- if caller_frame and not caller_frame.f_trace:
- caller_frame.f_trace = self.trace_dispatch
+ self._set_caller_tracefunc()
self._set_stopinfo(None, None)
+ def set_stepinstr(self):
+ """Stop before the next instruction."""
+ self._set_caller_tracefunc()
+ 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)
@@ -329,11 +370,12 @@ def set_trace(self, frame=None):
if frame is None:
frame = sys._getframe().f_back
self.reset()
+ self.enterframe = frame
while frame:
frame.f_trace = self.trace_dispatch
self.botframe = frame
- # We need f_trace_liens == True for the debugger to work
- self.frame_trace_lines[frame] = frame.f_trace_lines
+ 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()
@@ -353,9 +395,9 @@ def set_continue(self):
while frame and frame is not self.botframe:
del frame.f_trace
frame = frame.f_back
- for frame, prev_trace_lines in self.frame_trace_lines.items():
- frame.f_trace_lines = prev_trace_lines
- self.frame_trace_lines = {}
+ 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.
diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py
index 568c88e326c087..ed1a63daea1186 100644
--- a/Lib/test/test_bdb.py
+++ b/Lib/test/test_bdb.py
@@ -228,6 +228,10 @@ def user_exception(self, frame, exc_info):
self.process_event('exception', frame)
self.next_set_method()
+ def user_opcode(self, frame):
+ self.process_event('opcode', frame)
+ self.next_set_method()
+
def do_clear(self, arg):
# The temporary breakpoints are deleted in user_line().
bp_list = [self.currentbp]
@@ -366,7 +370,7 @@ def next_set_method(self):
set_method = getattr(self, 'set_' + set_type)
# The following set methods give back control to the tracer.
- if set_type in ('step', 'continue', 'quit'):
+ if set_type in ('step', 'stepinstr', 'continue', 'quit'):
set_method()
return
elif set_type in ('next', 'return'):
@@ -610,6 +614,15 @@ def test_step_next_on_last_statement(self):
with TracerRun(self) as tracer:
tracer.runcall(tfunc_main)
+ def test_stepinstr(self):
+ self.expect_set = [
+ ('line', 2, 'tfunc_main'), ('stepinstr', ),
+ ('opcode', 2, 'tfunc_main'), ('next', ),
+ ('line', 3, 'tfunc_main'), ('quit', ),
+ ]
+ with TracerRun(self) as tracer:
+ tracer.runcall(tfunc_main)
+
def test_next(self):
self.expect_set = [
('line', 2, 'tfunc_main'), ('step', ),
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index c9ea52717490b1..001562acae7458 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -2456,7 +2456,6 @@ def test_pdb_issue_gh_108976():
... 'continue'
... ]):
... test_function()
- bdb.Bdb.dispatch: unknown debugging event: 'opcode'
> <doctest test.test_pdb.test_pdb_issue_gh_108976[0]>(5)test_function()
-> a = 1
(Pdb) continue
diff --git
a/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst
b/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst
new file mode 100644
index 00000000000000..ed856e7667a372
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-11-07-22-41-42.gh-issue-111744.TbLxF0.rst
@@ -0,0 +1 @@
+Support opcode events in :mod:`bdb`
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]