/******************************************************************************
 * 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

/* _GNU_SOURCE must be defined before any system headers */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE_BUILTIN
#  ifndef Py_BUILD_CORE_MODULE
#    define Py_BUILD_CORE_MODULE 1
#  endif
#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_pyerrors.h"       // _PyErr_FormatFromCause
#include "internal/pycore_pyhash.h"        // _Py_HashPointerRaw
#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__)
#include <TargetConditionals.h>
#  if !defined(TARGET_OS_OSX)
     /* Older macOS SDKs do not define TARGET_OS_OSX */
#    define TARGET_OS_OSX 1
#  endif
#  if TARGET_OS_OSX
#    include <libproc.h>
#    include <sys/types.h>
#    define MAX_NATIVE_THREADS 4096
#  endif
#endif

// Platforms that support pausing/resuming threads for accurate stack sampling
#if defined(MS_WINDOWS) || defined(__linux__) || (defined(__APPLE__) && TARGET_OS_OSX)
#  define Py_REMOTE_DEBUG_SUPPORTS_BLOCKING 1
#endif

#ifdef MS_WINDOWS
#include <windows.h>
#include <winternl.h>
#endif

#if defined(__APPLE__) && TARGET_OS_OSX

typedef struct {
    mach_port_t task;
    int suspended;
} _Py_RemoteDebug_ThreadsState;

#elif defined(__linux__)

typedef struct {
    pid_t *tids;      // Points to unwinder's reusable buffer
    size_t count;     // Number of threads currently seized
} _Py_RemoteDebug_ThreadsState;

#elif defined(MS_WINDOWS)

typedef NTSTATUS (NTAPI *NtSuspendProcessFunc)(HANDLE ProcessHandle);
typedef NTSTATUS (NTAPI *NtResumeProcessFunc)(HANDLE ProcessHandle);

typedef struct {
    HANDLE hProcess;
    int suspended;
} _Py_RemoteDebug_ThreadsState;

#else

typedef struct {
    int dummy;
} _Py_RemoteDebug_ThreadsState;

#endif

#ifdef MS_WINDOWS
#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) \
    (*(const type *)memcpy(&(type){0}, (const char *)(obj) + (offset), sizeof(type)))
#define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS))
#define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(GET_MEMBER(type, 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)

/* Maximum sizes for validation to prevent buffer overflows from corrupted data */
#define MAX_STACK_CHUNK_SIZE (16 * 1024 * 1024)  /* 16 MB max for stack chunks */
#define MAX_LONG_DIGITS 64  /* Allows values up to ~2^1920 */
#define MAX_SET_TABLE_SIZE (1 << 20)  /* 1 million entries max for set iteration */

#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)
#define THREAD_STATUS_HAS_EXCEPTION       (1 << 4)
#define THREAD_STATUS_MAIN_THREAD         (1 << 5)

/* Exception cause macro */
#define set_exception_cause(unwinder, exc_type, message)                              \
    do {                                                                              \
        assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
        if (unwinder->debug && !_Py_RemoteDebug_HasPermissionError()) {               \
            _set_debug_exception_cause(exc_type, message);                            \
        }                                                                             \
    } while (0)

/* ============================================================================
 * 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
    PyObject *last_frame_info;
    ptrdiff_t last_addrq;
    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 thread_state_addr;
    uintptr_t addrs[FRAME_CACHE_MAX_FRAMES];
    Py_ssize_t num_addrs;
    PyObject *thread_id_obj;                 // owned reference, NULL if empty
    PyObject *frame_list;                    // owned reference, NULL if empty
} FrameCacheEntry;

#define INTERPRETER_THREAD_CACHE_SIZE 32
#if (INTERPRETER_THREAD_CACHE_SIZE & (INTERPRETER_THREAD_CACHE_SIZE - 1)) != 0
#  error "INTERPRETER_THREAD_CACHE_SIZE must be a power of two"
#endif

// The two per-interpreter L2 caches below are split into per-field tables so
// that a writer rebinding one slot cannot leave stale data in a field owned by
// the other when the slot is reused across interpreters.
typedef struct {
    uintptr_t interpreter_addr;
    uintptr_t thread_state_addr;
} InterpreterTstateCacheEntry;
typedef struct {
    uintptr_t interpreter_addr;
    uint64_t code_object_generation;
} InterpreterGenerationCacheEntry;

// Carries already-read thread state and/or frame buffers across helpers so the
// downstream callee can skip a remote read. Address fields are caller-supplied
// inputs; buffer pointers (tstate, frame) are NULL unless a prior batched read
// successfully populated them.
typedef struct {
    const char *tstate;
    uintptr_t tstate_addr;
    const char *frame;
    uintptr_t frame_addr;
} RemoteReadPrefetch;

/* 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
    uint64_t batched_read_attempts;          // Batched remote-read attempts
    uint64_t batched_read_successes;         // Attempts that read all requested segments
    uint64_t batched_read_misses;            // Attempts that fell back or partially read
    uint64_t batched_read_segments_requested; // Segments requested by batched reads
    uint64_t batched_read_segments_completed; // Segments completed by batched reads
} UnwinderStats;

#if defined(__GNUC__) || defined(__clang__)
#  define REMOTE_DEBUG_UNLIKELY(value) __builtin_expect(!!(value), 0)
#else
#  define REMOTE_DEBUG_UNLIKELY(value) (value)
#endif

/* Stats tracking macros - no-op when stats collection is disabled */
#define STATS_INC(unwinder, field) \
    do { if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) (unwinder)->stats.field++; } while(0)

#define STATS_ADD(unwinder, field, val) \
    do { if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) (unwinder)->stats.field += (val); } while(0)

#if HAVE_PROCESS_VM_READV
#  define STATS_BATCHED_READ(unwinder, requested, completed) \
    do { \
        if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) { \
            (unwinder)->stats.batched_read_attempts++; \
            (unwinder)->stats.batched_read_segments_requested += (uint64_t)(requested); \
            (unwinder)->stats.batched_read_segments_completed += (uint64_t)(completed); \
            if ((completed) == (requested)) { \
                (unwinder)->stats.batched_read_successes++; \
            } \
            else { \
                (unwinder)->stats.batched_read_misses++; \
            } \
        } \
    } while(0)
#else
#  define STATS_BATCHED_READ(unwinder, requested, completed) ((void)0)
#endif

typedef struct {
    PyTypeObject *RemoteDebugging_Type;
    PyTypeObject *TaskInfo_Type;
    PyTypeObject *LocationInfo_Type;
    PyTypeObject *FrameInfo_Type;
    PyTypeObject *CoroInfo_Type;
    PyTypeObject *ThreadInfo_Type;
    PyTypeObject *InterpreterInfo_Type;
    PyTypeObject *AwaitedInfo_Type;
    PyTypeObject *GCStatsInfo_Type;
    PyTypeObject *BinaryWriter_Type;
    PyTypeObject *BinaryReader_Type;
    PyTypeObject *GCMonitor_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,
    PROFILING_MODE_EXCEPTION = 4
};

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;
    _Py_hashtable_t *code_object_cache;
    int debug;
    int only_active_thread;
    int mode;
    int skip_non_matching_threads;
    int native;
    int gc;
    int opcodes;
    int cache_frames;
    int collect_stats;  // whether to collect statistics
    uint32_t stale_invalidation_counter;  // counter for throttling frame_cache_invalidate_stale
    // L1 single-entry shortcut over cached_tstates[]: most workloads sample one
    // interpreter, so check these pairs before hashing into the table below.
    uintptr_t cached_tstate_interpreter_addr;
    uintptr_t cached_tstate_addr;
    uintptr_t cached_generation_interpreter_addr;
    uint64_t cached_code_object_generation;
    RemoteDebuggingState *cached_state;
    FrameCacheEntry *frame_cache;  // preallocated array of FRAME_CACHE_MAX_THREADS entries
    UnwinderStats stats;  // statistics for performance analysis
    InterpreterTstateCacheEntry cached_tstates[INTERPRETER_THREAD_CACHE_SIZE];
    InterpreterGenerationCacheEntry cached_generations[INTERPRETER_THREAD_CACHE_SIZE];
#ifdef Py_GIL_DISABLED
    uint32_t tlbc_generation;
    _Py_hashtable_t *tlbc_cache;
#endif
#ifdef __APPLE__
    uint64_t thread_id_offset;
    int thread_id_offset_initialized;
#endif
#ifdef MS_WINDOWS
    PVOID win_process_buffer;
    ULONG win_process_buffer_size;
#endif
    // Thread stopping state (only on platforms that support it)
#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING
    _Py_RemoteDebug_ThreadsState threads_state;
    int threads_stopped;  // 1 if threads are currently stopped
#endif
#ifdef __linux__
    pid_t *thread_tids;           // Reusable buffer for thread IDs
    size_t thread_tids_capacity;  // Current capacity of thread_tids buffer
#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;

typedef struct {
    proc_handle_t handle;
    uintptr_t runtime_start_address;
    struct _Py_DebugOffsets debug_offsets;
    int debug;
} RuntimeOffsets;

/*
 * Context for frame chain traversal operations.
 */
typedef struct {
    /* Inputs */
    uintptr_t frame_addr;           // Starting frame address
    uintptr_t thread_state_addr;    // Owning thread state address
    uintptr_t base_frame_addr;      // Sentinel at bottom (for validation)
    uintptr_t gc_frame;             // GC frame address (0 if not tracking)
    uintptr_t last_profiled_frame;  // Last cached frame (0 if no cache)
    StackChunkList *chunks;         // Pre-copied stack chunks
    int skip_first_frame;           // Skip frame_addr itself (continue from its caller)
    RemoteReadPrefetch prefetch;     // Optional already-read thread/frame buffers

    /* Outputs */
    PyObject *frame_info;           // List to append FrameInfo objects
    uintptr_t *frame_addrs;         // Array of visited frame addresses
    Py_ssize_t num_addrs;           // Count of addresses collected
    Py_ssize_t max_addrs;           // Capacity of frame_addrs array
    uintptr_t last_frame_visited;   // Last frame address visited
    int stopped_at_cached_frame;    // Whether we stopped at cached frame
} FrameWalkContext;

/*
 * Context for code object parsing.
 */
typedef struct {
    uintptr_t code_addr;            // Code object address in remote process
    uintptr_t instruction_pointer;  // Current instruction pointer
    int32_t tlbc_index;             // Thread-local bytecode index (free-threading)
} CodeObjectContext;

typedef struct {
    PyObject_HEAD
    RuntimeOffsets offsets;
} GCMonitorObject;

#define GCMonitor_CAST(op) ((GCMonitorObject *)(op))

/* 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
);

typedef int (*interpreter_processor_func)(
    RuntimeOffsets *offsets,
    uintptr_t interpreter_state_addr,
    int64_t iid,
    void *context
);


/* ============================================================================
 * STRUCTSEQ DESCRIPTORS (extern declarations)
 * ============================================================================ */

extern PyStructSequence_Desc TaskInfo_desc;
extern PyStructSequence_Desc LocationInfo_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;
extern PyStructSequence_Desc GCStatsInfo_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);
#define PY_REMOTE_DEBUG_INVALID_ASYNC_DEBUG_OFFSETS (-2)

/* ============================================================================
 * 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,
    const CodeObjectContext *ctx
);

extern PyObject *make_location_info(
    RemoteUnwinderObject *unwinder,
    int lineno,
    int end_lineno,
    int col_offset,
    int end_col_offset
);

extern PyObject *make_frame_info(
    RemoteUnwinderObject *unwinder,
    PyObject *file,
    PyObject *location,  // LocationInfo structseq or None for synthetic frames
    PyObject *func,
    PyObject *opcode
);

/* Line table parsing */
extern bool parse_linetable(
    const uintptr_t addrq,
    const char* linetable,
    Py_ssize_t linetable_size,
    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,
    FrameWalkContext *ctx
);

/* 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 FrameCacheEntry *frame_cache_find_by_tstate(RemoteUnwinderObject *unwinder, uintptr_t tstate_addr);
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
// Only stores complete stacks that reach base_frame_addr
extern int frame_cache_store(
    RemoteUnwinderObject *unwinder,
    uint64_t thread_id,
    PyObject *frame_list,
    const uintptr_t *addrs,
    Py_ssize_t num_addrs,
    uintptr_t thread_state_addr,
    uintptr_t base_frame_addr,
    uintptr_t last_frame_visited);

extern int collect_frames_with_cache(
    RemoteUnwinderObject *unwinder,
    FrameWalkContext *ctx,
    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,
    uintptr_t main_thread_tstate,
    const RemoteReadPrefetch *prefetch
);

/* Thread stopping functions (for blocking mode) */
extern void _Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st);
extern int _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st);
extern void _Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st);

/* ============================================================================
 * INTERPRETER FUNCTION DECLARATIONS
 * ============================================================================ */

extern int
iterate_interpreters(
    RuntimeOffsets *offsets,
    interpreter_processor_func processor,
    void *context
);

/* ============================================================================
 * 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
);

/* ============================================================================
 * SUBPROCESS ENUMERATION FUNCTION DECLARATIONS
 * ============================================================================ */

/* Get all child PIDs of a process.
 * Returns a new Python list of PIDs, or NULL on error with exception set.
 * If recursive is true, includes all descendants (children, grandchildren, etc.)
 */
extern PyObject *enumerate_child_pids(pid_t target_pid, int recursive);

#ifdef __cplusplus
}
#endif

#endif /* Py_REMOTE_DEBUGGING_H */
