Skip to content
Draft
Changes from all commits
Commits
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
116 changes: 99 additions & 17 deletions Tools/gdb/libpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ def write(self, data):
def getvalue(self):
return self._val


def _PyStackRef_AsPyObjectBorrow(gdbval):
return gdb.Value(int(gdbval['bits']) & ~USED_TAGS)


class PyObjectPtr(object):
"""
Class wrapping a gdb.Value that's either a (PyObject*) within the
Expand All @@ -170,7 +175,7 @@ def __init__(self, gdbval, cast_to=None):
if gdbval.type.name == '_PyStackRef':
if cast_to is None:
cast_to = gdb.lookup_type('PyObject').pointer()
self._gdbval = gdb.Value(int(gdbval['bits']) & ~USED_TAGS).cast(cast_to)
self._gdbval = _PyStackRef_AsPyObjectBorrow(gdbval).cast(cast_to)
elif cast_to:
self._gdbval = gdbval.cast(cast_to)
else:
Expand Down Expand Up @@ -1040,6 +1045,23 @@ def print_traceback(self):
return
return self._frame.print_traceback()

def current_line(filename, lineno):
if lineno is None:
return '(failed to get frame line number)'

try:
with open(os.fsencode(filename), 'r', encoding="utf-8") as fp:
lines = fp.readlines()
except IOError:
return None

try:
# Convert from 1-based current_line_num to 0-based list offset
return lines[lineno - 1]
except IndexError:
return None


class PyFramePtr:

def __init__(self, gdbval):
Expand Down Expand Up @@ -1188,22 +1210,9 @@ def current_line(self):
if self.is_optimized_out():
return FRAME_INFO_OPTIMIZED_OUT

lineno = self.current_line_num()
if lineno is None:
return '(failed to get frame line number)'

filename = self.filename()
try:
with open(os.fsencode(filename), 'r', encoding="utf-8") as fp:
lines = fp.readlines()
except IOError:
return None

try:
# Convert from 1-based current_line_num to 0-based list offset
return lines[lineno - 1]
except IndexError:
return None
lineno = self.current_line_num()
return current_line(filename, lineno)

def write_repr(self, out, visited):
if self.is_optimized_out():
Expand Down Expand Up @@ -2072,7 +2081,6 @@ def __init__(self):
gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)


def invoke(self, args, from_tty):
frame = Frame.get_selected_python_frame()
if not frame:
Expand All @@ -2087,6 +2095,46 @@ def invoke(self, args, from_tty):

PyBacktrace()

class PyBacktraceTSS(gdb.Command):
'Similar to py-bt but read _Py_tss_gilstate or _Py_tss_tstate'
def __init__(self):
gdb.Command.__init__ (self,
"py-bt-tss",
gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)

def invoke(self, args, from_tty):
try:
iframe = gdb.parse_and_eval('_Py_tss_gilstate->current_frame')
except gdb.error:
iframe = gdb.parse_and_eval('_Py_tss_tstate->current_frame')
visited = set()

print('Traceback (most recent call first):')
while True:
is_complete = not _PyFrame_IsIncomplete(iframe)
if is_complete:
code = _PyFrame_GetCode(iframe)
code = PyCodeObjectPtr.from_pyobject_ptr(code)

filename = code.pyop_field('co_filename')
filename = filename.proxyval(visited)
lasti = iframe['instr_ptr'] - _PyFrame_GetBytecode(iframe)
lineno = code.addr2line(lasti)
name = code.pyop_field('co_name')
name = name.proxyval(visited)
print(' File "%s", line %s, in %s'
% (filename, lineno, name))
line = current_line(filename, lineno)
if line is not None:
sys.stdout.write(' %s\n' % line.strip())

iframe = iframe['previous']
if not iframe:
break

PyBacktraceTSS()

class PyPrint(gdb.Command):
'Look up the given python variable name, and print it'
def __init__(self):
Expand Down Expand Up @@ -2156,4 +2204,38 @@ def invoke(self, args, from_tty):

pyop_frame = pyop_frame.previous()


def _PyCode_CODE(code):
cast_to = gdb.lookup_type('PyCodeObject').pointer()
code = code.cast(cast_to)
cast_to = gdb.lookup_type('_Py_CODEUNIT').pointer()
return code['co_code_adaptive'].cast(cast_to)

def _PyFrame_GetCode(iframe):
executable = iframe['f_executable']
try:
# Python 3.14 and newer: f_executable is a _PyStackRef
return _PyStackRef_AsPyObjectBorrow(executable)
except gdb.error:
# Python 3.13: f_executable is a PyCodeObject*
return executable

def _PyFrame_GetBytecode(iframe):
# FIXME: #ifdef Py_GIL_DISABLED
# PyCodeObject *co = _PyFrame_GetCode(f);
# _PyCodeArray *tlbc = _PyCode_GetTLBCArray(co);
# assert(f->tlbc_index >= 0 && f->tlbc_index < tlbc->size);
# return (_Py_CODEUNIT *)tlbc->entries[f->tlbc_index];
return _PyCode_CODE(_PyFrame_GetCode(iframe))

def _PyFrame_IsIncomplete(iframe):
if iframe['owner'] >= FRAME_OWNED_BY_INTERPRETER:
return True
# FIXME:
# return frame->owner != FRAME_OWNED_BY_GENERATOR &&
# frame->instr_ptr < _PyFrame_GetBytecode(frame) +
# _PyFrame_GetCode(frame)->_co_firsttraceable;
return False


PyLocals()
Loading