@@ -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 :
@@ -1034,30 +1039,49 @@ def write_repr(self, out, visited):
10341039 return
10351040 return self ._frame .write_repr (out , visited )
10361041
1037- def print_traceback (self ):
1038- if self .is_optimized_out ():
1039- sys .stdout .write (' %s\n ' % FRAME_INFO_OPTIMIZED_OUT )
1040- return
1041- return self ._frame .print_traceback ()
1042-
10431042class PyFramePtr :
10441043
10451044 def __init__ (self , gdbval ):
10461045 self ._gdbval = gdbval
1046+ if self .is_optimized_out ():
1047+ return
1048+ self .co = self ._f_code ()
1049+ if self .is_shim ():
1050+ return
1051+ self .co_name = self .co .pyop_field ('co_name' )
1052+ self .co_filename = self .co .pyop_field ('co_filename' )
10471053
1048- if not self .is_optimized_out ():
1054+ self .f_lasti = self ._f_lasti ()
1055+ self .co_nlocals = int_from_int (self .co .field ('co_nlocals' ))
1056+ pnames = self .co .field ('co_localsplusnames' )
1057+ self .co_localsplusnames = PyTupleObjectPtr .from_pyobject_ptr (pnames )
1058+
1059+ @staticmethod
1060+ def get_thread_state ():
1061+ exprs = [
1062+ '_Py_tss_gilstate' , # 3.15+
1063+ '_Py_tss_tstate' , # 3.12+ (and not when GIL is released)
1064+ 'pthread_getspecific(_PyRuntime.autoTSSkey._key)' , # only live programs
1065+ '((struct pthread*)$fs_base)->specific_1stblock[_PyRuntime.autoTSSkey._key].data' # x86-64
1066+ ]
1067+ for expr in exprs :
10491068 try :
1050- self .co = self ._f_code ()
1051- self .co_name = self .co .pyop_field ('co_name' )
1052- self .co_filename = self .co .pyop_field ('co_filename' )
1053-
1054- self .f_lasti = self ._f_lasti ()
1055- self .co_nlocals = int_from_int (self .co .field ('co_nlocals' ))
1056- pnames = self .co .field ('co_localsplusnames' )
1057- self .co_localsplusnames = PyTupleObjectPtr .from_pyobject_ptr (pnames )
1058- self ._is_code = True
1059- except :
1060- self ._is_code = False
1069+ val = gdb .parse_and_eval (f'(PyThreadState*)({ expr } )' )
1070+ except gdb .error :
1071+ continue
1072+ if int (val ) != 0 :
1073+ return val
1074+ return None
1075+
1076+ @staticmethod
1077+ def get_thread_local_frame ():
1078+ thread_state = PyFramePtr .get_thread_state ()
1079+ if thread_state is None :
1080+ return None
1081+ current_frame = thread_state ['current_frame' ]
1082+ if int (current_frame ) == 0 :
1083+ return None
1084+ return PyFramePtr (current_frame )
10611085
10621086 def is_optimized_out (self ):
10631087 return self ._gdbval .is_optimized_out
@@ -1115,6 +1139,8 @@ def is_shim(self):
11151139 return self ._f_special ("owner" , int ) == FRAME_OWNED_BY_INTERPRETER
11161140
11171141 def previous (self ):
1142+ if int (self ._gdbval ['previous' ]) == 0 :
1143+ return None
11181144 return self ._f_special ("previous" , PyFramePtr )
11191145
11201146 def iter_globals (self ):
@@ -1243,6 +1269,27 @@ def print_traceback(self):
12431269 lineno ,
12441270 self .co_name .proxyval (visited )))
12451271
1272+ def print_traceback_until_shim (self , frame_index = None ):
1273+ # Print traceback for _PyInterpreterFrame and return previous frame
1274+ interp_frame = self
1275+ while True :
1276+ if not interp_frame :
1277+ sys .stdout .write (' (unable to read python frame information)\n ' )
1278+ return None
1279+ if interp_frame .is_shim ():
1280+ return interp_frame .previous ()
1281+
1282+ if frame_index is not None :
1283+ line = interp_frame .get_truncated_repr (MAX_OUTPUT_LEN )
1284+ sys .stdout .write ('#%i %s\n ' % (frame_index , line ))
1285+ else :
1286+ interp_frame .print_traceback ()
1287+ if not interp_frame .is_optimized_out ():
1288+ line = interp_frame .current_line ()
1289+ if line is not None :
1290+ sys .stdout .write (' %s\n ' % line .strip ())
1291+ interp_frame = interp_frame .previous ()
1292+
12461293 def get_truncated_repr (self , maxlen ):
12471294 '''
12481295 Get a repr-like string for the data, but truncate it at "maxlen" bytes
@@ -1855,50 +1902,17 @@ def get_selected_bytecode_frame(cls):
18551902 def print_summary (self ):
18561903 if self .is_evalframe ():
18571904 interp_frame = self .get_pyop ()
1858- while True :
1859- if interp_frame :
1860- if interp_frame .is_shim ():
1861- break
1862- line = interp_frame .get_truncated_repr (MAX_OUTPUT_LEN )
1863- sys .stdout .write ('#%i %s\n ' % (self .get_index (), line ))
1864- if not interp_frame .is_optimized_out ():
1865- line = interp_frame .current_line ()
1866- if line is not None :
1867- sys .stdout .write (' %s\n ' % line .strip ())
1868- else :
1869- sys .stdout .write ('#%i (unable to read python frame information)\n ' % self .get_index ())
1870- break
1871- interp_frame = interp_frame .previous ()
1905+ if interp_frame :
1906+ interp_frame .print_traceback_until_shim (self .get_index ())
1907+ else :
1908+ sys .stdout .write ('#%i (unable to read python frame information)\n ' % self .get_index ())
18721909 else :
18731910 info = self .is_other_python_frame ()
18741911 if info :
18751912 sys .stdout .write ('#%i %s\n ' % (self .get_index (), info ))
18761913 else :
18771914 sys .stdout .write ('#%i\n ' % self .get_index ())
18781915
1879- def print_traceback (self ):
1880- if self .is_evalframe ():
1881- interp_frame = self .get_pyop ()
1882- while True :
1883- if interp_frame :
1884- if interp_frame .is_shim ():
1885- break
1886- interp_frame .print_traceback ()
1887- if not interp_frame .is_optimized_out ():
1888- line = interp_frame .current_line ()
1889- if line is not None :
1890- sys .stdout .write (' %s\n ' % line .strip ())
1891- else :
1892- sys .stdout .write (' (unable to read python frame information)\n ' )
1893- break
1894- interp_frame = interp_frame .previous ()
1895- else :
1896- info = self .is_other_python_frame ()
1897- if info :
1898- sys .stdout .write (' %s\n ' % info )
1899- else :
1900- sys .stdout .write (' (not a python frame)\n ' )
1901-
19021916class PyList (gdb .Command ):
19031917 '''List the current Python source code, if any
19041918
@@ -2042,6 +2056,41 @@ def invoke(self, args, from_tty):
20422056 PyUp ()
20432057 PyDown ()
20442058
2059+
2060+ def print_traceback_helper (full_info ):
2061+ frame = Frame .get_selected_python_frame ()
2062+ interp_frame = PyFramePtr .get_thread_local_frame ()
2063+ if not frame and not interp_frame :
2064+ print ('Unable to locate python frame' )
2065+ return
2066+
2067+ sys .stdout .write ('Traceback (most recent call first):\n ' )
2068+ if frame :
2069+ while frame :
2070+ frame_index = frame .get_index () if full_info else None
2071+ if frame .is_evalframe ():
2072+ pyop = frame .get_pyop ()
2073+ if pyop is not None :
2074+ # Use the _PyInterpreterFrame from the gdb frame
2075+ interp_frame = pyop
2076+ if interp_frame :
2077+ interp_frame = interp_frame .print_traceback_until_shim (frame_index )
2078+ else :
2079+ sys .stdout .write (' (unable to read python frame information)\n ' )
2080+ else :
2081+ info = frame .is_other_python_frame ()
2082+ if full_info :
2083+ if info :
2084+ sys .stdout .write ('#%i %s\n ' % (frame_index , info ))
2085+ elif info :
2086+ sys .stdout .write (' %s\n ' % info )
2087+ frame = frame .older ()
2088+ else :
2089+ # Fall back to just using the thread-local frame
2090+ while interp_frame :
2091+ interp_frame = interp_frame .print_traceback_until_shim ()
2092+
2093+
20452094class PyBacktraceFull (gdb .Command ):
20462095 'Display the current python frame and all the frames within its call stack (if any)'
20472096 def __init__ (self ):
@@ -2052,15 +2101,7 @@ def __init__(self):
20522101
20532102
20542103 def invoke (self , args , from_tty ):
2055- frame = Frame .get_selected_python_frame ()
2056- if not frame :
2057- print ('Unable to locate python frame' )
2058- return
2059-
2060- while frame :
2061- if frame .is_python_frame ():
2062- frame .print_summary ()
2063- frame = frame .older ()
2104+ print_traceback_helper (full_info = True )
20642105
20652106PyBacktraceFull ()
20662107
@@ -2072,18 +2113,8 @@ def __init__(self):
20722113 gdb .COMMAND_STACK ,
20732114 gdb .COMPLETE_NONE )
20742115
2075-
20762116 def invoke (self , args , from_tty ):
2077- frame = Frame .get_selected_python_frame ()
2078- if not frame :
2079- print ('Unable to locate python frame' )
2080- return
2081-
2082- sys .stdout .write ('Traceback (most recent call first):\n ' )
2083- while frame :
2084- if frame .is_python_frame ():
2085- frame .print_traceback ()
2086- frame = frame .older ()
2117+ print_traceback_helper (full_info = False )
20872118
20882119PyBacktrace ()
20892120
0 commit comments