From 9a5db8f38e8f4c6ff26671cbb459bea1a1b546b3 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Wed, 24 Sep 2025 17:30:44 +0100 Subject: [PATCH 1/3] gh-139275: Fix compilation of Modules/_remote_debugging_module.c when the system doesn't have process_vm_readv --- Modules/_remote_debugging_module.c | 133 ++++++++++++++++++++++++++++- Python/remote_debug.h | 2 +- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/Modules/_remote_debugging_module.c b/Modules/_remote_debugging_module.c index c306143ee73b18..bf0d61eb1fb6ee 100644 --- a/Modules/_remote_debugging_module.c +++ b/Modules/_remote_debugging_module.c @@ -824,7 +824,7 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle) PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); _PyErr_ChainExceptions1(exc); } -#elif defined(__linux__) +#elif defined(__linux__) && HAVE_PROCESS_VM_READV // On Linux, search for asyncio debug in executable or DLL address = search_linux_map_for_section(handle, "AsyncioDebug", "python"); if (address == 0) { @@ -2453,6 +2453,137 @@ process_frame_chain( return 0; } +<<<<<<< Updated upstream +======= +static int +get_thread_status(RemoteUnwinderObject *unwinder, uint64_t tid, uint64_t pthread_id) { +#if defined(__APPLE__) && TARGET_OS_OSX + if (unwinder->thread_id_offset == 0) { + uint64_t *tids = (uint64_t *)PyMem_Malloc(MAX_NATIVE_THREADS * sizeof(uint64_t)); + if (!tids) { + PyErr_NoMemory(); + return -1; + } + int n = proc_pidinfo(unwinder->handle.pid, PROC_PIDLISTTHREADS, 0, tids, MAX_NATIVE_THREADS * sizeof(uint64_t)) / sizeof(uint64_t); + if (n <= 0) { + PyMem_Free(tids); + return THREAD_STATE_UNKNOWN; + } + uint64_t min_offset = UINT64_MAX; + for (int i = 0; i < n; i++) { + uint64_t offset = tids[i] - pthread_id; + if (offset < min_offset) { + min_offset = offset; + } + } + unwinder->thread_id_offset = min_offset; + PyMem_Free(tids); + } + struct proc_threadinfo ti; + uint64_t tid_with_offset = pthread_id + unwinder->thread_id_offset; + if (proc_pidinfo(unwinder->handle.pid, PROC_PIDTHREADINFO, tid_with_offset, &ti, sizeof(ti)) != sizeof(ti)) { + return THREAD_STATE_UNKNOWN; + } + if (ti.pth_run_state == TH_STATE_RUNNING) { + return THREAD_STATE_RUNNING; + } + return THREAD_STATE_IDLE; +#elif defined(__linux__) && HAVE_PROCESS_VM_READV + char stat_path[256]; + char buffer[2048] = ""; + + snprintf(stat_path, sizeof(stat_path), "/proc/%d/task/%lu/stat", unwinder->handle.pid, tid); + + int fd = open(stat_path, O_RDONLY); + if (fd == -1) { + return THREAD_STATE_UNKNOWN; + } + + if (read(fd, buffer, 2047) == 0) { + close(fd); + return THREAD_STATE_UNKNOWN; + } + close(fd); + + char *p = strchr(buffer, ')'); + if (!p) { + return THREAD_STATE_UNKNOWN; + } + + p += 2; // Skip ") " + if (*p == ' ') { + p++; + } + + switch (*p) { + case 'R': // Running + return THREAD_STATE_RUNNING; + case 'S': // Interruptible sleep + case 'D': // Uninterruptible sleep + case 'T': // Stopped + case 'Z': // Zombie + case 'I': // Idle kernel thread + return THREAD_STATE_IDLE; + default: + return THREAD_STATE_UNKNOWN; + } +#elif defined(MS_WINDOWS) + ULONG n; + NTSTATUS status = NtQuerySystemInformation( + SystemProcessInformation, + unwinder->win_process_buffer, + unwinder->win_process_buffer_size, + &n + ); + if (status == STATUS_INFO_LENGTH_MISMATCH) { + // Buffer was too small so we reallocate a larger one and try again. + unwinder->win_process_buffer_size = n; + PVOID new_buffer = PyMem_Realloc(unwinder->win_process_buffer, n); + if (!new_buffer) { + return -1; + } + unwinder->win_process_buffer = new_buffer; + return get_thread_status(unwinder, tid, pthread_id); + } + if (status != STATUS_SUCCESS) { + return -1; + } + + SYSTEM_PROCESS_INFORMATION *pi = (SYSTEM_PROCESS_INFORMATION *)unwinder->win_process_buffer; + while ((ULONG)(ULONG_PTR)pi->UniqueProcessId != unwinder->handle.pid) { + if (pi->NextEntryOffset == 0) { + // We didn't find the process + return -1; + } + pi = (SYSTEM_PROCESS_INFORMATION *)(((BYTE *)pi) + pi->NextEntryOffset); + } + + SYSTEM_THREAD_INFORMATION *ti = (SYSTEM_THREAD_INFORMATION *)((char *)pi + sizeof(SYSTEM_PROCESS_INFORMATION)); + for (Py_ssize_t i = 0; i < pi->NumberOfThreads; i++, ti++) { + if (ti->ClientId.UniqueThread == (HANDLE)tid) { + return ti->ThreadState != WIN32_THREADSTATE_RUNNING ? THREAD_STATE_IDLE : THREAD_STATE_RUNNING; + } + } + + return -1; +#else + return THREAD_STATE_UNKNOWN; +#endif +} + +typedef 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; +} _thread_status; + +>>>>>>> Stashed changes static PyObject* unwind_stack_for_thread( RemoteUnwinderObject *unwinder, diff --git a/Python/remote_debug.h b/Python/remote_debug.h index d920d9e5b5ff2c..e7676013197fa9 100644 --- a/Python/remote_debug.h +++ b/Python/remote_debug.h @@ -891,7 +891,7 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle) handle->pid); _PyErr_ChainExceptions1(exc); } -#elif defined(__linux__) +#elif defined(__linux__) && HAVE_PROCESS_VM_READV // On Linux, search for 'python' in executable or DLL address = search_linux_map_for_section(handle, "PyRuntime", "python"); if (address == 0) { From 8a14a30f5766594e08450404ae34c344e0649601 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Wed, 24 Sep 2025 17:33:23 +0100 Subject: [PATCH 2/3] Add NEWS --- .../2025-09-24-17-32-52.gh-issue-139275.novrqf.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-32-52.gh-issue-139275.novrqf.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-32-52.gh-issue-139275.novrqf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-32-52.gh-issue-139275.novrqf.rst new file mode 100644 index 00000000000000..8cb4b972ad69b6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-24-17-32-52.gh-issue-139275.novrqf.rst @@ -0,0 +1,2 @@ +Fix compilation problems in ``_remote_debugging_module.c`` when the system +doesn't have ``process_vm_readv``. Patch by Pablo Galindo From 798bea00bef90b52dad588d2ce963e7d14e4c443 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Wed, 24 Sep 2025 17:39:58 +0100 Subject: [PATCH 3/3] Fix conflicts --- Modules/_remote_debugging_module.c | 131 ----------------------------- 1 file changed, 131 deletions(-) diff --git a/Modules/_remote_debugging_module.c b/Modules/_remote_debugging_module.c index bf0d61eb1fb6ee..f38c91993bfee6 100644 --- a/Modules/_remote_debugging_module.c +++ b/Modules/_remote_debugging_module.c @@ -2453,137 +2453,6 @@ process_frame_chain( return 0; } -<<<<<<< Updated upstream -======= -static int -get_thread_status(RemoteUnwinderObject *unwinder, uint64_t tid, uint64_t pthread_id) { -#if defined(__APPLE__) && TARGET_OS_OSX - if (unwinder->thread_id_offset == 0) { - uint64_t *tids = (uint64_t *)PyMem_Malloc(MAX_NATIVE_THREADS * sizeof(uint64_t)); - if (!tids) { - PyErr_NoMemory(); - return -1; - } - int n = proc_pidinfo(unwinder->handle.pid, PROC_PIDLISTTHREADS, 0, tids, MAX_NATIVE_THREADS * sizeof(uint64_t)) / sizeof(uint64_t); - if (n <= 0) { - PyMem_Free(tids); - return THREAD_STATE_UNKNOWN; - } - uint64_t min_offset = UINT64_MAX; - for (int i = 0; i < n; i++) { - uint64_t offset = tids[i] - pthread_id; - if (offset < min_offset) { - min_offset = offset; - } - } - unwinder->thread_id_offset = min_offset; - PyMem_Free(tids); - } - struct proc_threadinfo ti; - uint64_t tid_with_offset = pthread_id + unwinder->thread_id_offset; - if (proc_pidinfo(unwinder->handle.pid, PROC_PIDTHREADINFO, tid_with_offset, &ti, sizeof(ti)) != sizeof(ti)) { - return THREAD_STATE_UNKNOWN; - } - if (ti.pth_run_state == TH_STATE_RUNNING) { - return THREAD_STATE_RUNNING; - } - return THREAD_STATE_IDLE; -#elif defined(__linux__) && HAVE_PROCESS_VM_READV - char stat_path[256]; - char buffer[2048] = ""; - - snprintf(stat_path, sizeof(stat_path), "/proc/%d/task/%lu/stat", unwinder->handle.pid, tid); - - int fd = open(stat_path, O_RDONLY); - if (fd == -1) { - return THREAD_STATE_UNKNOWN; - } - - if (read(fd, buffer, 2047) == 0) { - close(fd); - return THREAD_STATE_UNKNOWN; - } - close(fd); - - char *p = strchr(buffer, ')'); - if (!p) { - return THREAD_STATE_UNKNOWN; - } - - p += 2; // Skip ") " - if (*p == ' ') { - p++; - } - - switch (*p) { - case 'R': // Running - return THREAD_STATE_RUNNING; - case 'S': // Interruptible sleep - case 'D': // Uninterruptible sleep - case 'T': // Stopped - case 'Z': // Zombie - case 'I': // Idle kernel thread - return THREAD_STATE_IDLE; - default: - return THREAD_STATE_UNKNOWN; - } -#elif defined(MS_WINDOWS) - ULONG n; - NTSTATUS status = NtQuerySystemInformation( - SystemProcessInformation, - unwinder->win_process_buffer, - unwinder->win_process_buffer_size, - &n - ); - if (status == STATUS_INFO_LENGTH_MISMATCH) { - // Buffer was too small so we reallocate a larger one and try again. - unwinder->win_process_buffer_size = n; - PVOID new_buffer = PyMem_Realloc(unwinder->win_process_buffer, n); - if (!new_buffer) { - return -1; - } - unwinder->win_process_buffer = new_buffer; - return get_thread_status(unwinder, tid, pthread_id); - } - if (status != STATUS_SUCCESS) { - return -1; - } - - SYSTEM_PROCESS_INFORMATION *pi = (SYSTEM_PROCESS_INFORMATION *)unwinder->win_process_buffer; - while ((ULONG)(ULONG_PTR)pi->UniqueProcessId != unwinder->handle.pid) { - if (pi->NextEntryOffset == 0) { - // We didn't find the process - return -1; - } - pi = (SYSTEM_PROCESS_INFORMATION *)(((BYTE *)pi) + pi->NextEntryOffset); - } - - SYSTEM_THREAD_INFORMATION *ti = (SYSTEM_THREAD_INFORMATION *)((char *)pi + sizeof(SYSTEM_PROCESS_INFORMATION)); - for (Py_ssize_t i = 0; i < pi->NumberOfThreads; i++, ti++) { - if (ti->ClientId.UniqueThread == (HANDLE)tid) { - return ti->ThreadState != WIN32_THREADSTATE_RUNNING ? THREAD_STATE_IDLE : THREAD_STATE_RUNNING; - } - } - - return -1; -#else - return THREAD_STATE_UNKNOWN; -#endif -} - -typedef 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; -} _thread_status; - ->>>>>>> Stashed changes static PyObject* unwind_stack_for_thread( RemoteUnwinderObject *unwinder,