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 <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2024-06-10 15:18:15 +02:00
parent 0f983f2e95
commit 9d88da6c2b
5 changed files with 36 additions and 236 deletions

View File

@@ -124,6 +124,7 @@ const char notCompatibleMessage[] = "is not compatible with target architecture"
GdbEngine::GdbEngine() GdbEngine::GdbEngine()
{ {
m_gdbProc.setProcessMode(ProcessMode::Writer); m_gdbProc.setProcessMode(ProcessMode::Writer);
m_gdbProc.setUseCtrlCStub(true);
setObjectName("GdbEngine"); setObjectName("GdbEngine");
setDebuggerName("GDB"); setDebuggerName("GDB");
@@ -673,28 +674,9 @@ void GdbEngine::interruptInferior()
} else { } else {
showStatusMessage(Tr::tr("Stop requested..."), 5000); showStatusMessage(Tr::tr("Stop requested..."), 5000);
showMessage("TRYING TO INTERRUPT INFERIOR"); 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();
} }
} }
}
void GdbEngine::runCommand(const DebuggerCommand &command) void GdbEngine::runCommand(const DebuggerCommand &command)
{ {
@@ -1265,9 +1247,11 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
handleStop1(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) void GdbEngine::handleStop1(const GdbMi &data)
@@ -1416,7 +1400,7 @@ void GdbEngine::handleStop2(const GdbMi &data)
QString meaning = data["signal-meaning"].data(); QString meaning = data["signal-meaning"].data();
// Ignore these as they are showing up regularly when // Ignore these as they are showing up regularly when
// stopping debugging. // 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."); showMessage(name + " CONSIDERED HARMLESS. CONTINUING.");
} else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") { } else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") {
showMessage("SIGNAL 0 CONSIDERED BOGUS."); showMessage("SIGNAL 0 CONSIDERED BOGUS.");
@@ -3817,9 +3801,6 @@ void GdbEngine::setupEngine()
CHECK_STATE(EngineSetupRequested); CHECK_STATE(EngineSetupRequested);
showMessage("TRYING TO START ADAPTER"); showMessage("TRYING TO START ADAPTER");
if (isRemoteEngine())
m_gdbProc.setUseCtrlCStub(runParameters().useCtrlCStub); // This is only set for QNX
const DebuggerRunParameters &rp = runParameters(); const DebuggerRunParameters &rp = runParameters();
CommandLine gdbCommand = rp.debugger.command; CommandLine gdbCommand = rp.debugger.command;
@@ -4314,7 +4295,6 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
showMessage("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED", LogError); showMessage("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED", LogError);
return; return;
} }
QString errorMessage;
if (runParameters().runAsRoot) { if (runParameters().runAsRoot) {
Environment env = Environment::systemEnvironment(); Environment env = Environment::systemEnvironment();
RunControl::provideAskPassEntry(env); RunControl::provideAskPassEntry(env);
@@ -4323,11 +4303,8 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
proc.setEnvironment(env); proc.setEnvironment(env);
proc.start(); proc.start();
proc.waitForFinished(); proc.waitForFinished();
} else if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
showMessage("Interrupted " + QString::number(pid));
} else { } else {
showMessage(errorMessage, LogError); m_gdbProc.interrupt();
notifyInferiorStopFailed();
} }
} }

View File

@@ -143,7 +143,7 @@ void PdbEngine::handlePdbStarted()
void PdbEngine::interruptInferior() void PdbEngine::interruptInferior()
{ {
QString error; QString error;
interruptProcess(m_proc.processId(), GdbEngineType, &error); interruptProcess(m_proc.processId(), &error);
} }
void PdbEngine::executeStepIn(bool) void PdbEngine::executeStepIn(bool)

View File

@@ -23,128 +23,23 @@ static inline QString msgCannotInterrupt(qint64 pid, const QString &why)
# define PROCESS_SUSPEND_RESUME (0x0800) # define PROCESS_SUSPEND_RESUME (0x0800)
#endif // PROCESS_SUSPEND_RESUME #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<LPFN_ISWOW64PROCESS>(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 // 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; bool ok = false;
HANDLE inferior = NULL; HANDLE inferior = NULL;
do { const DWORD rights = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION | PROCESS_VM_OPERATION
const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_DUP_HANDLE
|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SUSPEND_RESUME;
|PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME;
inferior = OpenProcess(rights, FALSE, DWORD(pID)); inferior = OpenProcess(rights, FALSE, DWORD(pID));
if (inferior == NULL) { if (inferior == NULL) {
*errorMessage = QString::fromLatin1("Cannot open process %1: %2"). *errorMessage = QString::fromLatin1("Cannot open process %1: %2")
arg(pID).arg(Utils::winErrorMessage(GetLastError())); .arg(pID)
break; .arg(Utils::winErrorMessage(GetLastError()));
} } else if (ok = DebugBreakProcess(inferior); !ok) {
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()); *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) if (inferior != NULL)
CloseHandle(inferior); CloseHandle(inferior);
if (!ok) if (!ok)
@@ -159,8 +54,7 @@ GDB 32bit | Api | Api | NA | Win32
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
bool Debugger::Internal::interruptProcess(qint64 pID, int /* engineType */, bool Debugger::Internal::interruptProcess(qint64 pID, QString *errorMessage)
QString *errorMessage, const bool /*engineExecutableIs64Bit*/)
{ {
if (pID <= 0) { if (pID <= 0) {
*errorMessage = msgCannotInterrupt(pID, QString::fromLatin1("Invalid process id.")); *errorMessage = msgCannotInterrupt(pID, QString::fromLatin1("Invalid process id."));

View File

@@ -7,7 +7,6 @@
namespace Debugger::Internal { namespace Debugger::Internal {
bool interruptProcess(qint64 pID, int engineType, QString *errorMessage, bool interruptProcess(qint64 pID, QString *errorMessage);
const bool engineExecutableIs64Bit = false);
} // Debugger::Internal } // Debugger::Internal

View File

@@ -102,90 +102,20 @@ void DesktopProcessSignalOperation::killProcessSilently(qint64 pid)
void DesktopProcessSignalOperation::interruptProcessSilently(qint64 pid) void DesktopProcessSignalOperation::interruptProcessSilently(qint64 pid)
{ {
#ifdef Q_OS_WIN #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; HANDLE inferior = NULL;
do { const DWORD rights = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION | PROCESS_VM_OPERATION
const DWORD rights = PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_DUP_HANDLE
|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SUSPEND_RESUME;
|PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME;
inferior = OpenProcess(rights, FALSE, pid); inferior = OpenProcess(rights, FALSE, pid);
if (inferior == NULL) { if (inferior == NULL) {
appendMsgCannotInterrupt(pid, Tr::tr("Cannot open process: %1") appendMsgCannotInterrupt(
pid, Tr::tr("Cannot open process: %1") + winErrorMessage(GetLastError()));
} else if (!DebugBreakProcess(inferior)) {
appendMsgCannotInterrupt(
pid,
Tr::tr("DebugBreakProcess failed:") + QLatin1Char(' ')
+ winErrorMessage(GetLastError())); + 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);
if (inferior != NULL) if (inferior != NULL)
CloseHandle(inferior); CloseHandle(inferior);
#else #else