diff --git a/interpreter/python/python.go b/interpreter/python/python.go index 13544f72..c73813ea 100644 --- a/interpreter/python/python.go +++ b/interpreter/python/python.go @@ -718,7 +718,7 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr version := pythonVer(major, minor) minVer := pythonVer(3, 6) - maxVer := pythonVer(3, 12) + maxVer := pythonVer(3, 13) if version < minVer || version > maxVer { return nil, fmt.Errorf("unsupported Python %d.%d (need >= %d.%d and <= %d.%d)", major, minor, @@ -753,8 +753,8 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr // The Python main interpreter loop history in CPython git is: // //nolint:lll - // deaf509e8fc v3.11 2022-11-15 _PyEval_EvalFrameDefault(PyThreadState*,_PyInterpreterFrame*,int) - // bc2cdfc8157 v3.10 2022-11-15 _PyEval_EvalFrameDefault(PyThreadState*,PyFrameObject*,int) + // 87af12bff33 v3.11 2022-02-15 _PyEval_EvalFrameDefault(PyThreadState*,_PyInterpreterFrame*,int) + // ae0a2b75625 v3.10 2021-06-25 _PyEval_EvalFrameDefault(PyThreadState*,_interpreter_frame*,int) // 0b72b23fb0c v3.9 2020-03-12 _PyEval_EvalFrameDefault(PyThreadState*,PyFrameObject*,int) // 3cebf938727 v3.6 2016-09-05 _PyEval_EvalFrameDefault(PyFrameObject*,int) // 49fd7fa4431 v3.0 2006-04-21 PyEval_EvalFrameEx(PyFrameObject*,int) @@ -808,6 +808,15 @@ func Loader(ebpf interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpr vms.PyThreadState.Frame = 56 vms.PyCFrame.CurrentFrame = 0 vms.PyASCIIObject.Data = 40 + case pythonVer(3, 13): + vms.PyFrameObject.Code = 0 + vms.PyFrameObject.LastI = 56 // _Py_CODEUNIT *prev_instr + vms.PyFrameObject.Back = 8 // struct _PyInterpreterFrame *previous + vms.PyFrameObject.EntryMember = 70 // char owner + vms.PyFrameObject.EntryVal = 3 // enum _frameowner, FRAME_OWNED_BY_CSTACK + vms.PyThreadState.Frame = 72 + vms.PyCFrame.CurrentFrame = 8 + vms.PyASCIIObject.Data = 40 } // Read the introspection data from objects types that have it diff --git a/tools/coredump/testsources/python/gdb-dump-offsets.py b/tools/coredump/testsources/python/gdb-dump-offsets.py new file mode 100644 index 00000000..15926508 --- /dev/null +++ b/tools/coredump/testsources/python/gdb-dump-offsets.py @@ -0,0 +1,77 @@ +""" +gdb python script for dumping Python offsets. +""" + +def no_member_to_none(fn): + """Decorator translating errors about missing field to `None`.""" + def wrap(*args, **kwargs): + try: + return fn(*args, **kwargs) + except gdb.error as e: + if 'no member named' in str(e): + return None + raise + return wrap + +@no_member_to_none +def offset_of(ty, field): + for ns in ['', 'struct ', 'union ']: + try: + return int(gdb.parse_and_eval(f'(uintptr_t)(&(({ns}{ty}*)0)->{field})')) + except gdb.error: + pass + return None + +@no_member_to_none +def size_of(ty): + for ns in ['', 'struct ', 'union ']: + try: + return int(gdb.parse_and_eval(f'sizeof({ns} {ty})')) + except gdb.error: + pass + return None + +fields = { + 'PyTypeObject.BasicSize': offset_of('_typeobject', 'tp_basicsize'), + 'PyTypeObject.Members': offset_of('_typeobject', 'tp_members'), + + 'PyMemberDef.Sizeof': size_of('PyMemberDef'), + 'PyMemberDef.Name': offset_of('PyMemberDef', 'name'), + 'PyMemberDef.Offset': offset_of('PyMemberDef', 'offset'), + + 'PyASCIIObject.Data': offset_of('PyASCIIObject', 'data'), + + 'PyCodeObject.Sizeof': size_of('PyCodeObject'), + 'PyCodeObject.ArgCount': offset_of('PyCodeObject', 'co_argcount'), + 'PyCodeObject.KwOnlyArgCount': offset_of('PyCodeObject', 'co_kwonlyargcount'), + 'PyCodeObject.Flags': offset_of('PyCodeObject', 'co_flags'), + 'PyCodeObject.FirstLineno': offset_of('PyCodeObject', 'co_firstlineno'), + 'PyCodeObject.Filename': offset_of('PyCodeObject', 'co_filename'), + 'PyCodeObject.Name': offset_of('PyCodeObject', 'co_name'), + 'PyCodeObject.Lnotab': offset_of('PyCodeObject', 'co_lnotab'), + 'PyCodeObject.Linetable': offset_of('PyCodeObject', 'co_linetable'), + 'PyCodeObject.QualName': offset_of('PyCodeObject', 'co_qualname'), + + 'PyVarObject.ObSize': offset_of('PyVarObject', 'ob_size'), + + 'PyBytesObject.Sizeof': size_of('PyBytesObject'), + + 'PyThreadState.Frame': offset_of('_ts', 'frame'), + 'PyThreadState.cFrame': offset_of('_ts', 'current_frame'), + + 'PyFrameObject.Back': offset_of('PyFrameObject', 'f_back'), + 'PyFrameObject.Code': offset_of('PyFrameObject', 'f_code'), + 'PyFrameObject.LastI': offset_of('PyFrameObject', 'f_lasti'), + + 'PyInterpreterFrame.Previous': offset_of('_PyInterpreterFrame', 'previous'), + 'PyInterpreterFrame.Prev_Instr': offset_of('_PyInterpreterFrame', 'prev_instr'), + 'PyInterpreterFrame.Owner': offset_of('_PyInterpreterFrame', 'owner'), + + 'PyCFrame.CurrentFrame': offset_of('_PyCFrame', 'current_frame'), +} + +for field, value in fields.items(): + if value is None: + print(f"vms.{field}: ") + else: + print(f"vms.{field}: dec={value} hex=0x{value:x}")