@@ -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+
155160class 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+
10431065class 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
20882096PyBacktrace ()
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+
20902138class 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+
21592241PyLocals ()
0 commit comments