/****************************************************************************** * Python Remote Debugging Module - Shared Header * * This header provides common declarations, types, and utilities shared * across the remote debugging module implementation files. ******************************************************************************/ #ifndef Py_REMOTE_DEBUGGING_H #define Py_REMOTE_DEBUGGING_H #ifdef __cplusplus extern "C" { #endif #define _GNU_SOURCE #ifndef Py_BUILD_CORE_BUILTIN # define Py_BUILD_CORE_MODULE 1 #endif #include "Python.h" #include // _Py_DebugOffsets #include // FRAME_SUSPENDED_YIELD_FROM #include // FRAME_OWNED_BY_INTERPRETER #include // struct llist_node #include // _PyLong_GetZero #include // Py_TAG_BITS #include "../../Python/remote_debug.h" #include #include #include #include #include #include #include #include #ifndef HAVE_PROCESS_VM_READV # define HAVE_PROCESS_VM_READV 0 #endif #if defined(__APPLE__) && TARGET_OS_OSX #include #include #define MAX_NATIVE_THREADS 4096 #endif #ifdef MS_WINDOWS #include #include #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) typedef enum _WIN32_THREADSTATE { WIN32_THREADSTATE_INITIALIZED = 0, WIN32_THREADSTATE_READY = 1, WIN32_THREADSTATE_RUNNING = 2, WIN32_THREADSTATE_STANDBY = 3, WIN32_THREADSTATE_TERMINATED = 4, WIN32_THREADSTATE_WAITING = 5, WIN32_THREADSTATE_TRANSITION = 6, WIN32_THREADSTATE_UNKNOWN = 7 } WIN32_THREADSTATE; #endif /* ============================================================================ * MACROS AND CONSTANTS * ============================================================================ */ #define GET_MEMBER(type, obj, offset) (*(type*)((char*)(obj) + (offset))) #define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS)) #define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(*(type*)((char*)(obj) + (offset)))) /* Size macros for opaque buffers */ #define SIZEOF_BYTES_OBJ sizeof(PyBytesObject) #define SIZEOF_CODE_OBJ sizeof(PyCodeObject) #define SIZEOF_GEN_OBJ sizeof(PyGenObject) #define SIZEOF_INTERP_FRAME sizeof(_PyInterpreterFrame) #define SIZEOF_LLIST_NODE sizeof(struct llist_node) #define SIZEOF_PAGE_CACHE_ENTRY sizeof(page_cache_entry_t) #define SIZEOF_PYOBJECT sizeof(PyObject) #define SIZEOF_SET_OBJ sizeof(PySetObject) #define SIZEOF_TASK_OBJ 4096 #define SIZEOF_THREAD_STATE sizeof(PyThreadState) #define SIZEOF_TYPE_OBJ sizeof(PyTypeObject) #define SIZEOF_UNICODE_OBJ sizeof(PyUnicodeObject) #define SIZEOF_LONG_OBJ sizeof(PyLongObject) #define SIZEOF_GC_RUNTIME_STATE sizeof(struct _gc_runtime_state) #define SIZEOF_INTERPRETER_STATE sizeof(PyInterpreterState) #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifdef Py_GIL_DISABLED #define INTERP_STATE_MIN_SIZE MAX(MAX(MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ offsetof(PyInterpreterState, tlbc_indices.tlbc_generation) + sizeof(uint32_t)), \ offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)), \ offsetof(PyInterpreterState, gc.frame) + sizeof(_PyInterpreterFrame *)) #else #define INTERP_STATE_MIN_SIZE MAX(MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)), \ offsetof(PyInterpreterState, gc.frame) + sizeof(_PyInterpreterFrame *)) #endif #define INTERP_STATE_BUFFER_SIZE MAX(INTERP_STATE_MIN_SIZE, 256) #define MAX_TLBC_SIZE 2048 /* Thread status flags */ #define THREAD_STATUS_HAS_GIL (1 << 0) #define THREAD_STATUS_ON_CPU (1 << 1) #define THREAD_STATUS_UNKNOWN (1 << 2) #define THREAD_STATUS_GIL_REQUESTED (1 << 3) /* Exception cause macro */ #define set_exception_cause(unwinder, exc_type, message) \ if (unwinder->debug) { \ _set_debug_exception_cause(exc_type, message); \ } /* ============================================================================ * TYPE DEFINITIONS * ============================================================================ */ struct _Py_AsyncioModuleDebugOffsets { struct _asyncio_task_object { uint64_t size; uint64_t task_name; uint64_t task_awaited_by; uint64_t task_is_task; uint64_t task_awaited_by_is_set; uint64_t task_coro; uint64_t task_node; } asyncio_task_object; struct _asyncio_interpreter_state { uint64_t size; uint64_t asyncio_tasks_head; } asyncio_interpreter_state; struct _asyncio_thread_state { uint64_t size; uint64_t asyncio_running_loop; uint64_t asyncio_running_task; uint64_t asyncio_tasks_head; } asyncio_thread_state; }; typedef struct { PyObject *func_name; PyObject *file_name; int first_lineno; PyObject *linetable; // bytes uintptr_t addr_code_adaptive; } CachedCodeMetadata; /* Frame cache constants and types */ #define FRAME_CACHE_MAX_THREADS 32 #define FRAME_CACHE_MAX_FRAMES 1024 typedef struct { uint64_t thread_id; // 0 = empty slot uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; Py_ssize_t num_addrs; PyObject *frame_list; // owned reference, NULL if empty } FrameCacheEntry; /* Statistics for profiling performance analysis */ typedef struct { uint64_t total_samples; // Total number of get_stack_trace calls uint64_t frame_cache_hits; // Full cache hits (entire stack unchanged) uint64_t frame_cache_misses; // Cache misses requiring full walk uint64_t frame_cache_partial_hits; // Partial hits (stopped at cached frame) uint64_t frames_read_from_cache; // Total frames retrieved from cache uint64_t frames_read_from_memory; // Total frames read from remote memory uint64_t memory_reads; // Total remote memory read operations uint64_t memory_bytes_read; // Total bytes read from remote memory uint64_t code_object_cache_hits; // Code object cache hits uint64_t code_object_cache_misses; // Code object cache misses uint64_t stale_cache_invalidations; // Times stale entries were cleared } UnwinderStats; /* Stats tracking macros - no-op when stats collection is disabled */ #define STATS_INC(unwinder, field) \ do { if ((unwinder)->collect_stats) (unwinder)->stats.field++; } while(0) #define STATS_ADD(unwinder, field, val) \ do { if ((unwinder)->collect_stats) (unwinder)->stats.field += (val); } while(0) typedef struct { PyTypeObject *RemoteDebugging_Type; PyTypeObject *TaskInfo_Type; PyTypeObject *FrameInfo_Type; PyTypeObject *CoroInfo_Type; PyTypeObject *ThreadInfo_Type; PyTypeObject *InterpreterInfo_Type; PyTypeObject *AwaitedInfo_Type; } RemoteDebuggingState; enum _ThreadState { THREAD_STATE_RUNNING, THREAD_STATE_IDLE, THREAD_STATE_GIL_WAIT, THREAD_STATE_UNKNOWN }; enum _ProfilingMode { PROFILING_MODE_WALL = 0, PROFILING_MODE_CPU = 1, PROFILING_MODE_GIL = 2, PROFILING_MODE_ALL = 3 }; typedef struct { PyObject_HEAD proc_handle_t handle; uintptr_t runtime_start_address; struct _Py_DebugOffsets debug_offsets; int async_debug_offsets_available; struct _Py_AsyncioModuleDebugOffsets async_debug_offsets; uintptr_t interpreter_addr; uintptr_t tstate_addr; uint64_t code_object_generation; _Py_hashtable_t *code_object_cache; int debug; int only_active_thread; int mode; int skip_non_matching_threads; int native; int gc; int cache_frames; int collect_stats; // whether to collect statistics uint32_t stale_invalidation_counter; // counter for throttling frame_cache_invalidate_stale RemoteDebuggingState *cached_state; FrameCacheEntry *frame_cache; // preallocated array of FRAME_CACHE_MAX_THREADS entries UnwinderStats stats; // statistics for performance analysis #ifdef Py_GIL_DISABLED uint32_t tlbc_generation; _Py_hashtable_t *tlbc_cache; #endif #ifdef __APPLE__ uint64_t thread_id_offset; #endif #ifdef MS_WINDOWS PVOID win_process_buffer; ULONG win_process_buffer_size; #endif } RemoteUnwinderObject; #define RemoteUnwinder_CAST(op) ((RemoteUnwinderObject *)(op)) typedef struct { int lineno; int end_lineno; int column; int end_column; } LocationInfo; typedef struct { uintptr_t remote_addr; size_t size; void *local_copy; } StackChunkInfo; typedef struct { StackChunkInfo *chunks; size_t count; } StackChunkList; /* Function pointer types for iteration callbacks */ typedef int (*thread_processor_func)( RemoteUnwinderObject *unwinder, uintptr_t thread_state_addr, unsigned long tid, void *context ); typedef int (*set_entry_processor_func)( RemoteUnwinderObject *unwinder, uintptr_t key_addr, void *context ); /* ============================================================================ * STRUCTSEQ DESCRIPTORS (extern declarations) * ============================================================================ */ extern PyStructSequence_Desc TaskInfo_desc; extern PyStructSequence_Desc FrameInfo_desc; extern PyStructSequence_Desc CoroInfo_desc; extern PyStructSequence_Desc ThreadInfo_desc; extern PyStructSequence_Desc InterpreterInfo_desc; extern PyStructSequence_Desc AwaitedInfo_desc; /* ============================================================================ * UTILITY FUNCTION DECLARATIONS * ============================================================================ */ /* State access functions */ extern RemoteDebuggingState *RemoteDebugging_GetState(PyObject *module); extern RemoteDebuggingState *RemoteDebugging_GetStateFromType(PyTypeObject *type); extern RemoteDebuggingState *RemoteDebugging_GetStateFromObject(PyObject *obj); extern int RemoteDebugging_InitState(RemoteDebuggingState *st); /* Cache management */ extern void cached_code_metadata_destroy(void *ptr); /* Validation */ extern int is_prerelease_version(uint64_t version); extern int validate_debug_offsets(struct _Py_DebugOffsets *debug_offsets); /* ============================================================================ * MEMORY READING FUNCTION DECLARATIONS * ============================================================================ */ extern int read_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *result); extern int read_Py_ssize_t(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t *result); extern int read_char(RemoteUnwinderObject *unwinder, uintptr_t address, char *result); extern int read_py_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *ptr_addr); /* Python object reading */ extern PyObject *read_py_str(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t max_len); extern PyObject *read_py_bytes(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t max_len); extern long read_py_long(RemoteUnwinderObject *unwinder, uintptr_t address); /* ============================================================================ * CODE OBJECT FUNCTION DECLARATIONS * ============================================================================ */ extern int parse_code_object( RemoteUnwinderObject *unwinder, PyObject **result, uintptr_t address, uintptr_t instruction_pointer, uintptr_t *previous_frame, int32_t tlbc_index ); extern PyObject *make_frame_info( RemoteUnwinderObject *unwinder, PyObject *file, PyObject *line, PyObject *func ); /* Line table parsing */ extern bool parse_linetable( const uintptr_t addrq, const char* linetable, int firstlineno, LocationInfo* info ); /* TLBC cache (only for Py_GIL_DISABLED) */ #ifdef Py_GIL_DISABLED typedef struct { void *tlbc_array; Py_ssize_t tlbc_array_size; uint32_t generation; } TLBCCacheEntry; extern void tlbc_cache_entry_destroy(void *ptr); extern TLBCCacheEntry *get_tlbc_cache_entry(RemoteUnwinderObject *self, uintptr_t code_addr, uint32_t current_generation); extern int cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t tlbc_array_addr, uint32_t generation); #endif /* ============================================================================ * FRAME FUNCTION DECLARATIONS * ============================================================================ */ extern int is_frame_valid( RemoteUnwinderObject *unwinder, uintptr_t frame_addr, uintptr_t code_object_addr ); extern int parse_frame_object( RemoteUnwinderObject *unwinder, PyObject** result, uintptr_t address, uintptr_t* address_of_code_object, uintptr_t* previous_frame ); extern int parse_frame_from_chunks( RemoteUnwinderObject *unwinder, PyObject **result, uintptr_t address, uintptr_t *previous_frame, uintptr_t *stackpointer, StackChunkList *chunks ); /* Stack chunk management */ extern void cleanup_stack_chunks(StackChunkList *chunks); extern int copy_stack_chunks(RemoteUnwinderObject *unwinder, uintptr_t tstate_addr, StackChunkList *out_chunks); extern void *find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr); extern int process_frame_chain( RemoteUnwinderObject *unwinder, uintptr_t initial_frame_addr, StackChunkList *chunks, PyObject *frame_info, uintptr_t gc_frame, uintptr_t last_profiled_frame, int *stopped_at_cached_frame, uintptr_t *frame_addrs, Py_ssize_t *num_addrs, Py_ssize_t max_addrs ); /* Frame cache functions */ extern int frame_cache_init(RemoteUnwinderObject *unwinder); extern void frame_cache_cleanup(RemoteUnwinderObject *unwinder); extern FrameCacheEntry *frame_cache_find(RemoteUnwinderObject *unwinder, uint64_t thread_id); extern int clear_last_profiled_frames(RemoteUnwinderObject *unwinder); extern void frame_cache_invalidate_stale(RemoteUnwinderObject *unwinder, PyObject *result); extern int frame_cache_lookup_and_extend( RemoteUnwinderObject *unwinder, uint64_t thread_id, uintptr_t last_profiled_frame, PyObject *frame_info, uintptr_t *frame_addrs, Py_ssize_t *num_addrs, Py_ssize_t max_addrs); // Returns: 1 = stored, 0 = not stored (graceful), -1 = error extern int frame_cache_store( RemoteUnwinderObject *unwinder, uint64_t thread_id, PyObject *frame_list, const uintptr_t *addrs, Py_ssize_t num_addrs); extern int collect_frames_with_cache( RemoteUnwinderObject *unwinder, uintptr_t frame_addr, StackChunkList *chunks, PyObject *frame_info, uintptr_t gc_frame, uintptr_t last_profiled_frame, uint64_t thread_id); /* ============================================================================ * THREAD FUNCTION DECLARATIONS * ============================================================================ */ extern int iterate_threads( RemoteUnwinderObject *unwinder, thread_processor_func processor, void *context ); extern int populate_initial_state_data( int all_threads, RemoteUnwinderObject *unwinder, uintptr_t runtime_start_address, uintptr_t *interpreter_state, uintptr_t *tstate ); extern int find_running_frame( RemoteUnwinderObject *unwinder, uintptr_t address_of_thread, uintptr_t *frame ); extern int get_thread_status(RemoteUnwinderObject *unwinder, uint64_t tid, uint64_t pthread_id); extern PyObject* unwind_stack_for_thread( RemoteUnwinderObject *unwinder, uintptr_t *current_tstate, uintptr_t gil_holder_tstate, uintptr_t gc_frame ); /* ============================================================================ * ASYNCIO FUNCTION DECLARATIONS * ============================================================================ */ extern uintptr_t _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle); extern int read_async_debug(RemoteUnwinderObject *unwinder); extern int ensure_async_debug_offsets(RemoteUnwinderObject *unwinder); /* Task parsing */ extern PyObject *parse_task_name(RemoteUnwinderObject *unwinder, uintptr_t task_address); extern int parse_task( RemoteUnwinderObject *unwinder, uintptr_t task_address, PyObject *render_to ); extern int parse_coro_chain( RemoteUnwinderObject *unwinder, uintptr_t coro_address, PyObject *render_to ); extern int parse_async_frame_chain( RemoteUnwinderObject *unwinder, PyObject *calls, uintptr_t address_of_thread, uintptr_t running_task_code_obj ); /* Set iteration */ extern int iterate_set_entries( RemoteUnwinderObject *unwinder, uintptr_t set_addr, set_entry_processor_func processor, void *context ); /* Task awaited_by processing */ extern int process_task_awaited_by( RemoteUnwinderObject *unwinder, uintptr_t task_address, set_entry_processor_func processor, void *context ); extern int process_single_task_node( RemoteUnwinderObject *unwinder, uintptr_t task_addr, PyObject **task_info, PyObject *result ); extern int process_task_and_waiters( RemoteUnwinderObject *unwinder, uintptr_t task_addr, PyObject *result ); extern int find_running_task_in_thread( RemoteUnwinderObject *unwinder, uintptr_t thread_state_addr, uintptr_t *running_task_addr ); extern int get_task_code_object( RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *code_obj_addr ); extern int append_awaited_by( RemoteUnwinderObject *unwinder, unsigned long tid, uintptr_t head_addr, PyObject *result ); /* Thread processors for asyncio */ extern int process_thread_for_awaited_by( RemoteUnwinderObject *unwinder, uintptr_t thread_state_addr, unsigned long tid, void *context ); extern int process_thread_for_async_stack_trace( RemoteUnwinderObject *unwinder, uintptr_t thread_state_addr, unsigned long tid, void *context ); #ifdef __cplusplus } #endif #endif /* Py_REMOTE_DEBUGGING_H */