Skip to content

Commit b444bfb

Browse files
authoredJul 11, 2023
gh-106597: Add debugging struct with offsets for out-of-process tools (#106598)
1 parent 579aa89 commit b444bfb

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed
 

Diff for: ‎Include/internal/pycore_runtime.h

+89
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,101 @@ typedef struct _Py_AuditHookEntry {
5353
void *userData;
5454
} _Py_AuditHookEntry;
5555

56+
typedef struct _Py_DebugOffsets {
57+
char cookie[8];
58+
uint64_t version;
59+
// Runtime state offset;
60+
struct _runtime_state {
61+
off_t finalizing;
62+
off_t interpreters_head;
63+
} runtime_state;
64+
65+
// Interpreter state offset;
66+
struct _interpreter_state {
67+
off_t next;
68+
off_t threads_head;
69+
off_t gc;
70+
off_t imports_modules;
71+
off_t sysdict;
72+
off_t builtins;
73+
off_t ceval_gil;
74+
off_t gil_runtime_state_locked;
75+
off_t gil_runtime_state_holder;
76+
} interpreter_state;
77+
78+
// Thread state offset;
79+
struct _thread_state{
80+
off_t prev;
81+
off_t next;
82+
off_t interp;
83+
off_t cframe;
84+
off_t thread_id;
85+
off_t native_thread_id;
86+
} thread_state;
87+
88+
// InterpreterFrame offset;
89+
struct _interpreter_frame {
90+
off_t previous;
91+
off_t executable;
92+
off_t prev_instr;
93+
off_t localsplus;
94+
off_t owner;
95+
} interpreter_frame;
96+
97+
// CFrame offset;
98+
struct _cframe {
99+
off_t current_frame;
100+
off_t previous;
101+
} cframe;
102+
103+
// Code object offset;
104+
struct _code_object {
105+
off_t filename;
106+
off_t name;
107+
off_t linetable;
108+
off_t firstlineno;
109+
off_t argcount;
110+
off_t localsplusnames;
111+
off_t localspluskinds;
112+
off_t co_code_adaptive;
113+
} code_object;
114+
115+
// PyObject offset;
116+
struct _pyobject {
117+
off_t ob_type;
118+
} pyobject;
119+
120+
// PyTypeObject object offset;
121+
struct _type_object {
122+
off_t tp_name;
123+
} type_object;
124+
125+
// PyTuple object offset;
126+
struct _tuple_object {
127+
off_t ob_item;
128+
} tuple_object;
129+
} _Py_DebugOffsets;
130+
56131
/* Full Python runtime state */
57132

58133
/* _PyRuntimeState holds the global state for the CPython runtime.
59134
That data is exposed in the internal API as a static variable (_PyRuntime).
60135
*/
61136
typedef struct pyruntimestate {
137+
/* This field must be first to facilitate locating it by out of process
138+
* debuggers. Out of process debuggers will use the offsets contained in this
139+
* field to be able to locate other fields in several interpreter structures
140+
* in a way that doesn't require them to know the exact layout of those
141+
* structures.
142+
*
143+
* IMPORTANT:
144+
* This struct is **NOT** backwards compatible between minor version of the
145+
* interpreter and the members, order of members and size can change between
146+
* minor versions. This struct is only guaranteed to be stable between patch
147+
* versions for a given minor version of the interpreter.
148+
*/
149+
_Py_DebugOffsets debug_offsets;
150+
62151
/* Has been initialized to a safe state.
63152
64153
In order to be effective, this must be set to 0 during or right

Diff for: ‎Include/internal/pycore_runtime_init.h

+57-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,65 @@ extern PyTypeObject _PyExc_MemoryError;
2121
/* The static initializers defined here should only be used
2222
in the runtime init code (in pystate.c and pylifecycle.c). */
2323

24-
2524
#define _PyRuntimeState_INIT(runtime) \
2625
{ \
26+
.debug_offsets = { \
27+
.cookie = "xdebugpy", \
28+
.version = PY_VERSION_HEX, \
29+
.runtime_state = { \
30+
.finalizing = offsetof(_PyRuntimeState, _finalizing), \
31+
.interpreters_head = offsetof(_PyRuntimeState, interpreters.head), \
32+
}, \
33+
.interpreter_state = { \
34+
.next = offsetof(PyInterpreterState, next), \
35+
.threads_head = offsetof(PyInterpreterState, threads.head), \
36+
.gc = offsetof(PyInterpreterState, gc), \
37+
.imports_modules = offsetof(PyInterpreterState, imports.modules), \
38+
.sysdict = offsetof(PyInterpreterState, sysdict), \
39+
.builtins = offsetof(PyInterpreterState, builtins), \
40+
.ceval_gil = offsetof(PyInterpreterState, ceval.gil), \
41+
.gil_runtime_state_locked = offsetof(PyInterpreterState, _gil.locked), \
42+
.gil_runtime_state_holder = offsetof(PyInterpreterState, _gil.last_holder), \
43+
}, \
44+
.thread_state = { \
45+
.prev = offsetof(PyThreadState, prev), \
46+
.next = offsetof(PyThreadState, next), \
47+
.interp = offsetof(PyThreadState, interp), \
48+
.cframe = offsetof(PyThreadState, cframe), \
49+
.thread_id = offsetof(PyThreadState, thread_id), \
50+
.native_thread_id = offsetof(PyThreadState, native_thread_id), \
51+
}, \
52+
.interpreter_frame = { \
53+
.previous = offsetof(_PyInterpreterFrame, previous), \
54+
.executable = offsetof(_PyInterpreterFrame, f_executable), \
55+
.prev_instr = offsetof(_PyInterpreterFrame, prev_instr), \
56+
.localsplus = offsetof(_PyInterpreterFrame, localsplus), \
57+
.owner = offsetof(_PyInterpreterFrame, owner), \
58+
}, \
59+
.cframe = { \
60+
.current_frame = offsetof(_PyCFrame, current_frame), \
61+
.previous = offsetof(_PyCFrame, previous), \
62+
}, \
63+
.code_object = { \
64+
.filename = offsetof(PyCodeObject, co_filename), \
65+
.name = offsetof(PyCodeObject, co_name), \
66+
.linetable = offsetof(PyCodeObject, co_linetable), \
67+
.firstlineno = offsetof(PyCodeObject, co_firstlineno), \
68+
.argcount = offsetof(PyCodeObject, co_argcount), \
69+
.localsplusnames = offsetof(PyCodeObject, co_localsplusnames), \
70+
.localspluskinds = offsetof(PyCodeObject, co_localspluskinds), \
71+
.co_code_adaptive = offsetof(PyCodeObject, co_code_adaptive), \
72+
}, \
73+
.pyobject = { \
74+
.ob_type = offsetof(PyObject, ob_type), \
75+
}, \
76+
.type_object = { \
77+
.tp_name = offsetof(PyTypeObject, tp_name), \
78+
}, \
79+
.tuple_object = { \
80+
.ob_item = offsetof(PyTupleObject, ob_item), \
81+
}, \
82+
}, \
2783
.allocators = { \
2884
.standard = _pymem_allocators_standard_INIT(runtime), \
2985
.debug = _pymem_allocators_debug_INIT, \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
A new debug structure of offsets has been added to the ``_PyRuntimeState``
2+
that will help out-of-process debuggers and profilers to obtain the offsets
3+
to relevant interpreter structures in a way that is agnostic of how Python
4+
was compiled and that doesn't require copying the headers. Patch by Pablo
5+
Galindo

0 commit comments

Comments
 (0)
Please sign in to comment.