forked from qt-creator/qt-creator
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:
@@ -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,28 +674,9 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::runCommand(const DebuggerCommand &command)
|
||||
{
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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<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
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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 = 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());
|
||||
} 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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
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."));
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
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")
|
||||
appendMsgCannotInterrupt(
|
||||
pid, Tr::tr("Cannot open process: %1") + winErrorMessage(GetLastError()));
|
||||
} else if (!DebugBreakProcess(inferior)) {
|
||||
appendMsgCannotInterrupt(
|
||||
pid,
|
||||
Tr::tr("DebugBreakProcess failed:") + QLatin1Char(' ')
|
||||
+ 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)
|
||||
CloseHandle(inferior);
|
||||
#else
|
||||
|
Reference in New Issue
Block a user