@@ -144,6 +144,7 @@ def write(self, data):
144144 def getvalue (self ):
145145 return self ._val
146146
147+
147148class PyObjectPtr (object ):
148149 """
149150 Class wrapping a gdb.Value that's either a (PyObject*) within the
@@ -1012,30 +1013,49 @@ def write_repr(self, out, visited):
10121013 return
10131014 return self ._frame .write_repr (out , visited )
10141015
1015- def print_traceback (self ):
1016- if self .is_optimized_out ():
1017- sys .stdout .write (' %s\n ' % FRAME_INFO_OPTIMIZED_OUT )
1018- return
1019- return self ._frame .print_traceback ()
1020-
10211016class PyFramePtr :
10221017
10231018 def __init__ (self , gdbval ):
10241019 self ._gdbval = gdbval
1020+ if self .is_optimized_out ():
1021+ return
1022+ self .co = self ._f_code ()
1023+ if self .is_shim ():
1024+ return
1025+ self .co_name = self .co .pyop_field ('co_name' )
1026+ self .co_filename = self .co .pyop_field ('co_filename' )
10251027
1026- if not self .is_optimized_out ():
1028+ self .f_lasti = self ._f_lasti ()
1029+ self .co_nlocals = int_from_int (self .co .field ('co_nlocals' ))
1030+ pnames = self .co .field ('co_localsplusnames' )
1031+ self .co_localsplusnames = PyTupleObjectPtr .from_pyobject_ptr (pnames )
1032+
1033+ @staticmethod
1034+ def get_thread_state ():
1035+ exprs = [
1036+ '_Py_tss_gilstate' , # 3.15+
1037+ '_Py_tss_tstate' , # 3.12+ (and not when GIL is released)
1038+ 'pthread_getspecific(_PyRuntime.autoTSSkey._key)' , # only live programs
1039+ '((struct pthread*)$fs_base)->specific_1stblock[_PyRuntime.autoTSSkey._key].data' # x86-64
1040+ ]
1041+ for expr in exprs :
10271042 try :
1028- self .co = self ._f_code ()
1029- self .co_name = self .co .pyop_field ('co_name' )
1030- self .co_filename = self .co .pyop_field ('co_filename' )
1031-
1032- self .f_lasti = self ._f_lasti ()
1033- self .co_nlocals = int_from_int (self .co .field ('co_nlocals' ))
1034- pnames = self .co .field ('co_localsplusnames' )
1035- self .co_localsplusnames = PyTupleObjectPtr .from_pyobject_ptr (pnames )
1036- self ._is_code = True
1037- except :
1038- self ._is_code = False
1043+ val = gdb .parse_and_eval (f'(PyThreadState*)({ expr } )' )
1044+ except gdb .error :
1045+ continue
1046+ if int (val ) != 0 :
1047+ return val
1048+ return None
1049+
1050+ @staticmethod
1051+ def get_thread_local_frame ():
1052+ thread_state = PyFramePtr .get_thread_state ()
1053+ if thread_state is None :
1054+ return None
1055+ current_frame = thread_state ['current_frame' ]
1056+ if int (current_frame ) == 0 :
1057+ return None
1058+ return PyFramePtr (current_frame )
10391059
10401060 def is_optimized_out (self ):
10411061 return self ._gdbval .is_optimized_out
@@ -1088,6 +1108,8 @@ def is_shim(self):
10881108 return self ._f_special ("owner" , int ) == FRAME_OWNED_BY_CSTACK
10891109
10901110 def previous (self ):
1111+ if int (self ._gdbval ['previous' ]) == 0 :
1112+ return None
10911113 return self ._f_special ("previous" , PyFramePtr )
10921114
10931115 def iter_globals (self ):
@@ -1216,6 +1238,27 @@ def print_traceback(self):
12161238 lineno ,
12171239 self .co_name .proxyval (visited )))
12181240
1241+ def print_traceback_until_shim (self , frame_index = None ):
1242+ # Print traceback for _PyInterpreterFrame and return previous frame
1243+ interp_frame = self
1244+ while True :
1245+ if not interp_frame :
1246+ sys .stdout .write (' (unable to read python frame information)\n ' )
1247+ return None
1248+ if interp_frame .is_shim ():
1249+ return interp_frame .previous ()
1250+
1251+ if frame_index is not None :
1252+ line = interp_frame .get_truncated_repr (MAX_OUTPUT_LEN )
1253+ sys .stdout .write ('#%i %s\n ' % (frame_index , line ))
1254+ else :
1255+ interp_frame .print_traceback ()
1256+ if not interp_frame .is_optimized_out ():
1257+ line = interp_frame .current_line ()
1258+ if line is not None :
1259+ sys .stdout .write (' %s\n ' % line .strip ())
1260+ interp_frame = interp_frame .previous ()
1261+
12191262 def get_truncated_repr (self , maxlen ):
12201263 '''
12211264 Get a repr-like string for the data, but truncate it at "maxlen" bytes
@@ -1825,50 +1868,17 @@ def get_selected_bytecode_frame(cls):
18251868 def print_summary (self ):
18261869 if self .is_evalframe ():
18271870 interp_frame = self .get_pyop ()
1828- while True :
1829- if interp_frame :
1830- if interp_frame .is_shim ():
1831- break
1832- line = interp_frame .get_truncated_repr (MAX_OUTPUT_LEN )
1833- sys .stdout .write ('#%i %s\n ' % (self .get_index (), line ))
1834- if not interp_frame .is_optimized_out ():
1835- line = interp_frame .current_line ()
1836- if line is not None :
1837- sys .stdout .write (' %s\n ' % line .strip ())
1838- else :
1839- sys .stdout .write ('#%i (unable to read python frame information)\n ' % self .get_index ())
1840- break
1841- interp_frame = interp_frame .previous ()
1871+ if interp_frame :
1872+ interp_frame .print_traceback_until_shim (self .get_index ())
1873+ else :
1874+ sys .stdout .write ('#%i (unable to read python frame information)\n ' % self .get_index ())
18421875 else :
18431876 info = self .is_other_python_frame ()
18441877 if info :
18451878 sys .stdout .write ('#%i %s\n ' % (self .get_index (), info ))
18461879 else :
18471880 sys .stdout .write ('#%i\n ' % self .get_index ())
18481881
1849- def print_traceback (self ):
1850- if self .is_evalframe ():
1851- interp_frame = self .get_pyop ()
1852- while True :
1853- if interp_frame :
1854- if interp_frame .is_shim ():
1855- break
1856- interp_frame .print_traceback ()
1857- if not interp_frame .is_optimized_out ():
1858- line = interp_frame .current_line ()
1859- if line is not None :
1860- sys .stdout .write (' %s\n ' % line .strip ())
1861- else :
1862- sys .stdout .write (' (unable to read python frame information)\n ' )
1863- break
1864- interp_frame = interp_frame .previous ()
1865- else :
1866- info = self .is_other_python_frame ()
1867- if info :
1868- sys .stdout .write (' %s\n ' % info )
1869- else :
1870- sys .stdout .write (' (not a python frame)\n ' )
1871-
18721882class PyList (gdb .Command ):
18731883 '''List the current Python source code, if any
18741884
@@ -2012,6 +2022,41 @@ def invoke(self, args, from_tty):
20122022 PyUp ()
20132023 PyDown ()
20142024
2025+
2026+ def print_traceback_helper (full_info ):
2027+ frame = Frame .get_selected_python_frame ()
2028+ interp_frame = PyFramePtr .get_thread_local_frame ()
2029+ if not frame and not interp_frame :
2030+ print ('Unable to locate python frame' )
2031+ return
2032+
2033+ sys .stdout .write ('Traceback (most recent call first):\n ' )
2034+ if frame :
2035+ while frame :
2036+ frame_index = frame .get_index () if full_info else None
2037+ if frame .is_evalframe ():
2038+ pyop = frame .get_pyop ()
2039+ if pyop is not None :
2040+ # Use the _PyInterpreterFrame from the gdb frame
2041+ interp_frame = pyop
2042+ if interp_frame :
2043+ interp_frame = interp_frame .print_traceback_until_shim (frame_index )
2044+ else :
2045+ sys .stdout .write (' (unable to read python frame information)\n ' )
2046+ else :
2047+ info = frame .is_other_python_frame ()
2048+ if full_info :
2049+ if info :
2050+ sys .stdout .write ('#%i %s\n ' % (frame_index , info ))
2051+ elif info :
2052+ sys .stdout .write (' %s\n ' % info )
2053+ frame = frame .older ()
2054+ else :
2055+ # Fall back to just using the thread-local frame
2056+ while interp_frame :
2057+ interp_frame = interp_frame .print_traceback_until_shim ()
2058+
2059+
20152060class PyBacktraceFull (gdb .Command ):
20162061 'Display the current python frame and all the frames within its call stack (if any)'
20172062 def __init__ (self ):
@@ -2022,15 +2067,7 @@ def __init__(self):
20222067
20232068
20242069 def invoke (self , args , from_tty ):
2025- frame = Frame .get_selected_python_frame ()
2026- if not frame :
2027- print ('Unable to locate python frame' )
2028- return
2029-
2030- while frame :
2031- if frame .is_python_frame ():
2032- frame .print_summary ()
2033- frame = frame .older ()
2070+ print_traceback_helper (full_info = True )
20342071
20352072PyBacktraceFull ()
20362073
@@ -2042,18 +2079,8 @@ def __init__(self):
20422079 gdb .COMMAND_STACK ,
20432080 gdb .COMPLETE_NONE )
20442081
2045-
20462082 def invoke (self , args , from_tty ):
2047- frame = Frame .get_selected_python_frame ()
2048- if not frame :
2049- print ('Unable to locate python frame' )
2050- return
2051-
2052- sys .stdout .write ('Traceback (most recent call first):\n ' )
2053- while frame :
2054- if frame .is_python_frame ():
2055- frame .print_traceback ()
2056- frame = frame .older ()
2083+ print_traceback_helper (full_info = False )
20572084
20582085PyBacktrace ()
20592086
0 commit comments