Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
18e6e9c
Allow using sys.monitoring for bdb
gaogaotiantian Sep 25, 2024
b29aff5
Add basic line ignore
gaogaotiantian Sep 25, 2024
23601f3
Use setttrace as default backend for bdb
gaogaotiantian Sep 25, 2024
c9a92f6
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 25, 2024
8955d78
Use settrace by default for pdb.Pdb, but use monitoring for all
gaogaotiantian Sep 26, 2024
6afc2e7
Only trigger events on thread calling start_trace
gaogaotiantian Oct 16, 2024
b595682
Use local events when possible
gaogaotiantian Oct 16, 2024
addf465
Merge branch 'main' into pdb-use-monitoring
gaogaotiantian Jan 19, 2025
70d5138
Fix ignore and condition of breakpoints
gaogaotiantian Feb 8, 2025
7c83425
Merge branch 'main' into pdb-use-monitoring
gaogaotiantian Feb 8, 2025
fe9971c
Address comments about backend
gaogaotiantian Feb 18, 2025
69a5030
Merge branch 'main' into pdb-use-monitoring
gaogaotiantian Feb 18, 2025
05cc3b0
We need BaseException to handle SystemExit case
gaogaotiantian Feb 18, 2025
e6bc287
Move default_backend to module level and provide utils
gaogaotiantian Feb 19, 2025
f0c1306
Do not need to pass trace_dispatch explicitly
gaogaotiantian Feb 19, 2025
a9b53ed
Add docs
gaogaotiantian Feb 19, 2025
9790085
Add version added
gaogaotiantian Feb 19, 2025
ea811f2
Add new functions to __all__
gaogaotiantian Feb 19, 2025
ad2179e
Merge branch 'main' into pdb-use-monitoring
gaogaotiantian Mar 7, 2025
e4ccd8a
Restart events after user line
gaogaotiantian Mar 7, 2025
d2c1f2e
Merge branch 'main' into pdb-use-monitoring
gaogaotiantian Mar 17, 2025
e9252ec
Remove blank line
gaogaotiantian Mar 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Allow using sys.monitoring for bdb
  • Loading branch information
gaogaotiantian committed Jan 17, 2025
commit 18e6e9ceca88f6f3946ab06fe12b8562246562af
180 changes: 169 additions & 11 deletions Lib/bdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import weakref
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
from functools import partial

__all__ = ["BdbQuit", "Bdb", "Breakpoint"]

Expand All @@ -15,6 +16,142 @@ class BdbQuit(Exception):
"""Exception to give up completely."""


E = sys.monitoring.events

class _MonitoringTracer:
def __init__(self):
self._tool_id = sys.monitoring.DEBUGGER_ID
self._name = 'bdbtracer'
self._tracefunc = None

def start_trace(self, tracefunc):
self._tracefunc = tracefunc
curr_tool = sys.monitoring.get_tool(self._tool_id)
if curr_tool is None:
sys.monitoring.use_tool_id(self._tool_id, self._name)
elif curr_tool == self._name:
sys.monitoring.set_events(self._tool_id, 0)
else:
raise ValueError('Another debugger is using the monitoring tool')
E = sys.monitoring.events
all_events = 0
for event in (E.PY_START, E.PY_RESUME, E.PY_THROW):
sys.monitoring.register_callback(self._tool_id, event, self.call_callback)
all_events |= event
for event in (E.LINE, ):
sys.monitoring.register_callback(self._tool_id, event, self.line_callback)
all_events |= event
for event in (E.JUMP, ):
sys.monitoring.register_callback(self._tool_id, event, self.jump_callback)
all_events |= event
for event in (E.PY_RETURN, E.PY_YIELD):
sys.monitoring.register_callback(self._tool_id, event, self.return_callback)
all_events |= event
for event in (E.PY_UNWIND, ):
sys.monitoring.register_callback(self._tool_id, event, self.unwind_callback)
all_events |= event
for event in (E.RAISE, E.STOP_ITERATION):
sys.monitoring.register_callback(self._tool_id, event, self.exception_callback)
all_events |= event
for event in (E.INSTRUCTION, ):
sys.monitoring.register_callback(self._tool_id, event, self.opcode_callback)
self.check_trace_opcodes()
sys.monitoring.set_events(self._tool_id, all_events)

def stop_trace(self):
curr_tool = sys.monitoring.get_tool(self._tool_id)
if curr_tool != self._name:
return
for event in (E.PY_START, E.PY_RESUME, E.PY_RETURN, E.PY_YIELD, E.RAISE, E.LINE,
E.JUMP, E.PY_UNWIND, E.PY_THROW, E.STOP_ITERATION):
sys.monitoring.register_callback(self._tool_id, event, None)
sys.monitoring.set_events(self._tool_id, 0)
self.check_trace_opcodes()
sys.monitoring.free_tool_id(self._tool_id)

def callback_wrapper(func):
def wrapper(self, *args):
try:
frame = sys._getframe().f_back
return func(self, frame, *args)
except Exception:
self.stop_trace()
raise
return wrapper

@callback_wrapper
def call_callback(self, frame, code, *args):
local_tracefunc = self._tracefunc(frame, 'call', None)
if local_tracefunc is not None:
frame.f_trace = local_tracefunc

@callback_wrapper
def return_callback(self, frame, code, offset, retval):
if frame.f_trace:
frame.f_trace(frame, 'return', retval)

@callback_wrapper
def unwind_callback(self, frame, code, *args):
if frame.f_trace:
frame.f_trace(frame, 'return', None)

@callback_wrapper
def line_callback(self, frame, code, *args):
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)

@callback_wrapper
def jump_callback(self, frame, code, inst_offset, dest_offset):
Comment thread
markshannon marked this conversation as resolved.
if dest_offset > inst_offset:
return sys.monitoring.DISABLE
inst_lineno = self._get_lineno(code, inst_offset)
dest_lineno = self._get_lineno(code, dest_offset)
if inst_lineno != dest_lineno:
return sys.monitoring.DISABLE
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)

@callback_wrapper
def exception_callback(self, frame, code, offset, exc):
if frame.f_trace:
if exc.__traceback__ and hasattr(exc.__traceback__, 'tb_frame'):
tb = exc.__traceback__
while tb:
if tb.tb_frame.f_locals.get('self') is self:
return
tb = tb.tb_next
frame.f_trace(frame, 'exception', (type(exc), exc, exc.__traceback__))

@callback_wrapper
def opcode_callback(self, frame, code, offset):
if frame.f_trace and frame.f_trace_opcodes:
frame.f_trace(frame, 'opcode', None)

def check_trace_opcodes(self, frame=None):
if frame is None:
frame = sys._getframe().f_back
while frame is not None:
self.set_trace_opcodes(frame, frame.f_trace_opcodes)
frame = frame.f_back

def set_trace_opcodes(self, frame, trace_opcodes):
if sys.monitoring.get_tool(self._tool_id) != self._name:
return
if trace_opcodes:
sys.monitoring.set_local_events(self._tool_id, frame.f_code, E.INSTRUCTION)
else:
sys.monitoring.set_local_events(self._tool_id, frame.f_code, 0)

def _get_lineno(self, code, offset):
import dis
last_lineno = None
for start, lineno in dis.findlinestarts(code):
if offset < start:
return last_lineno
last_lineno = lineno
return last_lineno


class Bdb:
"""Generic Python debugger base class.

Expand All @@ -29,7 +166,7 @@ class Bdb:
is determined by the __name__ in the frame globals.
"""

def __init__(self, skip=None):
def __init__(self, skip=None, backend='monitoring'):
self.skip = set(skip) if skip else None
self.breaks = {}
self.fncache = {}
Expand All @@ -38,6 +175,11 @@ def __init__(self, skip=None):
self.trace_opcodes = False
self.enterframe = None
self.code_linenos = weakref.WeakKeyDictionary()
self.backend = backend
if backend == 'monitoring':
self.monitoring_tracer = _MonitoringTracer()
else:
Comment thread
gaogaotiantian marked this conversation as resolved.
Outdated
self.monitoring_tracer = None

self._load_breaks()

Expand All @@ -58,6 +200,18 @@ def canonic(self, filename):
self.fncache[filename] = canonic
return canonic

def start_trace(self, trace_dispatch):
if self.backend == 'monitoring':
Comment thread
gaogaotiantian marked this conversation as resolved.
Outdated
self.monitoring_tracer.start_trace(trace_dispatch)
else:
sys.settrace(self.trace_dispatch)

def stop_trace(self):
if self.backend == 'monitoring':
Comment thread
gaogaotiantian marked this conversation as resolved.
Outdated
self.monitoring_tracer.stop_trace()
else:
sys.settrace(None)

def reset(self):
"""Set values of attributes as ready to start debugging."""
import linecache
Expand Down Expand Up @@ -327,6 +481,8 @@ def _set_trace_opcodes(self, trace_opcodes):
frame = self.enterframe
while frame is not None:
frame.f_trace_opcodes = trace_opcodes
if self.backend == 'monitoring':
Comment thread
gaogaotiantian marked this conversation as resolved.
Outdated
self.monitoring_tracer.set_trace_opcodes(frame, trace_opcodes)
if frame is self.botframe:
break
frame = frame.f_back
Expand Down Expand Up @@ -391,7 +547,7 @@ def set_trace(self, frame=None):

If frame is not specified, debugging starts from caller's frame.
"""
sys.settrace(None)
self.stop_trace()
if frame is None:
frame = sys._getframe().f_back
self.reset()
Expand All @@ -405,7 +561,7 @@ def set_trace(self, frame=None):
frame = frame.f_back
self.set_stepinstr()
self.enterframe = None
sys.settrace(self.trace_dispatch)
self.start_trace(self.trace_dispatch)

def set_continue(self):
"""Stop only at breakpoints or when finished.
Expand All @@ -416,13 +572,15 @@ def set_continue(self):
self._set_stopinfo(self.botframe, None, -1)
if not self.breaks:
# no breakpoints; run without debugger overhead
sys.settrace(None)
self.stop_trace()
frame = sys._getframe().f_back
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
if self.backend == 'monitoring':
self.monitoring_tracer.set_trace_opcodes(frame, trace_opcodes)
self.frame_trace_lines_opcodes = {}
self.enterframe = None

Expand All @@ -434,7 +592,7 @@ def set_quit(self):
self.stopframe = self.botframe
self.returnframe = None
self.quitting = True
sys.settrace(None)
self.stop_trace()

# Derived classes and clients can call the following methods
# to manipulate breakpoints. These methods return an
Expand Down Expand Up @@ -679,14 +837,14 @@ def run(self, cmd, globals=None, locals=None):
self.reset()
if isinstance(cmd, str):
cmd = compile(cmd, "<string>", "exec")
sys.settrace(self.trace_dispatch)
self.start_trace(self.trace_dispatch)
try:
exec(cmd, globals, locals)
except BdbQuit:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()

def runeval(self, expr, globals=None, locals=None):
"""Debug an expression executed via the eval() function.
Expand All @@ -699,14 +857,14 @@ def runeval(self, expr, globals=None, locals=None):
if locals is None:
locals = globals
self.reset()
sys.settrace(self.trace_dispatch)
self.start_trace(self.trace_dispatch)
try:
return eval(expr, globals, locals)
except BdbQuit:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()

def runctx(self, cmd, globals, locals):
"""For backwards-compatibility. Defers to run()."""
Expand All @@ -721,15 +879,15 @@ def runcall(self, func, /, *args, **kwds):
Return the result of the function call.
"""
self.reset()
sys.settrace(self.trace_dispatch)
self.start_trace(self.trace_dispatch)
res = None
try:
res = func(*args, **kwds)
except BdbQuit:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()
return res


Expand Down
4 changes: 2 additions & 2 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ def do_debug(self, arg):
argument (which is an arbitrary expression or statement to be
executed in the current environment).
"""
sys.settrace(None)
self.stop_trace()
globals = self.curframe.f_globals
locals = self.curframe.f_locals
p = Pdb(self.completekey, self.stdin, self.stdout)
Expand All @@ -1715,7 +1715,7 @@ def do_debug(self, arg):
except Exception:
self._error_exc()
self.message("LEAVING RECURSIVE DEBUGGER")
sys.settrace(self.trace_dispatch)
self.start_trace(self.trace_dispatch)
self.lastcmd = p.lastcmd

complete_debug = _complete_expression
Expand Down