Skip to content

Commit 1600660

Browse files
committed
gh-142095: Add py-bt-tss command to python-gdb.py
1 parent 71a7cb8 commit 1600660

File tree

1 file changed

+99
-17
lines changed

1 file changed

+99
-17
lines changed

Tools/gdb/libpython.py

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ def write(self, data):
152152
def getvalue(self):
153153
return self._val
154154

155+
156+
def _PyStackRef_AsPyObjectBorrow(gdbval):
157+
return gdb.Value(int(gdbval['bits']) & ~USED_TAGS)
158+
159+
155160
class PyObjectPtr(object):
156161
"""
157162
Class wrapping a gdb.Value that's either a (PyObject*) within the
@@ -170,7 +175,7 @@ def __init__(self, gdbval, cast_to=None):
170175
if gdbval.type.name == '_PyStackRef':
171176
if cast_to is None:
172177
cast_to = gdb.lookup_type('PyObject').pointer()
173-
self._gdbval = gdb.Value(int(gdbval['bits']) & ~USED_TAGS).cast(cast_to)
178+
self._gdbval = _PyStackRef_AsPyObjectBorrow(gdbval).cast(cast_to)
174179
elif cast_to:
175180
self._gdbval = gdbval.cast(cast_to)
176181
else:
@@ -1040,6 +1045,23 @@ def print_traceback(self):
10401045
return
10411046
return self._frame.print_traceback()
10421047

1048+
def current_line(filename, lineno):
1049+
if lineno is None:
1050+
return '(failed to get frame line number)'
1051+
1052+
try:
1053+
with open(os.fsencode(filename), 'r', encoding="utf-8") as fp:
1054+
lines = fp.readlines()
1055+
except IOError:
1056+
return None
1057+
1058+
try:
1059+
# Convert from 1-based current_line_num to 0-based list offset
1060+
return lines[lineno - 1]
1061+
except IndexError:
1062+
return None
1063+
1064+
10431065
class PyFramePtr:
10441066

10451067
def __init__(self, gdbval):
@@ -1188,22 +1210,9 @@ def current_line(self):
11881210
if self.is_optimized_out():
11891211
return FRAME_INFO_OPTIMIZED_OUT
11901212

1191-
lineno = self.current_line_num()
1192-
if lineno is None:
1193-
return '(failed to get frame line number)'
1194-
11951213
filename = self.filename()
1196-
try:
1197-
with open(os.fsencode(filename), 'r', encoding="utf-8") as fp:
1198-
lines = fp.readlines()
1199-
except IOError:
1200-
return None
1201-
1202-
try:
1203-
# Convert from 1-based current_line_num to 0-based list offset
1204-
return lines[lineno - 1]
1205-
except IndexError:
1206-
return None
1214+
lineno = self.current_line_num()
1215+
return current_line(filename, lineno)
12071216

12081217
def write_repr(self, out, visited):
12091218
if self.is_optimized_out():
@@ -2072,7 +2081,6 @@ def __init__(self):
20722081
gdb.COMMAND_STACK,
20732082
gdb.COMPLETE_NONE)
20742083

2075-
20762084
def invoke(self, args, from_tty):
20772085
frame = Frame.get_selected_python_frame()
20782086
if not frame:
@@ -2087,6 +2095,46 @@ def invoke(self, args, from_tty):
20872095

20882096
PyBacktrace()
20892097

2098+
class PyBacktraceTSS(gdb.Command):
2099+
'Similar to py-bt but read _Py_tss_gilstate or _Py_tss_tstate'
2100+
def __init__(self):
2101+
gdb.Command.__init__ (self,
2102+
"py-bt-tss",
2103+
gdb.COMMAND_STACK,
2104+
gdb.COMPLETE_NONE)
2105+
2106+
def invoke(self, args, from_tty):
2107+
try:
2108+
iframe = gdb.parse_and_eval('_Py_tss_gilstate->current_frame')
2109+
except gdb.error:
2110+
iframe = gdb.parse_and_eval('_Py_tss_tstate->current_frame')
2111+
visited = set()
2112+
2113+
print('Traceback (most recent call first):')
2114+
while True:
2115+
is_complete = not _PyFrame_IsIncomplete(iframe)
2116+
if is_complete:
2117+
code = _PyFrame_GetCode(iframe)
2118+
code = PyCodeObjectPtr.from_pyobject_ptr(code)
2119+
2120+
filename = code.pyop_field('co_filename')
2121+
filename = filename.proxyval(visited)
2122+
lasti = iframe['instr_ptr'] - _PyFrame_GetBytecode(iframe)
2123+
lineno = code.addr2line(lasti)
2124+
name = code.pyop_field('co_name')
2125+
name = name.proxyval(visited)
2126+
print(' File "%s", line %s, in %s'
2127+
% (filename, lineno, name))
2128+
line = current_line(filename, lineno)
2129+
if line is not None:
2130+
sys.stdout.write(' %s\n' % line.strip())
2131+
2132+
iframe = iframe['previous']
2133+
if not iframe:
2134+
break
2135+
2136+
PyBacktraceTSS()
2137+
20902138
class PyPrint(gdb.Command):
20912139
'Look up the given python variable name, and print it'
20922140
def __init__(self):
@@ -2156,4 +2204,38 @@ def invoke(self, args, from_tty):
21562204

21572205
pyop_frame = pyop_frame.previous()
21582206

2207+
2208+
def _PyCode_CODE(code):
2209+
cast_to = gdb.lookup_type('PyCodeObject').pointer()
2210+
code = code.cast(cast_to)
2211+
cast_to = gdb.lookup_type('_Py_CODEUNIT').pointer()
2212+
return code['co_code_adaptive'].cast(cast_to)
2213+
2214+
def _PyFrame_GetCode(iframe):
2215+
executable = iframe['f_executable']
2216+
try:
2217+
# Python 3.14 and newer: f_executable is a _PyStackRef
2218+
return _PyStackRef_AsPyObjectBorrow(executable)
2219+
except gdb.error:
2220+
# Python 3.13: f_executable is a PyCodeObject*
2221+
return executable
2222+
2223+
def _PyFrame_GetBytecode(iframe):
2224+
# FIXME: #ifdef Py_GIL_DISABLED
2225+
# PyCodeObject *co = _PyFrame_GetCode(f);
2226+
# _PyCodeArray *tlbc = _PyCode_GetTLBCArray(co);
2227+
# assert(f->tlbc_index >= 0 && f->tlbc_index < tlbc->size);
2228+
# return (_Py_CODEUNIT *)tlbc->entries[f->tlbc_index];
2229+
return _PyCode_CODE(_PyFrame_GetCode(iframe))
2230+
2231+
def _PyFrame_IsIncomplete(iframe):
2232+
if iframe['owner'] >= FRAME_OWNED_BY_INTERPRETER:
2233+
return True
2234+
# FIXME:
2235+
# return frame->owner != FRAME_OWNED_BY_GENERATOR &&
2236+
# frame->instr_ptr < _PyFrame_GetBytecode(frame) +
2237+
# _PyFrame_GetCode(frame)->_co_firsttraceable;
2238+
return False
2239+
2240+
21592241
PyLocals()

0 commit comments

Comments
 (0)