Skip to content

Commit

Permalink
interpreter: add support for Python 3.13 (open-telemetry#190)
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Lehner <dev@der-flo.net>
Signed-off-by: Florian Lehner <florian.lehner@elastic.co>
  • Loading branch information
florianl authored Oct 16, 2024
1 parent 13a01a8 commit b00b572
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
15 changes: 12 additions & 3 deletions interpreter/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
77 changes: 77 additions & 0 deletions tools/coredump/testsources/python/gdb-dump-offsets.py
Original file line number Diff line number Diff line change
@@ -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}: <field not present>")
else:
print(f"vms.{field}: dec={value} hex=0x{value:x}")

0 comments on commit b00b572

Please sign in to comment.