diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 96ba54b274c804..115dd40b8bdfce 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -373,6 +373,10 @@ PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyS #endif #endif +#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG) +extern int _PyRunRemoteDebugger(PyThreadState *tstate); +#endif + #ifdef __cplusplus } #endif diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index dc6485878b7cb4..54bcd3270ef31a 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1777,6 +1777,10 @@ PyErr_CheckSignals(void) _Py_RunGC(tstate); } +#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG) + _PyRunRemoteDebugger(tstate); +#endif + if (!_Py_ThreadCanHandleSignals(tstate->interp)) { return 0; } diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index ac254363058ec6..1040433663361f 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -1242,6 +1242,38 @@ static inline void run_remote_debugger_script(const char *path) PyErr_FormatUnraisable("Error executing debugger script %s", path); } } + +int _PyRunRemoteDebugger(PyThreadState *tstate) +{ + const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); + if (config->remote_debug == 1 + && tstate->remote_debugger_support.debugger_pending_call == 1) + { + tstate->remote_debugger_support.debugger_pending_call = 0; + + // Immediately make a copy in case of a race with another debugger + // process that's trying to write to the buffer. At least this way + // we'll be internally consistent: what we audit is what we run. + const size_t pathsz + = sizeof(tstate->remote_debugger_support.debugger_script_path); + + char *path = PyMem_Malloc(pathsz); + if (path) { + // And don't assume the debugger correctly null terminated it. + memcpy( + path, + tstate->remote_debugger_support.debugger_script_path, + pathsz); + path[pathsz - 1] = '\0'; + if (*path) { + run_remote_debugger_script(path); + } + PyMem_Free(path); + } + } + return 0; +} + #endif /* Do periodic things, like check for signals and async I/0. @@ -1372,32 +1404,7 @@ _Py_HandlePending(PyThreadState *tstate) } #if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG) - const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); - if (config->remote_debug == 1 - && tstate->remote_debugger_support.debugger_pending_call == 1) - { - tstate->remote_debugger_support.debugger_pending_call = 0; - - // Immediately make a copy in case of a race with another debugger - // process that's trying to write to the buffer. At least this way - // we'll be internally consistent: what we audit is what we run. - const size_t pathsz - = sizeof(tstate->remote_debugger_support.debugger_script_path); - - char *path = PyMem_Malloc(pathsz); - if (path) { - // And don't assume the debugger correctly null terminated it. - memcpy( - path, - tstate->remote_debugger_support.debugger_script_path, - pathsz); - path[pathsz - 1] = '\0'; - if (*path) { - run_remote_debugger_script(path); - } - PyMem_Free(path); - } - } + _PyRunRemoteDebugger(tstate); #endif return 0;