Commit 38510a48 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger: Fix interrupting code.



- Windows: Always use DebugBreakProcess if Qt Creator is compiled
  64bit. Else always use win64interrupt.exe if Qt Creator
  is a WOW64 application.
- Remove redundant code (procinterrupt/hostutils, gdb adapters).
- Give interruptProcess an errorMessage parameter such that it can
  be used by all C++ engines and a proper error is displayed.
- Improve error messages.
- Build win64interrupt if target architecture is 64 bit (clean tools
  profile, add a profile), borrowing the check from
  qtcreatorcdbext.pro.

Change-Id: I2a6caf98e46051c49c84e1f3aac4c8d2aba66e8b
Reviewed-by: default avatarDavid Schulz <david.schulz@nokia.com>
Reviewed-by: default avatarFriedemann Kleint <Friedemann.Kleint@nokia.com>
parent 1d3dc301
......@@ -57,6 +57,7 @@
#include "gdb/gdbmi.h"
#include "shared/cdbsymbolpathlisteditor.h"
#include "shared/hostutils.h"
#include "procinterrupt.h"
#include <TranslationUnit.h>
......@@ -1155,10 +1156,15 @@ void CdbEngine::interruptInferior()
{
if (debug)
qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
if (canInterruptInferior()) {
doInterruptInferior(NoSpecialStop);
} else {
bool ok = false;
if (!canInterruptInferior()) {
showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
} else {
ok = doInterruptInferior(NoSpecialStop);
}
// Restore running state if stop failed.
if (!ok) {
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
notifyInferiorStopOk();
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
......@@ -1175,20 +1181,19 @@ void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
m_customSpecialStopData.push_back(v);
}
void CdbEngine::doInterruptInferior(SpecialStopMode sm)
bool CdbEngine::doInterruptInferior(SpecialStopMode sm)
{
#ifdef Q_OS_WIN
const SpecialStopMode oldSpecialMode = m_specialStopMode;
m_specialStopMode = sm;
QString errorMessage;
showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
if (!winDebugBreakProcess(inferiorPid(), &errorMessage, Utils::winIs64BitBinary(cdbBinary(startParameters())))) {
QString errorMessage;
const bool ok = interruptProcess(inferiorPid(), CdbEngineType, &errorMessage);
if (!ok) {
m_specialStopMode = oldSpecialMode;
showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(inferiorPid()).arg(errorMessage), LogError);
showMessage(errorMessage, LogError);
}
#else
Q_UNUSED(sm)
#endif
return ok;
}
void CdbEngine::executeRunToLine(const ContextData &data)
......
......@@ -202,7 +202,7 @@ private:
void handleSessionAccessible(unsigned long cdbExState);
void handleSessionInaccessible(unsigned long cdbExState);
void handleSessionIdle(const QByteArray &message);
void doInterruptInferior(SpecialStopMode sm);
bool doInterruptInferior(SpecialStopMode sm);
void doInterruptInferiorCustomSpecialStop(const QVariant &v);
void doContinueInferior();
inline void parseOutputLine(QByteArray line);
......
......@@ -34,6 +34,7 @@
#include "gdbengine.h"
#include "debuggerstartparameters.h"
#include "abstractgdbprocess.h"
#include "procinterrupt.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
......@@ -158,5 +159,21 @@ void AbstractGdbAdapter::handleRemoteSetupFailed(const QString &reason)
Q_UNUSED(reason);
}
void AbstractGdbAdapter::interruptLocalInferior(qint64 pid)
{
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return);
if (pid <= 0) {
showMessage(QLatin1String("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"), LogError);
return;
}
QString errorMessage;
if (interruptProcess(pid, GdbEngineType, &errorMessage)) {
showMessage(QLatin1String("Interrupted ") + QString::number(pid));
} else {
showMessage(errorMessage, LogError);
m_engine->notifyInferiorStopFailed();
}
}
} // namespace Internal
} // namespace Debugger
......@@ -96,6 +96,7 @@ public:
virtual void handleRemoteSetupDone(int gdbServerPort, int qmlPort);
virtual void handleRemoteSetupFailed(const QString &reason);
protected:
DebuggerState state() const;
GdbEngine *engine() const { return m_engine; }
......@@ -103,6 +104,7 @@ protected:
DebuggerStartParameters &startParameters();
void showMessage(const QString &msg, int channel = LogDebug, int timeout = 1);
bool prepareCommand();
void interruptLocalInferior(qint64 pid);
GdbEngine * const m_engine;
};
......
......@@ -113,13 +113,7 @@ void AttachGdbAdapter::handleAttach(const GdbResponse &response)
void AttachGdbAdapter::interruptInferior()
{
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return);
const qint64 pid = startParameters().attachPID;
QTC_ASSERT(pid > 0, return);
if (!interruptProcess(pid)) {
showMessage(_("CANNOT INTERRUPT %1").arg(pid));
m_engine->notifyInferiorStopFailed();
}
interruptLocalInferior(startParameters().attachPID);
}
void AttachGdbAdapter::shutdownInferior()
......
......@@ -174,18 +174,7 @@ void LocalPlainGdbAdapter::checkForReleaseBuild()
void LocalPlainGdbAdapter::interruptInferior()
{
const qint64 attachedPID = m_engine->inferiorPid();
if (attachedPID <= 0) {
showMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
return;
}
if (interruptProcess(attachedPID)) {
showMessage(_("INTERRUPTED %1").arg(attachedPID));
} else {
showMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
m_engine->notifyInferiorStopFailed();
}
interruptLocalInferior(m_engine->inferiorPid());
}
QByteArray LocalPlainGdbAdapter::execFilePath() const
......
......@@ -192,10 +192,7 @@ void TermGdbAdapter::runEngine()
void TermGdbAdapter::interruptInferior()
{
const qint64 attachedPID = m_engine->inferiorPid();
QTC_ASSERT(attachedPID > 0, return);
if (!interruptProcess(attachedPID))
showMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
interruptLocalInferior(m_engine->inferiorPid());
}
void TermGdbAdapter::stubError(const QString &msg)
......
......@@ -31,12 +31,19 @@
**************************************************************************/
#include "procinterrupt.h"
#include "debuggerconstants.h"
#include <QtCore/QProcess> // makes kill visible on Windows.
#include <QtCore/QFile>
#include <QtCore/QDir>
using namespace Debugger::Internal;
static inline QString msgCannotInterrupt(int pid, const QString &why)
{
return QString::fromLatin1("Cannot interrupt process %1: %2").arg(pid).arg(why);
}
#if defined(Q_OS_WIN)
#define _WIN32_WINNT 0x0501 /* WinXP, needed for DebugBreakProcess() */
......@@ -44,45 +51,97 @@ using namespace Debugger::Internal;
#include <utils/winutils.h>
#include <windows.h>
#if !defined(PROCESS_SUSPEND_RESUME) // Check flag for MinGW
# define PROCESS_SUSPEND_RESUME (0x0800)
#endif // PROCESS_SUSPEND_RESUME
static BOOL isWow64Process(HANDLE hproc)
{
BOOL ret = false;
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process = NULL;
HMODULE hModule = GetModuleHandle(L"kernel32.dll");
if (hModule == NULL)
return false;
fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>(GetProcAddress(hModule, "IsWow64Process"));
if (fnIsWow64Process == NULL)
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))
if (!fnIsWow64Process(hproc, &ret)) {
qWarning("IsWow64Process() failed for %p: %s",
hproc, qPrintable(Utils::winErrorMessage(GetLastError())));
return false;
}
return ret;
}
bool Debugger::Internal::interruptProcess(int pID)
// Open the process and break into it
bool Debugger::Internal::interruptProcess(int pID, int engineType, QString *errorMessage)
{
if (pID <= 0)
return false;
HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, false, pID);
if (hproc == NULL)
return false;
BOOL proc64bit = false;
if (Utils::winIs64BitSystem())
proc64bit = !isWow64Process(hproc);
bool ok = false;
if (proc64bit)
ok = !QProcess::execute(QCoreApplication::applicationDirPath() + QString::fromLatin1("/win64interrupt.exe %1").arg(pID));
else
ok = !DebugBreakProcess(hproc);
CloseHandle(hproc);
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) {
*errorMessage = QString::fromLatin1("Cannot open process %1: %2").
arg(pID).arg(Utils::winErrorMessage(GetLastError()));
break;
}
// Try DebugBreakProcess if either Qt Creator is compiled 64 bit or
// both Qt Creator and application are 32 bit.
#ifdef Q_OS_WIN64
Q_UNUSED(engineType)
// Qt-Creator compiled 64 bit: Always use DebugBreakProcess.
const bool useDebugBreakApi = true;
#else
// Qt-Creator compiled 32 bit:
// CDB: If Qt-Creator is a WOW64 process (meaning a 32bit process
// running in emulation), always use win64interrupt.exe for native
// 64 bit processes and WOW64 processes. While DebugBreakProcess()
// works in theory for other WOW64 processes, the break appears
// as a WOW64 breakpoint, which CDB is configured to ignore since
// it also triggers on module loading.
// GDB: Use win64interrupt for native 64bit processes only (it fails
// for WOW64 processes.
static const bool hostIsWow64Process = isWow64Process(GetCurrentProcess());
const bool useDebugBreakApi = engineType == CdbEngineType ?
!hostIsWow64Process :
isWow64Process(inferior);
#endif
if (useDebugBreakApi) {
ok = DebugBreakProcess(inferior);
if (!ok)
*errorMessage = QLatin1String("DebugBreakProcess failed: ") + Utils::winErrorMessage(GetLastError());
} else {
const QString executable = QCoreApplication::applicationDirPath() + QLatin1String("/win64interrupt.exe");
switch (QProcess::execute(executable + QLatin1Char(' ') + 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)
+ QLatin1String(" could not break the process.");
break;
}
break;
}
} while (false);
if (inferior != NULL)
CloseHandle(inferior);
if (!ok)
*errorMessage = msgCannotInterrupt(pID, *errorMessage);
return ok;
}
......@@ -90,14 +149,21 @@ bool Debugger::Internal::interruptProcess(int pID)
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
bool Debugger::Internal::interruptProcess(int pID)
bool Debugger::Internal::interruptProcess(int pID, int /* engineType */,
QString *errorMessage)
{
if (pID > 0) {
if (kill(pID, SIGINT) == 0)
return true;
if (pID <= 0) {
*errorMessage = msgCannotInterrupt(pID, QString::fromLatin1("Invalid process id."));
return false;
}
if (kill(pID, SIGINT)) {
*errorMessage = msgCannotInterrupt(pID, QString::fromLocal8Bit(strerror(errno)));
return false;
}
return false;
return true;
}
#endif // !Q_OS_WIN
......@@ -33,10 +33,12 @@
#ifndef DEBUGGER_PROCINTERRUPT_H
#define DEBUGGER_PROCINTERRUPT_H
#include <QtCore/QString>
namespace Debugger {
namespace Internal {
bool interruptProcess(int pID);
bool interruptProcess(int pID, int engineType, QString *errorMessage);
} // Internal
} // GdbDebugger
......
......@@ -49,10 +49,6 @@
#define _WIN32_WINNT 0x0502
#include <windows.h>
#include <utils/winutils.h>
#if !defined(PROCESS_SUSPEND_RESUME) // Check flag for MinGW
# define PROCESS_SUSPEND_RESUME (0x0800)
#endif // PROCESS_SUSPEND_RESUME
#include <tlhelp32.h>
#include <psapi.h>
......@@ -145,46 +141,6 @@ bool winResumeThread(unsigned long dwThreadId, QString *errorMessage)
return ok;
}
// Open the process and break into it
bool winDebugBreakProcess(unsigned long pid, QString *errorMessage, bool isCdb64bit)
{
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, pid);
if (inferior == NULL) {
*errorMessage = QString::fromLatin1("Cannot open process %1: %2").
arg(pid).arg(Utils::winErrorMessage(GetLastError()));
break;
}
if (isCdb64bit) {
switch (QProcess::execute(QCoreApplication::applicationDirPath() + QString::fromLatin1("/win64interrupt.exe %1").arg(pid))) {
case -2:
*errorMessage = QString::fromLatin1("Cannot start win64interrupt.exe. Check src/tools/win64interrupt/win64interrupt.c for more information.");
break;
case 0:
ok = true;
break;
default:
*errorMessage = QString::fromLatin1("win64interrupt.exe could not break the process with the pid %1.").arg(pid);
break;
}
break;
} else if (!DebugBreakProcess(inferior)) {
*errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError()));
break;
}
ok = true;
} while (false);
if (inferior != NULL)
CloseHandle(inferior);
return ok;
}
unsigned long winGetCurrentProcessId()
{
return GetCurrentProcessId();
......
......@@ -59,9 +59,6 @@ QList<ProcData> hostProcessList();
// Resume a suspended thread by id.
bool winResumeThread(unsigned long dwThreadId, QString *errorMessage);
// Open a process by PID and break into it.
bool winDebugBreakProcess(unsigned long pid, QString *errorMessage, bool isCdb64bit);
unsigned long winGetCurrentProcessId();
bool isWinProcessBeingDebugged(unsigned long pid);
......
TEMPLATE = subdirs
win32:SUBDIRS = qtcdebugger
SUBDIRS += qtpromaker \
qmlprofilertool
SUBDIRS += qmlpuppet
SUBDIRS = qtpromaker \
qmlprofilertool \
qmlpuppet
!win32 {
win32 {
SUBDIRS += qtcdebugger
# win64interrupt only make sense for 64bit builds
ENV_CPU=$$(CPU)
ENV_LIBPATH=$$(LIBPATH)
contains(ENV_CPU, ^AMD64$) {
SUBDIRS += win64interrupt
} else:isEmpty(ENV_CPU):contains(ENV_LIBPATH, ^.*amd64.*$) {
SUBDIRS += win64interrupt
}
} else {
SUBDIRS += valgrindfake
}
......
......@@ -39,6 +39,7 @@
#endif
#include <Windows.h>
#include <stdio.h>
/* To debug break a 64bit application under Windows, you must call
* DebugBreakProcess() from an 64bit apllication. Therefore:
......@@ -54,22 +55,30 @@
int main(int argc, char *argv[])
{
HANDLE proc;
BOOL break_result;
DWORD proc_id;
BOOL break_result = FALSE;
if (argc != 2)
if (argc != 2) {
fprintf(stderr, "Usage: %s <process-id>\n", argv[0]);
return 1;
}
proc_id = strtoul(argv[1], NULL, 0);
if (proc_id == 0)
if (proc_id == 0) {
fprintf(stderr, "%s: Invalid argument '%s'\n", argv[0], argv[1]);
return 2;
}
proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc_id);
if (!proc)
if (!proc) {
fprintf(stderr, "%s: OpenProcess() failed, error 0x%lx\n", argv[0], GetLastError());
return 3;
}
break_result = DebugBreakProcess(proc);
if (!break_result)
fprintf(stderr, "%s: DebugBreakProcess() failed, error 0x%lx\n", argv[0], GetLastError());
CloseHandle(proc);
return !break_result;
}
CONFIG += warn_on console use_c_linker static
CONFIG -= qt app_bundle
include(../../../qtcreator.pri)
SOURCES = win64interrupt.c
TEMPLATE = app
DESTDIR = $$IDE_LIBEXEC_PATH
build_all:!build_pass {
CONFIG -= build_all
CONFIG += release
}
target.path = /bin # FIXME: libexec, more or less
INSTALLS += target
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment