diff options
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp')
-rw-r--r-- | lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 37b2868..ed4ad27 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -70,6 +70,14 @@ extern "C" void init_lldb(void); #define LLDBSwigPyInit init_lldb #endif +#if defined(_WIN32) +// Don't mess with the signal handlers on Windows. +#define LLDB_USE_PYTHON_SET_INTERRUPT 0 +#else +// PyErr_SetInterrupt was introduced in 3.2. +#define LLDB_USE_PYTHON_SET_INTERRUPT \ + (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || (PY_MAJOR_VERSION > 3) +#endif static ScriptInterpreterPythonImpl *GetPythonInterpreter(Debugger &debugger) { ScriptInterpreter *script_interpreter = @@ -920,6 +928,22 @@ void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() { } bool ScriptInterpreterPythonImpl::Interrupt() { +#if LLDB_USE_PYTHON_SET_INTERRUPT + // If the interpreter isn't evaluating any Python at the moment then return + // false to signal that this function didn't handle the interrupt and the + // next component should try handling it. + if (!IsExecutingPython()) + return false; + + // Tell Python that it should pretend to have received a SIGINT. + PyErr_SetInterrupt(); + // PyErr_SetInterrupt has no way to return an error so we can only pretend the + // signal got successfully handled and return true. + // Python 3.10 introduces PyErr_SetInterruptEx that could return an error, but + // the error handling is limited to checking the arguments which would be + // just our (hardcoded) input signal code SIGINT, so that's not useful at all. + return true; +#else Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (IsExecutingPython()) { @@ -941,6 +965,7 @@ bool ScriptInterpreterPythonImpl::Interrupt() { "ScriptInterpreterPythonImpl::Interrupt() python code not running, " "can't interrupt"); return false; +#endif } bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn( @@ -3144,6 +3169,30 @@ ScriptInterpreterPythonImpl::AcquireInterpreterLock() { return py_lock; } +#if LLDB_USE_PYTHON_SET_INTERRUPT +namespace { +/// Saves the current signal handler for the specified signal and restores +/// it at the end of the current scope. +struct RestoreSignalHandlerScope { + /// The signal handler. + struct sigaction m_prev_handler; + int m_signal_code; + RestoreSignalHandlerScope(int signal_code) : m_signal_code(signal_code) { + // Initialize sigaction to their default state. + std::memset(&m_prev_handler, 0, sizeof(m_prev_handler)); + // Don't install a new handler, just read back the old one. + struct sigaction *new_handler = nullptr; + int signal_err = ::sigaction(m_signal_code, new_handler, &m_prev_handler); + lldbassert(signal_err == 0 && "sigaction failed to read handler"); + } + ~RestoreSignalHandlerScope() { + int signal_err = ::sigaction(m_signal_code, &m_prev_handler, nullptr); + lldbassert(signal_err == 0 && "sigaction failed to restore old handler"); + } +}; +} // namespace +#endif + void ScriptInterpreterPythonImpl::InitializePrivate() { if (g_initialized) return; @@ -3179,6 +3228,25 @@ void ScriptInterpreterPythonImpl::InitializePrivate() { "lldb.embedded_interpreter; from " "lldb.embedded_interpreter import run_python_interpreter; " "from lldb.embedded_interpreter import run_one_line"); + +#if LLDB_USE_PYTHON_SET_INTERRUPT + // Python will not just overwrite its internal SIGINT handler but also the + // one from the process. Backup the current SIGINT handler to prevent that + // Python deletes it. + RestoreSignalHandlerScope save_sigint(SIGINT); + + // Setup a default SIGINT signal handler that works the same way as the + // normal Python REPL signal handler which raises a KeyboardInterrupt. + // Also make sure to not pollute the user's REPL with the signal module nor + // our utility function. + PyRun_SimpleString("def lldb_setup_sigint_handler():\n" + " import signal;\n" + " def signal_handler(sig, frame):\n" + " raise KeyboardInterrupt()\n" + " signal.signal(signal.SIGINT, signal_handler);\n" + "lldb_setup_sigint_handler();\n" + "del lldb_setup_sigint_handler\n"); +#endif } void ScriptInterpreterPythonImpl::AddToSysPath(AddLocation location, |