diff --git a/pyproject.toml b/pyproject.toml index c7b951c8..b80cb06c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Debuggers", ] diff --git a/src/pystack/_pystack/cpython/code.h b/src/pystack/_pystack/cpython/code.h index d837c66b..5069ffe7 100644 --- a/src/pystack/_pystack/cpython/code.h +++ b/src/pystack/_pystack/cpython/code.h @@ -197,82 +197,4 @@ typedef struct } PyCodeObject; } // namespace Python3_13 -namespace Python3_14 { -typedef uint16_t _Py_CODEUNIT; - -typedef struct -{ - PyObject_VAR_HEAD PyObject* co_consts; - PyObject* co_names; - PyObject* co_exceptiontable; - int co_flags; - int co_argcount; - int co_posonlyargcount; - int co_kwonlyargcount; - int co_stacksize; - int co_firstlineno; - int co_nlocalsplus; - int co_framesize; - int co_nlocals; - int co_ncellvars; - int co_nfreevars; - uint32_t co_version; - PyObject* co_localsplusnames; - PyObject* co_localspluskinds; - PyObject* co_filename; - PyObject* co_name; - PyObject* co_qualname; - PyObject* co_linetable; - PyObject* co_weakreflist; - void* co_executors; - void* _co_cached; - uintptr_t _co_instrumentation_version; - void* _co_monitoring; - Py_ssize_t _co_unique_id; - int _co_firsttraceable; - void* co_extra; - /* deal with co_tlbc somehow */ - char co_code_adaptive[1]; -} PyCodeObject; -} // namespace Python3_14 - -namespace Python3_15 { -typedef uint16_t _Py_CODEUNIT; - -typedef struct -{ - PyObject_VAR_HEAD PyObject* co_consts; - PyObject* co_names; - PyObject* co_exceptiontable; - int co_flags; - int co_argcount; - int co_posonlyargcount; - int co_kwonlyargcount; - int co_stacksize; - int co_firstlineno; - int co_nlocalsplus; - int co_framesize; - int co_nlocals; - int co_ncellvars; - int co_nfreevars; - uint32_t co_version; - PyObject* co_localsplusnames; - PyObject* co_localspluskinds; - PyObject* co_filename; - PyObject* co_name; - PyObject* co_qualname; - PyObject* co_linetable; - PyObject* co_weakreflist; - void* co_executors; - void* _co_cached; - uintptr_t _co_instrumentation_version; - void* _co_monitoring; - Py_ssize_t _co_unique_id; - int _co_firsttraceable; - void* co_extra; - /* deal with co_tlbc somehow */ - char co_code_adaptive[1]; -} PyCodeObject; -} // namespace Python3_15 - } // namespace pystack diff --git a/src/pystack/_pystack/cpython/frame.h b/src/pystack/_pystack/cpython/frame.h index a71f9dd8..0951a252 100644 --- a/src/pystack/_pystack/cpython/frame.h +++ b/src/pystack/_pystack/cpython/frame.h @@ -147,24 +147,6 @@ typedef union _PyStackRef { uintptr_t bits; } _PyStackRef; -typedef struct _interpreter_frame -{ - _PyStackRef f_executable; - void* previous; - void* f_funcobj; - PyObject* f_globals; - PyObject* f_builtins; - PyObject* f_locals; - PyObject* frame_obj; - _Py_CODEUNIT* instr_ptr; - _PyStackRef stackpointer; - /* int32_t tlbc_index; */ - uint16_t return_offset; - char owner; - uint8_t visited; - void* localsplus[1]; -} PyFrameObject; - } // namespace Python3_14 } // namespace pystack diff --git a/src/pystack/_pystack/cpython/gc.h b/src/pystack/_pystack/cpython/gc.h index 5ac0c941..85e6b8bd 100644 --- a/src/pystack/_pystack/cpython/gc.h +++ b/src/pystack/_pystack/cpython/gc.h @@ -83,48 +83,4 @@ struct _gc_runtime_state } // namespace Python3_8 -namespace Python3_14 { - -struct _gc_runtime_state -{ - PyObject* trash_delete_later; - int trash_delete_nesting; - int enabled; - int debug; - struct Python3_8::gc_generation young; - struct Python3_8::gc_generation old[2]; - struct Python3_8::gc_generation permanent_generation; - struct gc_generation_stats generation_stats[NUM_GENERATIONS]; - int collecting; - PyObject* garbage; - PyObject* callbacks; - Py_ssize_t heap_size; - Py_ssize_t work_to_do; - int visited_space; - int phase; -}; - -} // namespace Python3_14 - -namespace Python3_15 { - -struct _gc_runtime_state -{ - int enabled; - int debug; - struct Python3_8::gc_generation young; - struct Python3_8::gc_generation old[2]; - struct Python3_8::gc_generation permanent_generation; - struct gc_generation_stats generation_stats[NUM_GENERATIONS]; - int collecting; - struct _PyInterpreterFrame* frame; - PyObject* garbage; - PyObject* callbacks; - Py_ssize_t heap_size; - Py_ssize_t long_lived_total; - Py_ssize_t long_lived_pending; -}; - -} // namespace Python3_15 - } // namespace pystack diff --git a/src/pystack/_pystack/cpython/interpreter.h b/src/pystack/_pystack/cpython/interpreter.h index bc2f1742..31382998 100644 --- a/src/pystack/_pystack/cpython/interpreter.h +++ b/src/pystack/_pystack/cpython/interpreter.h @@ -339,157 +339,4 @@ typedef struct _is } PyInterpreterState; } // namespace Python3_13 -namespace Python3_14 { - -struct _pythreadstate; - -typedef struct -{ - Python3_13::PyMutex mutex; - unsigned long long thread; - size_t level; -} _PyRecursiveMutex; - -struct _import_state -{ - PyObject* modules; - PyObject* modules_by_index; - PyObject* importlib; - int override_frozen_modules; - int override_multi_interp_extensions_check; - PyObject* import_func; - _PyRecursiveMutex lock; - /* diagnostic info in PyImport_ImportModuleLevelObject() */ - struct - { - int import_level; - int64_t accumulated; - int header; - } find_and_load; -}; - -struct _gil_runtime_state -{ - unsigned long interval; - struct _pythreadstate* last_holder; - int locked; - unsigned long switch_number; - pthread_cond_t cond; - pthread_cond_t mutex; -#ifdef FORCE_SWITCHING - pthread_cond_t switch_cond; - pthread_cond_t switch_mutex; -#endif -}; - -typedef struct _is -{ - struct _ceval_state ceval; - void* _malloced; - struct _is* next; - int64_t id; - Py_ssize_t id_refcount; - int requires_idref; - long _whence; - int _initialized; - int _ready; - int finalizing; - uintptr_t last_restart_version; - struct pythreads - { - uint64_t next_unique_id; - struct _pythreadstate* head; - struct _pythreadstate* preallocated; - struct _pythreadstate* main; - Py_ssize_t count; - size_t stacksize; - } threads; - void* runtime; - struct _pythreadstate* _finalizing; - unsigned long _finalizing_id; - struct _gc_runtime_state gc; - PyObject* sysdict; - PyObject* builtins; - struct _import_state imports; - struct _gil_runtime_state _gil; -} PyInterpreterState; - -} // namespace Python3_14 - -namespace Python3_15 { - -struct _pythreadstate; - -typedef struct -{ - Python3_13::PyMutex mutex; - unsigned long long thread; - size_t level; -} _PyRecursiveMutex; - -struct _import_state -{ - PyObject* modules; - PyObject* modules_by_index; - PyObject* importlib; - int override_frozen_modules; - int override_multi_interp_extensions_check; - PyObject* import_func; - _PyRecursiveMutex lock; - /* diagnostic info in PyImport_ImportModuleLevelObject() */ - struct - { - int import_level; - int64_t accumulated; - int header; - } find_and_load; -}; - -struct _gil_runtime_state -{ - unsigned long interval; - struct _pythreadstate* last_holder; - int locked; - unsigned long switch_number; - pthread_cond_t cond; - pthread_cond_t mutex; -#ifdef FORCE_SWITCHING - pthread_cond_t switch_cond; - pthread_cond_t switch_mutex; -#endif -}; - -typedef struct _is -{ - struct _ceval_state ceval; - struct _is* next; - int64_t id; - Py_ssize_t id_refcount; - int requires_idref; - long _whence; - int _initialized; - int _ready; - int finalizing; - uintptr_t last_restart_version; - struct pythreads - { - uint64_t next_unique_id; - struct _pythreadstate* head; - struct _pythreadstate* preallocated; - struct _pythreadstate* main; - Py_ssize_t count; - size_t stacksize; - } threads; - void* runtime; - struct _pythreadstate* _finalizing; - unsigned long _finalizing_id; - struct _gc_runtime_state gc; - PyObject* sysdict; - PyObject* builtins; - struct _import_state imports; - struct _gil_runtime_state _gil; -} PyInterpreterState; - -} // namespace Python3_15 - } // namespace pystack diff --git a/src/pystack/_pystack/cpython/runtime.h b/src/pystack/_pystack/cpython/runtime.h index 9ba3153f..26f1821a 100644 --- a/src/pystack/_pystack/cpython/runtime.h +++ b/src/pystack/_pystack/cpython/runtime.h @@ -619,21 +619,6 @@ typedef struct _Py_DebugOffsets typedef struct pyruntimestate { _Py_DebugOffsets debug_offsets; - int _initialized; - int preinitializing; - int preinitialized; - int core_initialized; - int initialized; - struct _pythreadstate* finalizing; - unsigned long _finalizing_id; - - struct pyinterpreters - { - Python3_13::PyMutex mutex; - PyInterpreterState* head; - PyInterpreterState* main; - int64_t next_id; - } interpreters; } PyRuntimeState; } // namespace Python3_14 @@ -859,21 +844,6 @@ typedef struct _Py_DebugOffsets typedef struct pyruntimestate { _Py_DebugOffsets debug_offsets; - int _initialized; - int preinitializing; - int preinitialized; - int core_initialized; - int initialized; - struct _pythreadstate* finalizing; - unsigned long _finalizing_id; - - struct pyinterpreters - { - Python3_13::PyMutex mutex; - PyInterpreterState* head; - PyInterpreterState* main; - int64_t next_id; - } interpreters; } PyRuntimeState; } // namespace Python3_15 diff --git a/src/pystack/_pystack/cpython/string.h b/src/pystack/_pystack/cpython/string.h index 17d02fe0..b6618c3c 100644 --- a/src/pystack/_pystack/cpython/string.h +++ b/src/pystack/_pystack/cpython/string.h @@ -122,23 +122,9 @@ struct _PyUnicode_State } // namespace Python3_14t -namespace Python3_15t { - -struct _PyUnicode_State -{ - unsigned char interned; - unsigned int kind : 3; - unsigned int compact : 1; - unsigned int ascii : 1; - unsigned int statically_allocated : 1; -}; - -} // namespace Python3_15t - union AnyPyUnicodeState { Python3::_PyUnicode_State python3; Python3_14t::_PyUnicode_State python3_14t; - Python3_15t::_PyUnicode_State python3_15t; }; } // namespace pystack diff --git a/src/pystack/_pystack/cpython/thread.h b/src/pystack/_pystack/cpython/thread.h index 091481ec..c9b5da8d 100644 --- a/src/pystack/_pystack/cpython/thread.h +++ b/src/pystack/_pystack/cpython/thread.h @@ -295,144 +295,4 @@ typedef struct _pythreadstate } PyThreadState; } // namespace Python3_13 -namespace Python3_14 { - -typedef struct _remote_debugger_support -{ - int32_t debugger_pending_call; - char debugger_script_path[512]; -} _PyRemoteDebuggerSupport; - -typedef struct _pythreadstate -{ - struct _pythreadstate* prev; - struct _pythreadstate* next; - PyInterpreterState* interp; - uintptr_t eval_breaker; - struct - { - unsigned int initialized : 1; - unsigned int bound : 1; - unsigned int unbound : 1; - unsigned int bound_gilstate : 1; - unsigned int active : 1; - unsigned int finalizing : 1; - unsigned int cleared : 1; - unsigned int finalized : 1; - unsigned int : 24; - } _status; - int holds_gil; - int _whence; - int state; - int py_recursion_remaining; - int py_recursion_limit; - int recursion_headroom; - int tracing; - int what_event; - struct _PyInterpreterFrame* current_frame; - Py_tracefunc c_profilefunc; - Py_tracefunc c_tracefunc; - PyObject* c_profileobj; - PyObject* c_traceobj; - PyObject* current_exception; - Python3_13::_PyErr_StackItem* exc_info; - PyObject* dict; - int gilstate_counter; - PyObject* async_exc; - unsigned long thread_id; - unsigned long native_thread_id; - PyObject* delete_later; - uintptr_t critical_section; - int coroutine_origin_tracking_depth; - PyObject* async_gen_firstiter; - PyObject* async_gen_finalizer; - PyObject* context; - uint64_t context_ver; - uint64_t id; - void* datastack_chunk; - PyObject** datastack_top; - PyObject** datastack_limit; - Python3_13::_PyErr_StackItem exc_state; - PyObject* current_executor; - uint64_t dict_global_version; - PyObject* threading_local_key; - PyObject* threading_local_sentinel; - _PyRemoteDebuggerSupport remote_debugger_support; -} PyThreadState; - -} // namespace Python3_14 - -namespace Python3_15 { - -typedef struct _remote_debugger_support -{ - int32_t debugger_pending_call; - char debugger_script_path[512]; -} _PyRemoteDebuggerSupport; - -typedef struct _pythreadstate -{ - struct _pythreadstate* prev; - struct _pythreadstate* next; - PyInterpreterState* interp; - uintptr_t eval_breaker; - struct - { - unsigned int initialized : 1; - unsigned int bound : 1; - unsigned int unbound : 1; - unsigned int bound_gilstate : 1; - unsigned int active : 1; - unsigned int finalizing : 1; - unsigned int cleared : 1; - unsigned int finalized : 1; - unsigned int : 24; - } _status; - int holds_gil; - int gil_requested; - int _whence; - int state; - int py_recursion_remaining; - int py_recursion_limit; - int recursion_headroom; - int tracing; - int what_event; - struct _PyInterpreterFrame* current_frame; - struct _PyInterpreterFrame* base_frame; - struct _PyInterpreterFrame* last_profiled_frame; - - Py_tracefunc c_profilefunc; - Py_tracefunc c_tracefunc; - PyObject* c_profileobj; - PyObject* c_traceobj; - PyObject* current_exception; - Python3_13::_PyErr_StackItem* exc_info; - PyObject* dict; - int gilstate_counter; - PyObject* async_exc; - unsigned long thread_id; - unsigned long native_thread_id; - PyObject* delete_later; - uintptr_t critical_section; - int coroutine_origin_tracking_depth; - PyObject* async_gen_firstiter; - PyObject* async_gen_finalizer; - PyObject* context; - uint64_t context_ver; - uint64_t id; - void* datastack_chunk; - PyObject** datastack_top; - PyObject** datastack_limit; - void* datastack_cached_chunk; - Python3_13::_PyErr_StackItem exc_state; - PyObject* current_executor; - uint64_t dict_global_version; - PyObject* threading_local_key; - PyObject* threading_local_sentinel; - _PyRemoteDebuggerSupport remote_debugger_support; - -} PyThreadState; - -} // namespace Python3_15 - } // namespace pystack diff --git a/src/pystack/_pystack/process.cpp b/src/pystack/_pystack/process.cpp index d351eecd..9aac1b2c 100644 --- a/src/pystack/_pystack/process.cpp +++ b/src/pystack/_pystack/process.cpp @@ -821,6 +821,11 @@ AbstractProcessManager::setPythonVersion(const std::pair& version) void AbstractProcessManager::warnIfOffsetsAreMismatched(remote_addr_t runtime_addr) const { + if (versionIsAtLeast(3, 14)) { + // From 3.14 onwards, we don't have static offsets to compare to. + return; + } + Structure py_runtime(shared_from_this(), runtime_addr); if (0 != memcmp(py_runtime.getField(&py_runtime_v::o_dbg_off_cookie), "xdebugpy", 8)) { @@ -1455,6 +1460,12 @@ ProcessManager::initializeVersion(pid_t pid, const ProcessMemoryMapInfo& map_inf } setPythonVersion(python_version); + + if (!d_debug_offsets && versionIsAtLeast(3, 14)) { + throw std::runtime_error( + "The process runs Python " + std::to_string(d_major) + "." + std::to_string(d_minor) + + ", but we've failed to locate its _Py_DebugOffsets structure in memory."); + } } const std::vector& @@ -1539,6 +1550,12 @@ CoreFileProcessManager::initializeVersion( } setPythonVersion(python_version); + + if (!d_debug_offsets && versionIsAtLeast(3, 14)) { + throw std::runtime_error( + "The process runs Python " + std::to_string(d_major) + "." + std::to_string(d_minor) + + ", but we've failed to locate its _Py_DebugOffsets structure in memory."); + } } const std::vector& diff --git a/src/pystack/_pystack/version.cpp b/src/pystack/_pystack/version.cpp index 975b5446..9f0733d6 100644 --- a/src/pystack/_pystack/version.cpp +++ b/src/pystack/_pystack/version.cpp @@ -351,6 +351,92 @@ py_runtimev313() }; } +template +constexpr py_runtime_v +py_runtimev314() +{ + return { + sizeof(T), + {}, + {}, + {}, + {}, + {offsetof(T, debug_offsets.cookie)}, + {offsetof(T, debug_offsets.version)}, + {offsetof(T, debug_offsets.free_threaded)}, + {offsetof(T, debug_offsets.runtime_state.size)}, + {offsetof(T, debug_offsets.runtime_state.finalizing)}, + {offsetof(T, debug_offsets.runtime_state.interpreters_head)}, + {offsetof(T, debug_offsets.interpreter_state.size)}, + {offsetof(T, debug_offsets.interpreter_state.id)}, + {offsetof(T, debug_offsets.interpreter_state.next)}, + {offsetof(T, debug_offsets.interpreter_state.threads_head)}, + {offsetof(T, debug_offsets.interpreter_state.gc)}, + {offsetof(T, debug_offsets.interpreter_state.imports_modules)}, + {offsetof(T, debug_offsets.interpreter_state.sysdict)}, + {offsetof(T, debug_offsets.interpreter_state.builtins)}, + {offsetof(T, debug_offsets.interpreter_state.ceval_gil)}, + {offsetof(T, debug_offsets.interpreter_state.gil_runtime_state)}, + {offsetof(T, debug_offsets.interpreter_state.gil_runtime_state_enabled)}, + {offsetof(T, debug_offsets.interpreter_state.gil_runtime_state_locked)}, + {offsetof(T, debug_offsets.interpreter_state.gil_runtime_state_holder)}, + {offsetof(T, debug_offsets.thread_state.size)}, + {offsetof(T, debug_offsets.thread_state.prev)}, + {offsetof(T, debug_offsets.thread_state.next)}, + {offsetof(T, debug_offsets.thread_state.interp)}, + {offsetof(T, debug_offsets.thread_state.current_frame)}, + {offsetof(T, debug_offsets.thread_state.thread_id)}, + {offsetof(T, debug_offsets.thread_state.native_thread_id)}, + {offsetof(T, debug_offsets.thread_state.datastack_chunk)}, + {offsetof(T, debug_offsets.thread_state.status)}, + {offsetof(T, debug_offsets.interpreter_frame.size)}, + {offsetof(T, debug_offsets.interpreter_frame.previous)}, + {offsetof(T, debug_offsets.interpreter_frame.executable)}, + {offsetof(T, debug_offsets.interpreter_frame.instr_ptr)}, + {offsetof(T, debug_offsets.interpreter_frame.localsplus)}, + {offsetof(T, debug_offsets.interpreter_frame.owner)}, + {offsetof(T, debug_offsets.code_object.size)}, + {offsetof(T, debug_offsets.code_object.filename)}, + {offsetof(T, debug_offsets.code_object.name)}, + {offsetof(T, debug_offsets.code_object.qualname)}, + {offsetof(T, debug_offsets.code_object.linetable)}, + {offsetof(T, debug_offsets.code_object.firstlineno)}, + {offsetof(T, debug_offsets.code_object.argcount)}, + {offsetof(T, debug_offsets.code_object.localsplusnames)}, + {offsetof(T, debug_offsets.code_object.localspluskinds)}, + {offsetof(T, debug_offsets.code_object.co_code_adaptive)}, + {offsetof(T, debug_offsets.pyobject.size)}, + {offsetof(T, debug_offsets.pyobject.ob_type)}, + {offsetof(T, debug_offsets.type_object.size)}, + {offsetof(T, debug_offsets.type_object.tp_name)}, + {offsetof(T, debug_offsets.type_object.tp_repr)}, + {offsetof(T, debug_offsets.type_object.tp_flags)}, + {offsetof(T, debug_offsets.tuple_object.size)}, + {offsetof(T, debug_offsets.tuple_object.ob_item)}, + {offsetof(T, debug_offsets.tuple_object.ob_size)}, + {offsetof(T, debug_offsets.list_object.size)}, + {offsetof(T, debug_offsets.list_object.ob_item)}, + {offsetof(T, debug_offsets.list_object.ob_size)}, + {offsetof(T, debug_offsets.dict_object.size)}, + {offsetof(T, debug_offsets.dict_object.ma_keys)}, + {offsetof(T, debug_offsets.dict_object.ma_values)}, + {offsetof(T, debug_offsets.float_object.size)}, + {offsetof(T, debug_offsets.float_object.ob_fval)}, + {offsetof(T, debug_offsets.long_object.size)}, + {offsetof(T, debug_offsets.long_object.lv_tag)}, + {offsetof(T, debug_offsets.long_object.ob_digit)}, + {offsetof(T, debug_offsets.bytes_object.size)}, + {offsetof(T, debug_offsets.bytes_object.ob_size)}, + {offsetof(T, debug_offsets.bytes_object.ob_sval)}, + {offsetof(T, debug_offsets.unicode_object.size)}, + {offsetof(T, debug_offsets.unicode_object.state)}, + {offsetof(T, debug_offsets.unicode_object.length)}, + {offsetof(T, debug_offsets.unicode_object.asciiobject_size)}, + {offsetof(T, debug_offsets.gc.size)}, + {offsetof(T, debug_offsets.gc.collecting)}, + }; +} + template constexpr py_type_v py_type() @@ -723,25 +809,25 @@ python_v python_v3_13 = { // ---- Python 3.14 ------------------------------------------------------------ python_v python_v3_14 = { - py_tuple(), - py_list(), - py_dict(), + {}, + {}, + {}, py_dictkeys(), py_dictvalues(), - py_float(), - py_long<_PyLongObject>(), - py_bytes(), - py_unicode(), - py_object(), - py_type(), - py_codev311(), - py_framev314(), - py_threadv313(), - py_isv312(), - py_runtimev313(), - py_gc(), + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + py_runtimev314(), + {}, py_cframe(), - py_gilruntimestate(), + {}, }; // ----------------------------------------------------------------------------- @@ -749,25 +835,25 @@ python_v python_v3_14 = { // ---- Python 3.15 ------------------------------------------------------------ python_v python_v3_15 = { - py_tuple(), - py_list(), - py_dict(), + {}, + {}, + {}, py_dictkeys(), py_dictvalues(), - py_float(), - py_long<_PyLongObject>(), - py_bytes(), - py_unicode(), - py_object(), - py_type(), - py_codev311(), - py_framev314(), - py_threadv313(), - py_isv312(), - py_runtimev313(), - py_gc(), + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + py_runtimev314(), + {}, py_cframe(), - py_gilruntimestate(), + {}, }; // -----------------------------------------------------------------------------