Skip to content

gh-91048: Chain some exceptions in _testexternalinspection.c #132970

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 2, 2025
68 changes: 38 additions & 30 deletions Modules/_testexternalinspection.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ struct _Py_AsyncioModuleDebugOffsets {
} asyncio_thread_state;
};

// Helper to chain exceptions and avoid repetitions
static void
chain_exceptions(PyObject *exception, const char *string)
{
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(exception, string);
_PyErr_ChainExceptions1(exc);
}

// Get the PyAsyncioDebug section address for any platform
static uintptr_t
_Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle)
Expand All @@ -65,7 +74,7 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle)
address = search_map_for_section(handle, "AsyncioDebug", "_asyncio.cpython");
}
#else
address = 0;
Py_UNREACHABLE();
#endif

return address;
Expand Down Expand Up @@ -304,7 +313,7 @@ parse_task_name(
if ((flags & Py_TPFLAGS_LONG_SUBCLASS)) {
long res = read_py_long(handle, offsets, task_name_addr);
if (res == -1) {
PyErr_SetString(PyExc_RuntimeError, "Failed to get task name");
chain_exceptions(PyExc_RuntimeError, "Failed to get task name");
return NULL;
}
return PyUnicode_FromFormat("Task-%d", res);
Expand Down Expand Up @@ -482,9 +491,6 @@ parse_task(
return -1;
}

uintptr_t refcnt;
read_ptr(handle, task_address + sizeof(Py_ssize_t), &refcnt);

PyObject* result = PyList_New(0);
if (result == NULL) {
return -1;
Expand Down Expand Up @@ -1159,30 +1165,32 @@ get_all_awaited_by(PyObject* self, PyObject* args)
return 0;
}

PyObject *result = NULL;

uintptr_t runtime_start_addr = _Py_RemoteDebug_GetPyRuntimeAddress(handle);
if (runtime_start_addr == 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(
PyExc_RuntimeError, "Failed to get .PyRuntime address");
}
return NULL;
goto result_err;
}
struct _Py_DebugOffsets local_debug_offsets;

if (_Py_RemoteDebug_ReadDebugOffsets(handle, &runtime_start_addr, &local_debug_offsets)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read debug offsets");
return NULL;
chain_exceptions(PyExc_RuntimeError, "Failed to read debug offsets");
goto result_err;
}

struct _Py_AsyncioModuleDebugOffsets local_async_debug;
if (read_async_debug(handle, &local_async_debug)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read asyncio debug offsets");
return NULL;
chain_exceptions(PyExc_RuntimeError, "Failed to read asyncio debug offsets");
goto result_err;
}

PyObject *result = PyList_New(0);
result = PyList_New(0);
if (result == NULL) {
return NULL;
goto result_err;
}

uint64_t interpreter_state_list_head =
Expand Down Expand Up @@ -1259,7 +1267,7 @@ get_all_awaited_by(PyObject* self, PyObject* args)
return result;

result_err:
Py_DECREF(result);
Py_XDECREF(result);
_Py_RemoteDebug_CleanupProcHandle(handle);
return NULL;
}
Expand Down Expand Up @@ -1299,7 +1307,7 @@ get_stack_trace(PyObject* self, PyObject* args)
struct _Py_DebugOffsets local_debug_offsets;

if (_Py_RemoteDebug_ReadDebugOffsets(handle, &runtime_start_address, &local_debug_offsets)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read debug offsets");
chain_exceptions(PyExc_RuntimeError, "Failed to read debug offsets");
goto result_err;
}

Expand Down Expand Up @@ -1357,48 +1365,48 @@ get_async_stack_trace(PyObject* self, PyObject* args)
return 0;
}

PyObject *result = NULL;

uintptr_t runtime_start_address = _Py_RemoteDebug_GetPyRuntimeAddress(handle);
if (runtime_start_address == 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(
PyExc_RuntimeError, "Failed to get .PyRuntime address");
}
return NULL;
goto result_err;
}
struct _Py_DebugOffsets local_debug_offsets;

if (_Py_RemoteDebug_ReadDebugOffsets(handle, &runtime_start_address, &local_debug_offsets)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read debug offsets");
return NULL;
chain_exceptions(PyExc_RuntimeError, "Failed to read debug offsets");
goto result_err;
}

struct _Py_AsyncioModuleDebugOffsets local_async_debug;
if (read_async_debug(handle, &local_async_debug)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read asyncio debug offsets");
return NULL;
chain_exceptions(PyExc_RuntimeError, "Failed to read asyncio debug offsets");
goto result_err;
}

PyObject* result = PyList_New(1);
result = PyList_New(1);
if (result == NULL) {
return NULL;
goto result_err;
}
PyObject* calls = PyList_New(0);
if (calls == NULL) {
Py_DECREF(result);
return NULL;
goto result_err;
}
if (PyList_SetItem(result, 0, calls)) { /* steals ref to 'calls' */
Py_DECREF(result);
Py_DECREF(calls);
return NULL;
goto result_err;
}

uintptr_t running_task_addr = (uintptr_t)NULL;
if (find_running_task(
handle, runtime_start_address, &local_debug_offsets, &local_async_debug,
&running_task_addr)
) {
PyErr_SetString(PyExc_RuntimeError, "Failed to find running task");
chain_exceptions(PyExc_RuntimeError, "Failed to find running task");
goto result_err;
}

Expand All @@ -1413,7 +1421,7 @@ get_async_stack_trace(PyObject* self, PyObject* args)
running_task_addr + local_async_debug.asyncio_task_object.task_coro,
&running_coro_addr
)) {
PyErr_SetString(PyExc_RuntimeError, "Failed to read running task coro");
chain_exceptions(PyExc_RuntimeError, "Failed to read running task coro");
goto result_err;
}

Expand Down Expand Up @@ -1443,7 +1451,7 @@ get_async_stack_trace(PyObject* self, PyObject* args)
handle, runtime_start_address, &local_debug_offsets,
&address_of_current_frame)
) {
PyErr_SetString(PyExc_RuntimeError, "Failed to find running frame");
chain_exceptions(PyExc_RuntimeError, "Failed to find running frame");
goto result_err;
}

Expand All @@ -1459,7 +1467,7 @@ get_async_stack_trace(PyObject* self, PyObject* args)
);

if (res < 0) {
PyErr_SetString(PyExc_RuntimeError, "Failed to parse async frame object");
chain_exceptions(PyExc_RuntimeError, "Failed to parse async frame object");
goto result_err;
}

Expand Down Expand Up @@ -1501,7 +1509,7 @@ get_async_stack_trace(PyObject* self, PyObject* args)

result_err:
_Py_RemoteDebug_CleanupProcHandle(handle);
Py_DECREF(result);
Py_XDECREF(result);
return NULL;
}

Expand Down
17 changes: 14 additions & 3 deletions Python/remote_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
munmap(map, fs.st_size);
if (close(fd) != 0) {
PyErr_SetFromErrno(PyExc_OSError);
result = 0;
}
return result;
}
Expand Down Expand Up @@ -371,7 +372,9 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s

mach_port_t proc_ref = pid_to_task(handle->pid);
if (proc_ref == 0) {
PyErr_SetString(PyExc_PermissionError, "Cannot get task for PID");
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_PermissionError, "Cannot get task for PID");
}
return 0;
}

Expand Down Expand Up @@ -495,6 +498,7 @@ search_elf_file_for_section(
}
if (fd >= 0 && close(fd) != 0) {
PyErr_SetFromErrno(PyExc_OSError);
result = 0;
}
return result;
}
Expand Down Expand Up @@ -570,7 +574,10 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
}

PyMem_Free(line);
fclose(maps_file);
if (fclose(maps_file) != 0) {
PyErr_SetFromErrno(PyExc_OSError);
retval = 0;
}

return retval;
}
Expand Down Expand Up @@ -681,14 +688,18 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
address = search_windows_map_for_section(handle, "PyRuntime", L"python");
if (address == 0) {
// Error out: 'python' substring covers both executable and DLL
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(PyExc_RuntimeError, "Failed to find the PyRuntime section in the process.");
_PyErr_ChainExceptions1(exc);
}
#elif defined(__linux__)
// On Linux, search for 'python' in executable or DLL
address = search_linux_map_for_section(handle, "PyRuntime", "python");
if (address == 0) {
// Error out: 'python' substring covers both executable and DLL
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(PyExc_RuntimeError, "Failed to find the PyRuntime section in the process.");
_PyErr_ChainExceptions1(exc);
}
#elif defined(__APPLE__) && TARGET_OS_OSX
// On macOS, try libpython first, then fall back to python
Expand All @@ -699,7 +710,7 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
address = search_map_for_section(handle, "PyRuntime", "python");
}
#else
address = 0;
Py_UNREACHABLE();
#endif

return address;
Expand Down
Loading