From 9d88da6c2bc40b17215137d9b2face5aafdfae3d Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 10 Jun 2024 15:18:15 +0200 Subject: [PATCH] Debugger: simplify interrupting windows processes The win32interrupt.exe is gone. And since we do not support 32bit Qt Creator builds anymore there is no purpose for a win64interrupt.exe. Since some 'recent' changes interrupting the cdb works now by simply setting Process::setUseCtrlCStub and calling Process::interrupt on the debugger process. The same seems to work with gdb nowadays, but we need to expect the SIGINT signal that gdb receives in this situation. Otherwise the user will be shown a message box about the emission of that signal. Change-Id: I7a21d7ea34409d8bbf62d94e28387d591e7feb3d Reviewed-by: Christian Stenger --- src/plugins/debugger/gdb/gdbengine.cpp | 39 ++---- src/plugins/debugger/pdb/pdbengine.cpp | 2 +- src/plugins/debugger/procinterrupt.cpp | 132 ++---------------- src/plugins/debugger/procinterrupt.h | 3 +- .../desktopprocesssignaloperation.cpp | 96 ++----------- 5 files changed, 36 insertions(+), 236 deletions(-) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 1079fb24eaf..ab173cc0eab 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -124,6 +124,7 @@ const char notCompatibleMessage[] = "is not compatible with target architecture" GdbEngine::GdbEngine() { m_gdbProc.setProcessMode(ProcessMode::Writer); + m_gdbProc.setUseCtrlCStub(true); setObjectName("GdbEngine"); setDebuggerName("GDB"); @@ -673,26 +674,7 @@ void GdbEngine::interruptInferior() } else { showStatusMessage(Tr::tr("Stop requested..."), 5000); showMessage("TRYING TO INTERRUPT INFERIOR"); - if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) { - IDevice::ConstPtr dev = device(); - QTC_ASSERT(dev, notifyInferiorStopFailed(); return); - DeviceProcessSignalOperation::Ptr signalOperation = dev->signalOperation(); - QTC_ASSERT(signalOperation, notifyInferiorStopFailed(); return); - connect(signalOperation.get(), &DeviceProcessSignalOperation::finished, - this, [this, signalOperation](const QString &error) { - if (error.isEmpty()) { - showMessage("Interrupted " + QString::number(inferiorPid())); - notifyInferiorStopOk(); - } else { - showMessage(error, LogError); - notifyInferiorStopFailed(); - } - }); - signalOperation->setDebuggerCommand(runParameters().debugger.command.executable()); - signalOperation->interruptProcess(inferiorPid()); - } else { - interruptInferior2(); - } + interruptInferior2(); } } @@ -1265,9 +1247,11 @@ void GdbEngine::handleStopResponse(const GdbMi &data) handleStop1(data); } -static QString stopSignal(const Abi &abi) +static QStringList stopSignals(const Abi &abi) { - return QLatin1String(abi.os() == Abi::WindowsOS ? "SIGTRAP" : "SIGINT"); + static QStringList winSignals = { "SIGTRAP", "SIGINT" }; + static QStringList unixSignals = { "SIGINT" }; + return abi.os() == Abi::WindowsOS ? winSignals : unixSignals; } void GdbEngine::handleStop1(const GdbMi &data) @@ -1416,7 +1400,7 @@ void GdbEngine::handleStop2(const GdbMi &data) QString meaning = data["signal-meaning"].data(); // Ignore these as they are showing up regularly when // stopping debugging. - if (name == stopSignal(rp.toolChainAbi) || rp.expectedSignals.contains(name)) { + if (stopSignals(rp.toolChainAbi).contains(name) || rp.expectedSignals.contains(name)) { showMessage(name + " CONSIDERED HARMLESS. CONTINUING."); } else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") { showMessage("SIGNAL 0 CONSIDERED BOGUS."); @@ -3817,9 +3801,6 @@ void GdbEngine::setupEngine() CHECK_STATE(EngineSetupRequested); showMessage("TRYING TO START ADAPTER"); - if (isRemoteEngine()) - m_gdbProc.setUseCtrlCStub(runParameters().useCtrlCStub); // This is only set for QNX - const DebuggerRunParameters &rp = runParameters(); CommandLine gdbCommand = rp.debugger.command; @@ -4314,7 +4295,6 @@ void GdbEngine::interruptLocalInferior(qint64 pid) showMessage("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED", LogError); return; } - QString errorMessage; if (runParameters().runAsRoot) { Environment env = Environment::systemEnvironment(); RunControl::provideAskPassEntry(env); @@ -4323,11 +4303,8 @@ void GdbEngine::interruptLocalInferior(qint64 pid) proc.setEnvironment(env); proc.start(); proc.waitForFinished(); - } else if (interruptProcess(pid, GdbEngineType, &errorMessage)) { - showMessage("Interrupted " + QString::number(pid)); } else { - showMessage(errorMessage, LogError); - notifyInferiorStopFailed(); + m_gdbProc.interrupt(); } } diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index e0bfa126f5a..657447f17e0 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -143,7 +143,7 @@ void PdbEngine::handlePdbStarted() void PdbEngine::interruptInferior() { QString error; - interruptProcess(m_proc.processId(), GdbEngineType, &error); + interruptProcess(m_proc.processId(), &error); } void PdbEngine::executeStepIn(bool) diff --git a/src/plugins/debugger/procinterrupt.cpp b/src/plugins/debugger/procinterrupt.cpp index 113fbeefc4f..0f104f3bfc0 100644 --- a/src/plugins/debugger/procinterrupt.cpp +++ b/src/plugins/debugger/procinterrupt.cpp @@ -23,128 +23,23 @@ static inline QString msgCannotInterrupt(qint64 pid, const QString &why) # define PROCESS_SUSPEND_RESUME (0x0800) #endif // PROCESS_SUSPEND_RESUME -static BOOL isWow64Process(HANDLE hproc) -{ - using LPFN_ISWOW64PROCESS = BOOL (WINAPI*)(HANDLE, PBOOL); - - BOOL ret = false; - - static LPFN_ISWOW64PROCESS fnIsWow64Process = NULL; - if (!fnIsWow64Process) { - if (HMODULE hModule = GetModuleHandle(L"kernel32.dll")) - fnIsWow64Process = reinterpret_cast(GetProcAddress(hModule, "IsWow64Process")); - } - - if (!fnIsWow64Process) { - qWarning("Cannot retrieve symbol 'IsWow64Process'."); - return false; - } - - if (!fnIsWow64Process(hproc, &ret)) { - qWarning("IsWow64Process() failed for %p: %s", - hproc, qPrintable(Utils::winErrorMessage(GetLastError()))); - return false; - } - return ret; -} - // Open the process and break into it -bool Debugger::Internal::interruptProcess(qint64 pID, int engineType, QString *errorMessage, const bool engineExecutableIs64Bit) +bool Debugger::Internal::interruptProcess(qint64 pID, QString *errorMessage) { bool ok = false; HANDLE inferior = NULL; - do { - const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION - |PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ - |PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME; - inferior = OpenProcess(rights, FALSE, DWORD(pID)); - if (inferior == NULL) { - *errorMessage = QString::fromLatin1("Cannot open process %1: %2"). - arg(pID).arg(Utils::winErrorMessage(GetLastError())); - break; - } + const DWORD rights = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_DUP_HANDLE + | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SUSPEND_RESUME; + inferior = OpenProcess(rights, FALSE, DWORD(pID)); + if (inferior == NULL) { + *errorMessage = QString::fromLatin1("Cannot open process %1: %2") + .arg(pID) + .arg(Utils::winErrorMessage(GetLastError())); + } else if (ok = DebugBreakProcess(inferior); !ok) { + *errorMessage = "DebugBreakProcess failed: " + Utils::winErrorMessage(GetLastError()); + } - enum DebugBreakApi { - UseDebugBreakApi, - UseWin64Interrupt, - UseWin32Interrupt - }; -/* - Windows 64 bit has a 32 bit subsystem (WOW64) which makes it possible to run a - 32 bit application inside a 64 bit environment. - When GDB is used DebugBreakProcess must be called from the same system (32/64 bit) running - the inferior. If CDB is used we could in theory break wow64 processes, - but the break is actually a wow64 breakpoint. CDB is configured to ignore these - breakpoints, because they also appear on module loading. - Therefore we need helper executables (win(32/64)interrupt.exe) on Windows 64 bit calling - DebugBreakProcess from the correct system. - - DebugBreak matrix for windows - - Api = UseDebugBreakApi - Win64 = UseWin64Interrupt - Win32 = UseWin32Interrupt - N/A = This configuration is not possible - - | Windows 32bit | Windows 64bit - | QtCreator 32bit | QtCreator 32bit | QtCreator 64bit - | Inferior 32bit | Inferior 32bit | Inferior 64bit | Inferior 32bit | Inferior 64bit | -----------|-----------------|-----------------|-----------------|-----------------|----------------| -CDB 32bit | Api | Api | NA | Win32 | NA | - 64bit | NA | Win64 | Win64 | Api | Api | -----------|-----------------|-----------------|-----------------|-----------------|----------------| -GDB 32bit | Api | Api | NA | Win32 | NA | - 64bit | NA | Api | Win64 | Win32 | Api | -----------|-----------------|-----------------|-----------------|-----------------|----------------| - -*/ - - DebugBreakApi breakApi = UseDebugBreakApi; -#ifdef Q_OS_WIN64 - if ((engineType == GdbEngineType && isWow64Process(inferior)) - || (engineType == CdbEngineType && !engineExecutableIs64Bit)) { - breakApi = UseWin32Interrupt; - } -#else - if (isWow64Process(GetCurrentProcess()) - && ((engineType == CdbEngineType && engineExecutableIs64Bit) - || (engineType == GdbEngineType && !isWow64Process(inferior)))) { - breakApi = UseWin64Interrupt; - } -#endif - if (breakApi == UseDebugBreakApi) { - ok = DebugBreakProcess(inferior); - if (!ok) - *errorMessage = "DebugBreakProcess failed: " + Utils::winErrorMessage(GetLastError()); - } else { - const QString executable = breakApi == UseWin32Interrupt - ? QCoreApplication::applicationDirPath() + "/win32interrupt.exe" - : QCoreApplication::applicationDirPath() + "/win64interrupt.exe"; - if (!QFileInfo::exists(executable)) { - *errorMessage = QString::fromLatin1( - "%1 does not exist. If you have built %2 " - "on your own, checkout " - "https://code.qt.io/cgit/qt-creator/binary-artifacts.git/.") - .arg(QDir::toNativeSeparators(executable), - QGuiApplication::applicationDisplayName()); - break; - } - switch (QProcess::execute(executable, QStringList(QString::number(pID)))) { - case -2: - *errorMessage = QString::fromLatin1("Cannot start %1. Check src\\tools\\win64interrupt\\win64interrupt.c for more information."). - arg(QDir::toNativeSeparators(executable)); - break; - case 0: - ok = true; - break; - default: - *errorMessage = QDir::toNativeSeparators(executable) - + " could not break the process."; - break; - } - break; - } - } while (false); if (inferior != NULL) CloseHandle(inferior); if (!ok) @@ -159,8 +54,7 @@ GDB 32bit | Api | Api | NA | Win32 #include #include -bool Debugger::Internal::interruptProcess(qint64 pID, int /* engineType */, - QString *errorMessage, const bool /*engineExecutableIs64Bit*/) +bool Debugger::Internal::interruptProcess(qint64 pID, QString *errorMessage) { if (pID <= 0) { *errorMessage = msgCannotInterrupt(pID, QString::fromLatin1("Invalid process id.")); diff --git a/src/plugins/debugger/procinterrupt.h b/src/plugins/debugger/procinterrupt.h index c7872ed2e42..ef4d20300db 100644 --- a/src/plugins/debugger/procinterrupt.h +++ b/src/plugins/debugger/procinterrupt.h @@ -7,7 +7,6 @@ namespace Debugger::Internal { -bool interruptProcess(qint64 pID, int engineType, QString *errorMessage, - const bool engineExecutableIs64Bit = false); +bool interruptProcess(qint64 pID, QString *errorMessage); } // Debugger::Internal diff --git a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp index 70412124462..773f4d3bca5 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp @@ -102,90 +102,20 @@ void DesktopProcessSignalOperation::killProcessSilently(qint64 pid) void DesktopProcessSignalOperation::interruptProcessSilently(qint64 pid) { #ifdef Q_OS_WIN - enum SpecialInterrupt { NoSpecialInterrupt, Win32Interrupt, Win64Interrupt }; - - bool is64BitSystem = is64BitWindowsSystem(); - SpecialInterrupt si = NoSpecialInterrupt; - if (is64BitSystem) - si = is64BitWindowsBinary(m_debuggerCommand) ? Win64Interrupt : Win32Interrupt; - /* - Windows 64 bit has a 32 bit subsystem (WOW64) which makes it possible to run a - 32 bit application inside a 64 bit environment. - When GDB is used DebugBreakProcess must be called from the same system (32/64 bit) running - the inferior. If CDB is used we could in theory break wow64 processes, - but the break is actually a wow64 breakpoint. CDB is configured to ignore these - breakpoints, because they also appear on module loading. - Therefore we need helper executables (win(32/64)interrupt.exe) on Windows 64 bit calling - DebugBreakProcess from the correct system. - - DebugBreak matrix for windows - - Api = UseDebugBreakApi - Win64 = UseWin64InterruptHelper - Win32 = UseWin32InterruptHelper - N/A = This configuration is not possible - - | Windows 32bit | Windows 64bit - | QtCreator 32bit | QtCreator 32bit | QtCreator 64bit - | Inferior 32bit | Inferior 32bit | Inferior 64bit | Inferior 32bit | Inferior 64bit -----------|-----------------|-----------------|-----------------|-----------------|---------------- -CDB 32bit | Api | Api | N/A | Win32 | N/A - 64bit | N/A | Win64 | Win64 | Api | Api -----------|-----------------|-----------------|-----------------|-----------------|---------------- -GDB 32bit | Api | Api | N/A | Win32 | N/A - 64bit | N/A | N/A | Win64 | N/A | Api -----------|-----------------|-----------------|-----------------|-----------------|---------------- - - */ HANDLE inferior = NULL; - do { - const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION - |PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ - |PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME; - inferior = OpenProcess(rights, FALSE, pid); - if (inferior == NULL) { - appendMsgCannotInterrupt(pid, Tr::tr("Cannot open process: %1") - + winErrorMessage(GetLastError())); - break; - } - bool creatorIs64Bit = is64BitWindowsBinary( - FilePath::fromUserInput(QCoreApplication::applicationFilePath())); - if (!is64BitSystem - || si == NoSpecialInterrupt - || (si == Win64Interrupt && creatorIs64Bit) - || (si == Win32Interrupt && !creatorIs64Bit)) { - if (!DebugBreakProcess(inferior)) { - appendMsgCannotInterrupt(pid, Tr::tr("DebugBreakProcess failed:") - + QLatin1Char(' ') + winErrorMessage(GetLastError())); - } - } else if (si == Win32Interrupt || si == Win64Interrupt) { - QString executable = QCoreApplication::applicationDirPath(); - executable += si == Win32Interrupt - ? QLatin1String("/win32interrupt.exe") - : QLatin1String("/win64interrupt.exe"); - if (!QFileInfo::exists(executable)) { - appendMsgCannotInterrupt(pid, - Tr::tr("%1 does not exist. If you built %2 " - "yourself, check out https://code.qt.io/cgit/" - "qt-creator/binary-artifacts.git/.") - .arg(QDir::toNativeSeparators(executable), - QGuiApplication::applicationDisplayName())); - } - switch (QProcess::execute(executable, QStringList(QString::number(pid)))) { - case -2: - appendMsgCannotInterrupt(pid, Tr::tr( - "Cannot start %1. Check src\\tools\\win64interrupt\\win64interrupt.c " - "for more information.").arg(QDir::toNativeSeparators(executable))); - break; - case 0: - break; - default: - appendMsgCannotInterrupt(pid, QDir::toNativeSeparators(executable) - + QLatin1Char(' ') + Tr::tr("could not break the process.")); - break; - } - } - } while (false); + const DWORD rights = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_DUP_HANDLE + | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SUSPEND_RESUME; + inferior = OpenProcess(rights, FALSE, pid); + if (inferior == NULL) { + appendMsgCannotInterrupt( + pid, Tr::tr("Cannot open process: %1") + winErrorMessage(GetLastError())); + } else if (!DebugBreakProcess(inferior)) { + appendMsgCannotInterrupt( + pid, + Tr::tr("DebugBreakProcess failed:") + QLatin1Char(' ') + + winErrorMessage(GetLastError())); + } if (inferior != NULL) CloseHandle(inferior); #else