cpython/Modules/_remote_debugging/_remote_debugging.h
2025-12-07 15:53:48 +00:00

574 lines
18 KiB
C

/******************************************************************************
* 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 <internal/pycore_debug_offsets.h> // _Py_DebugOffsets
#include <internal/pycore_frame.h> // FRAME_SUSPENDED_YIELD_FROM
#include <internal/pycore_interpframe.h> // FRAME_OWNED_BY_INTERPRETER
#include <internal/pycore_llist.h> // struct llist_node
#include <internal/pycore_long.h> // _PyLong_GetZero
#include <internal/pycore_stackref.h> // Py_TAG_BITS
#include "../../Python/remote_debug.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef HAVE_PROCESS_VM_READV
# define HAVE_PROCESS_VM_READV 0
#endif
#if defined(__APPLE__) && TARGET_OS_OSX
#include <libproc.h>
#include <sys/types.h>
#define MAX_NATIVE_THREADS 4096
#endif
#ifdef MS_WINDOWS
#include <windows.h>
#include <winternl.h>
#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 base_frame_addr,
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 */