forked from qt-creator/qt-creator
Debugger/CDB: Split engine for testing/scripting purposes.
This commit is contained in:
@@ -1,32 +1,8 @@
|
|||||||
# Detect presence of "Debugging Tools For Windows"
|
include(cdbcore.pri)
|
||||||
# in case VS compilers are used.
|
|
||||||
|
|
||||||
win32 {
|
!isEmpty(CDB_PATH) {
|
||||||
contains(QMAKE_CXX, cl) {
|
|
||||||
|
|
||||||
CDB_PATH="$$(CDB_PATH)"
|
|
||||||
isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk"
|
|
||||||
|
|
||||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk"
|
|
||||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk"
|
|
||||||
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk"
|
|
||||||
|
|
||||||
exists($$CDB_PATH) {
|
|
||||||
|
|
||||||
message("Adding support for $$CDB_PATH")
|
|
||||||
|
|
||||||
DEFINES+=CDB_ENABLED
|
|
||||||
|
|
||||||
CDB_PLATFORM=i386
|
|
||||||
|
|
||||||
INCLUDEPATH*=$$CDB_PATH
|
|
||||||
INCLUDEPATH*=$$PWD
|
|
||||||
DEPENDPATH*=$$PWD
|
|
||||||
|
|
||||||
CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/cdbcom.h \
|
|
||||||
$$PWD/cdbdebugengine.h \
|
$$PWD/cdbdebugengine.h \
|
||||||
$$PWD/cdbdebugengine_p.h \
|
$$PWD/cdbdebugengine_p.h \
|
||||||
$$PWD/cdbdebugeventcallback.h \
|
$$PWD/cdbdebugeventcallback.h \
|
||||||
@@ -63,8 +39,4 @@ SOURCES += \
|
|||||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||||
|
|
||||||
LIBS+=-lpsapi
|
LIBS+=-lpsapi
|
||||||
} else {
|
}
|
||||||
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
|
||||||
} # exists($$CDB_PATH)
|
|
||||||
} # (QMAKE_CXX, cl)
|
|
||||||
} # win32
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ bool getRegisters(CIDebugControl *ctl,
|
|||||||
ULONG count;
|
ULONG count;
|
||||||
HRESULT hr = ireg->GetNumberRegisters(&count);
|
HRESULT hr = ireg->GetNumberRegisters(&count);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("GetNumberRegisters", hr);
|
*errorMessage= CdbCore::msgComFailed("GetNumberRegisters", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!count)
|
if (!count)
|
||||||
@@ -80,7 +80,7 @@ bool getRegisters(CIDebugControl *ctl,
|
|||||||
for (ULONG r = 0; r < count; r++) {
|
for (ULONG r = 0; r < count; r++) {
|
||||||
hr = ireg->GetDescriptionWide(r, wszBuf, MAX_PATH - 1, 0, 0);
|
hr = ireg->GetDescriptionWide(r, wszBuf, MAX_PATH - 1, 0, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("GetDescriptionWide", hr);
|
*errorMessage= CdbCore::msgComFailed("GetDescriptionWide", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Register reg;
|
Register reg;
|
||||||
@@ -93,13 +93,13 @@ bool getRegisters(CIDebugControl *ctl,
|
|||||||
memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE));
|
memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE));
|
||||||
hr = ireg->GetValues(count, 0, 0, valuesPtr);
|
hr = ireg->GetValues(count, 0, 0, valuesPtr);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("GetValues", hr);
|
*errorMessage= CdbCore::msgComFailed("GetValues", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (base < 2)
|
if (base < 2)
|
||||||
base = 10;
|
base = 10;
|
||||||
for (ULONG r = 0; r < count; r++)
|
for (ULONG r = 0; r < count; r++)
|
||||||
(*registers)[r].value = CdbSymbolGroupContext::debugValueToString(values.at(r), ctl, 0, base);
|
(*registers)[r].value = CdbCore::debugValueToString(values.at(r), 0, base, ctl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +211,7 @@ void DisassemblerOutputParser::parse(const QStringList &l)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dissassemble(CIDebugClient *client,
|
bool dissassemble(CdbCore::CoreEngine *engine,
|
||||||
CIDebugControl *ctl,
|
|
||||||
ULONG64 offset,
|
ULONG64 offset,
|
||||||
unsigned long beforeLines,
|
unsigned long beforeLines,
|
||||||
unsigned long afterLines,
|
unsigned long afterLines,
|
||||||
@@ -222,26 +221,11 @@ bool dissassemble(CIDebugClient *client,
|
|||||||
{
|
{
|
||||||
if (debugCDB)
|
if (debugCDB)
|
||||||
qDebug() << Q_FUNC_INFO << offset;
|
qDebug() << Q_FUNC_INFO << offset;
|
||||||
|
QString lines;
|
||||||
const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME;
|
if (!engine->dissassemble(offset, beforeLines, afterLines, &lines, errorMessage))
|
||||||
// Catch the output by temporarily setting another handler.
|
|
||||||
// We use the method that outputs to the output handler as it
|
|
||||||
// conveniently provides the 'beforeLines' context (stepping back
|
|
||||||
// in assembler code). We build a complete string first as line breaks
|
|
||||||
// may occur in-between messages.
|
|
||||||
StringOutputHandler stringHandler;
|
|
||||||
OutputRedirector redir(client, &stringHandler);
|
|
||||||
// For some reason, we need to output to "all clients"
|
|
||||||
const HRESULT hr = ctl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS,
|
|
||||||
beforeLines, beforeLines + afterLines,
|
|
||||||
offset, flags, 0, 0, 0, 0);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage= QString::fromLatin1("Unable to disassamble at 0x%1: %2").
|
|
||||||
arg(offset, 0, 16).arg(msgComFailed("OutputDisassemblyLines", hr));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
DisassemblerOutputParser parser(str, addressFieldWidth);
|
DisassemblerOutputParser parser(str, addressFieldWidth);
|
||||||
parser.parse(stringHandler.result().split(QLatin1Char('\n')));
|
parser.parse(lines.split(QLatin1Char('\n')));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE
|
|||||||
class QTextStream;
|
class QTextStream;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
class CoreEngine;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -51,8 +55,7 @@ bool getRegisters(CIDebugControl *ctl,
|
|||||||
QString *errorMessage,
|
QString *errorMessage,
|
||||||
int base = 10 /* 16 for hex, etc */);
|
int base = 10 /* 16 for hex, etc */);
|
||||||
|
|
||||||
bool dissassemble(CIDebugClient *client,
|
bool dissassemble(CdbCore::CoreEngine *engine,
|
||||||
CIDebugControl *ctl,
|
|
||||||
ULONG64 offset,
|
ULONG64 offset,
|
||||||
unsigned long beforeLines,
|
unsigned long beforeLines,
|
||||||
unsigned long afterLines,
|
unsigned long afterLines,
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
|
|||||||
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
|
const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
|
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
|
||||||
arg(expr, msgComFailed("SetOffsetExpressionWide", hr));
|
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Pass Count is ignoreCount + 1
|
// Pass Count is ignoreCount + 1
|
||||||
@@ -192,7 +192,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
|||||||
*id = 0;
|
*id = 0;
|
||||||
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
|
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgCannotAddBreakPoint(msgComFailed("AddBreakpoint2", hr));
|
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ibp) {
|
if (!ibp) {
|
||||||
@@ -210,7 +210,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
|||||||
if (id) {
|
if (id) {
|
||||||
hr = ibp->GetId(id);
|
hr = ibp->GetId(id);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgCannotAddBreakPoint(msgComFailed("GetId", hr));
|
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("GetId", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,7 +326,7 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
|
|||||||
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
|
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
|
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
|
||||||
arg(msgComFailed("GetOffsetExpressionWide", hr));
|
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Pass Count is ignoreCount + 1
|
// Pass Count is ignoreCount + 1
|
||||||
@@ -398,7 +398,7 @@ bool CDBBreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *coun
|
|||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
if (errorMessage)
|
if (errorMessage)
|
||||||
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
|
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
|
||||||
arg(msgComFailed("GetNumberBreakpoints", hr));
|
arg(CdbCore::msgComFailed("GetNumberBreakpoints", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -416,7 +416,7 @@ bool CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakP
|
|||||||
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
|
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
|
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
|
||||||
arg(b).arg(msgComFailed("GetBreakpointByIndex2", hr));
|
arg(b).arg(CdbCore::msgComFailed("GetBreakpointByIndex2", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CDBBreakPoint bp;
|
CDBBreakPoint bp;
|
||||||
@@ -438,7 +438,7 @@ static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id,
|
|||||||
CIDebugBreakpoint *ibp = 0;
|
CIDebugBreakpoint *ibp = 0;
|
||||||
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
|
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgNoBreakPointWithId(id, msgComFailed("GetBreakpointById2", hr));
|
*errorMessage = msgNoBreakPointWithId(id, CdbCore::msgComFailed("GetBreakpointById2", hr));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!ibp) {
|
if (!ibp) {
|
||||||
@@ -458,7 +458,7 @@ static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString
|
|||||||
return false;
|
return false;
|
||||||
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
|
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(msgComFailed("RemoveBreakpointById2", hr));
|
*errorMessage = QString::fromLatin1("Cannot remove breakpoint %1: %2").arg(id).arg(CdbCore::msgComFailed("RemoveBreakpointById2", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -486,7 +486,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool
|
|||||||
ULONG flags;
|
ULONG flags;
|
||||||
HRESULT hr = ibp->GetFlags(&flags);
|
HRESULT hr = ibp->GetFlags(&flags);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("GetFlags", hr));
|
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("GetFlags", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
||||||
@@ -500,7 +500,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool
|
|||||||
}
|
}
|
||||||
hr = ibp->SetFlags(flags);
|
hr = ibp->SetFlags(flags);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("SetFlags", hr));
|
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("SetFlags", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
43
src/plugins/debugger/cdb/cdbcore.pri
Normal file
43
src/plugins/debugger/cdb/cdbcore.pri
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Detect presence of "Debugging Tools For Windows"
|
||||||
|
# in case VS compilers are used.
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
contains(QMAKE_CXX, cl) {
|
||||||
|
|
||||||
|
CDB_PATH="$$(CDB_PATH)"
|
||||||
|
isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk"
|
||||||
|
|
||||||
|
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk"
|
||||||
|
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk"
|
||||||
|
!exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk"
|
||||||
|
|
||||||
|
exists($$CDB_PATH) {
|
||||||
|
|
||||||
|
message("Adding support for $$CDB_PATH")
|
||||||
|
|
||||||
|
DEFINES+=CDB_ENABLED
|
||||||
|
|
||||||
|
CDB_PLATFORM=i386
|
||||||
|
|
||||||
|
INCLUDEPATH*=$$CDB_PATH
|
||||||
|
CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/cdbcom.h \
|
||||||
|
$$PWD/coreengine.h \
|
||||||
|
$$PWD/debugoutputbase.h \
|
||||||
|
$$PWD/debugeventcallbackbase.h
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/coreengine.cpp \
|
||||||
|
$$PWD/debugoutputbase.cpp \
|
||||||
|
$$PWD/debugeventcallbackbase.cpp
|
||||||
|
|
||||||
|
INCLUDEPATH*=$$PWD
|
||||||
|
DEPENDPATH*=$$PWD
|
||||||
|
|
||||||
|
} else {
|
||||||
|
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
||||||
|
CDB_PATH=""
|
||||||
|
} # exists($$CDB_PATH)
|
||||||
|
} # (QMAKE_CXX, cl)
|
||||||
|
} # win32
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ namespace Internal {
|
|||||||
class DisassemblerViewAgent;
|
class DisassemblerViewAgent;
|
||||||
class CdbDebugEventCallback;
|
class CdbDebugEventCallback;
|
||||||
class CdbDebugOutput;
|
class CdbDebugOutput;
|
||||||
struct CdbDebugEnginePrivate;
|
class CdbDebugEnginePrivate;
|
||||||
struct CdbOptions;
|
struct CdbOptions;
|
||||||
|
|
||||||
class CdbDebugEngine : public IDebuggerEngine
|
class CdbDebugEngine : public IDebuggerEngine
|
||||||
@@ -104,9 +104,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void syncDebuggerPaths();
|
void syncDebuggerPaths();
|
||||||
|
|
||||||
protected:
|
|
||||||
void timerEvent(QTimerEvent*);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotConsoleStubStarted();
|
void slotConsoleStubStarted();
|
||||||
void slotConsoleStubError(const QString &msg);
|
void slotConsoleStubError(const QString &msg);
|
||||||
@@ -116,19 +113,16 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void setState(DebuggerState state, const char *func, int line);
|
void setState(DebuggerState state, const char *func, int line);
|
||||||
bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
||||||
bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage);
|
inline bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage);
|
||||||
void startWatchTimer();
|
|
||||||
void killWatchTimer();
|
|
||||||
void processTerminated(unsigned long exitCode);
|
void processTerminated(unsigned long exitCode);
|
||||||
bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
|
|
||||||
void evaluateWatcher(WatchData *wd);
|
void evaluateWatcher(WatchData *wd);
|
||||||
QString editorToolTip(const QString &exp, const QString &function);
|
QString editorToolTip(const QString &exp, const QString &function);
|
||||||
bool step(unsigned long executionStatus);
|
bool step(unsigned long executionStatus);
|
||||||
|
|
||||||
CdbDebugEnginePrivate *m_d;
|
CdbDebugEnginePrivate *m_d;
|
||||||
|
|
||||||
friend struct CdbDebugEnginePrivate;
|
friend class CdbDebugEnginePrivate;
|
||||||
friend class CdbDebugEventCallback;
|
friend class CdbDebugEventCallback;
|
||||||
friend class CdbDebugOutput;
|
friend class CdbDebugOutput;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,8 +30,7 @@
|
|||||||
#ifndef DEBUGGER_CDBENGINEPRIVATE_H
|
#ifndef DEBUGGER_CDBENGINEPRIVATE_H
|
||||||
#define DEBUGGER_CDBENGINEPRIVATE_H
|
#define DEBUGGER_CDBENGINEPRIVATE_H
|
||||||
|
|
||||||
#include "cdbdebugeventcallback.h"
|
#include "coreengine.h"
|
||||||
#include "cdbdebugoutput.h"
|
|
||||||
#include "cdboptions.h"
|
#include "cdboptions.h"
|
||||||
#include "cdbdumperhelper.h"
|
#include "cdbdumperhelper.h"
|
||||||
#include "stackhandler.h"
|
#include "stackhandler.h"
|
||||||
@@ -50,52 +49,11 @@ class WatchHandler;
|
|||||||
class CdbStackFrameContext;
|
class CdbStackFrameContext;
|
||||||
class CdbStackTraceContext;
|
class CdbStackTraceContext;
|
||||||
|
|
||||||
// Thin wrapper around the 'DBEng' debugger engine shared library
|
class CdbDebugEnginePrivate : public CdbCore::CoreEngine
|
||||||
// which is loaded at runtime.
|
|
||||||
|
|
||||||
class DebuggerEngineLibrary
|
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DebuggerEngineLibrary();
|
|
||||||
bool init(const QString &path, QString *dbgEngDLL, QString *errorMessage);
|
|
||||||
|
|
||||||
inline HRESULT debugCreate(REFIID interfaceId, PVOID *interfaceHandle) const
|
|
||||||
{ return m_debugCreate(interfaceId, interfaceHandle); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// The exported functions of the library
|
|
||||||
typedef HRESULT (STDAPICALLTYPE *DebugCreateFunction)(REFIID, PVOID *);
|
|
||||||
|
|
||||||
DebugCreateFunction m_debugCreate;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A class that sets an expression syntax on the debug control while in scope.
|
|
||||||
// Can be nested as it checks for the old value.
|
|
||||||
class SyntaxSetter {
|
|
||||||
Q_DISABLE_COPY(SyntaxSetter)
|
|
||||||
public:
|
|
||||||
explicit inline SyntaxSetter(CIDebugControl *ctl, ULONG desiredSyntax);
|
|
||||||
inline ~SyntaxSetter();
|
|
||||||
private:
|
|
||||||
const ULONG m_desiredSyntax;
|
|
||||||
CIDebugControl *m_ctl;
|
|
||||||
ULONG m_oldSyntax;
|
|
||||||
};
|
|
||||||
|
|
||||||
// helper struct to pass interfaces around
|
|
||||||
struct CdbComInterfaces
|
|
||||||
{
|
|
||||||
CdbComInterfaces();
|
|
||||||
CIDebugClient* debugClient;
|
|
||||||
CIDebugControl* debugControl;
|
|
||||||
CIDebugSystemObjects* debugSystemObjects;
|
|
||||||
CIDebugSymbols* debugSymbols;
|
|
||||||
CIDebugRegisters* debugRegisters;
|
|
||||||
CIDebugDataSpaces* debugDataSpaces;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CdbDebugEnginePrivate
|
|
||||||
{
|
|
||||||
typedef QMap<QString, QString> EditorToolTipCache;
|
typedef QMap<QString, QString> EditorToolTipCache;
|
||||||
|
|
||||||
enum HandleBreakEventMode { // Special modes for break event handler.
|
enum HandleBreakEventMode { // Special modes for break event handler.
|
||||||
@@ -107,15 +65,14 @@ struct CdbDebugEnginePrivate
|
|||||||
explicit CdbDebugEnginePrivate(DebuggerManager *parent,
|
explicit CdbDebugEnginePrivate(DebuggerManager *parent,
|
||||||
const QSharedPointer<CdbOptions> &options,
|
const QSharedPointer<CdbOptions> &options,
|
||||||
CdbDebugEngine* engine);
|
CdbDebugEngine* engine);
|
||||||
|
~CdbDebugEnginePrivate();
|
||||||
bool init(QString *errorMessage);
|
bool init(QString *errorMessage);
|
||||||
~CdbDebugEnginePrivate();
|
|
||||||
|
|
||||||
void checkVersion();
|
void checkVersion();
|
||||||
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
|
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
|
||||||
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
|
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
|
||||||
|
|
||||||
bool isDebuggeeRunning() const { return m_watchTimer != -1; }
|
bool isDebuggeeRunning() const { return isWatchTimerRunning(); }
|
||||||
void handleDebugEvent();
|
|
||||||
ULONG updateThreadList();
|
ULONG updateThreadList();
|
||||||
bool setCDBThreadId(unsigned long threadId, QString *errorMessage);
|
bool setCDBThreadId(unsigned long threadId, QString *errorMessage);
|
||||||
void updateStackTrace();
|
void updateStackTrace();
|
||||||
@@ -143,17 +100,12 @@ struct CdbDebugEnginePrivate
|
|||||||
enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto };
|
enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto };
|
||||||
void endDebugging(EndDebuggingMode em = EndDebuggingAuto);
|
void endDebugging(EndDebuggingMode em = EndDebuggingAuto);
|
||||||
|
|
||||||
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
void updateCodeLevel();
|
||||||
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
|
|
||||||
|
|
||||||
QStringList sourcePaths() const;
|
public slots:
|
||||||
bool setSourcePaths(const QStringList &s, QString *errorMessage);
|
void handleDebugEvent();
|
||||||
|
|
||||||
QStringList symbolPaths() const;
|
|
||||||
bool setSymbolPaths(const QStringList &s, QString *errorMessage);
|
|
||||||
|
|
||||||
bool setCodeLevel();
|
|
||||||
|
|
||||||
|
public:
|
||||||
const QSharedPointer<CdbOptions> m_options;
|
const QSharedPointer<CdbOptions> m_options;
|
||||||
HANDLE m_hDebuggeeProcess;
|
HANDLE m_hDebuggeeProcess;
|
||||||
HANDLE m_hDebuggeeThread;
|
HANDLE m_hDebuggeeThread;
|
||||||
@@ -164,12 +116,7 @@ struct CdbDebugEnginePrivate
|
|||||||
bool m_ignoreInitialBreakPoint;
|
bool m_ignoreInitialBreakPoint;
|
||||||
HandleBreakEventMode m_breakEventMode;
|
HandleBreakEventMode m_breakEventMode;
|
||||||
|
|
||||||
int m_watchTimer;
|
|
||||||
CdbComInterfaces m_cif;
|
|
||||||
CdbDebugEventCallback m_debugEventCallBack;
|
|
||||||
CdbDebugOutput m_debugOutputCallBack;
|
|
||||||
QSharedPointer<CdbDumperHelper> m_dumper;
|
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
QString m_baseImagePath;
|
|
||||||
|
|
||||||
CdbDebugEngine *m_engine;
|
CdbDebugEngine *m_engine;
|
||||||
inline DebuggerManager *manager() const;
|
inline DebuggerManager *manager() const;
|
||||||
@@ -181,19 +128,8 @@ struct CdbDebugEnginePrivate
|
|||||||
|
|
||||||
DebuggerStartMode m_mode;
|
DebuggerStartMode m_mode;
|
||||||
Utils::ConsoleProcess m_consoleStubProc;
|
Utils::ConsoleProcess m_consoleStubProc;
|
||||||
QString m_dbengDLL;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// helper functions
|
|
||||||
|
|
||||||
bool getExecutionStatus(CIDebugControl *ctl, ULONG *executionStatus, QString *errorMessage = 0);
|
|
||||||
const char *executionStatusString(ULONG executionStatus);
|
|
||||||
const char *executionStatusString(CIDebugControl *ctl);
|
|
||||||
|
|
||||||
// Message
|
|
||||||
QString msgDebugEngineComResult(HRESULT hr);
|
|
||||||
QString msgComFailed(const char *func, HRESULT hr);
|
|
||||||
|
|
||||||
enum { messageTimeOut = 5000 };
|
enum { messageTimeOut = 5000 };
|
||||||
|
|
||||||
enum { debugCDB = 0 };
|
enum { debugCDB = 0 };
|
||||||
|
|||||||
@@ -40,174 +40,6 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// CdbDebugEventCallbackBase
|
|
||||||
CdbDebugEventCallbackBase::CdbDebugEventCallbackBase()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::QueryInterface(
|
|
||||||
THIS_
|
|
||||||
IN REFIID InterfaceId,
|
|
||||||
OUT PVOID* Interface)
|
|
||||||
{
|
|
||||||
*Interface = NULL;
|
|
||||||
|
|
||||||
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
|
||||||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) {
|
|
||||||
*Interface = (IDebugOutputCallbacks *)this;
|
|
||||||
AddRef();
|
|
||||||
return S_OK;
|
|
||||||
} else {
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::AddRef(THIS)
|
|
||||||
{
|
|
||||||
// This class is designed to be static so
|
|
||||||
// there's no true refcount.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP_(ULONG) CdbDebugEventCallbackBase::Release(THIS)
|
|
||||||
{
|
|
||||||
// This class is designed to be static so
|
|
||||||
// there's no true refcount.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::Exception(
|
|
||||||
THIS_
|
|
||||||
__in PEXCEPTION_RECORD64,
|
|
||||||
__in ULONG /* FirstChance */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::CreateThread(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 /* Handle */,
|
|
||||||
__in ULONG64 /* DataOffset */,
|
|
||||||
__in ULONG64 /* StartOffset */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::ExitThread(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* ExitCode */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::CreateProcess(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 /* ImageFileHandle */,
|
|
||||||
__in ULONG64 /* Handle */,
|
|
||||||
__in ULONG64 /* BaseOffset */,
|
|
||||||
__in ULONG /* ModuleSize */,
|
|
||||||
__in_opt PCWSTR /* ModuleName */,
|
|
||||||
__in_opt PCWSTR /* ImageName */,
|
|
||||||
__in ULONG /* CheckSum */,
|
|
||||||
__in ULONG /* TimeDateStamp */,
|
|
||||||
__in ULONG64 /* InitialThreadHandle */,
|
|
||||||
__in ULONG64 /* ThreadDataOffset */,
|
|
||||||
__in ULONG64 /* StartOffset */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::ExitProcess(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* ExitCode */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::LoadModule(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 /* ImageFileHandle */,
|
|
||||||
__in ULONG64 /* BaseOffset */,
|
|
||||||
__in ULONG /* ModuleSize */,
|
|
||||||
__in_opt PCWSTR /* ModuleName */,
|
|
||||||
__in_opt PCWSTR /* ImageName */,
|
|
||||||
__in ULONG /* CheckSum */,
|
|
||||||
__in ULONG /* TimeDateStamp */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::UnloadModule(
|
|
||||||
THIS_
|
|
||||||
__in_opt PCWSTR /* ImageBaseName */,
|
|
||||||
__in ULONG64 /* BaseOffset */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::SystemError(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* Error */,
|
|
||||||
__in ULONG /* Level */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::SessionStatus(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* Status */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::ChangeDebuggeeState(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* Flags */,
|
|
||||||
__in ULONG64 /* Argument */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::ChangeEngineState(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* Flags */,
|
|
||||||
__in ULONG64 /* Argument */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugEventCallbackBase::ChangeSymbolState(
|
|
||||||
THIS_
|
|
||||||
__in ULONG /* Flags */,
|
|
||||||
__in ULONG64 /* Argument */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
IDebugEventCallbacksWide *CdbDebugEventCallbackBase::getEventCallback(CIDebugClient *clnt)
|
|
||||||
{
|
|
||||||
IDebugEventCallbacksWide *rc = 0;
|
|
||||||
if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc)))
|
|
||||||
return rc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- CdbDebugEventCallback
|
// ---------- CdbDebugEventCallback
|
||||||
|
|
||||||
CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) :
|
CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) :
|
||||||
@@ -420,18 +252,5 @@ STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------- EventCallbackRedirector
|
|
||||||
EventCallbackRedirector::EventCallbackRedirector(CIDebugClient *client, IDebugEventCallbacksWide *cb) :
|
|
||||||
m_client(client),
|
|
||||||
m_oldCb(CdbDebugEventCallbackBase::getEventCallback(client))
|
|
||||||
{
|
|
||||||
client->SetEventCallbacksWide(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventCallbackRedirector::~EventCallbackRedirector()
|
|
||||||
{
|
|
||||||
m_client->SetEventCallbacksWide(m_oldCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#ifndef DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
#ifndef DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||||
#define DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
#define DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "debugeventcallbackbase.h"
|
||||||
|
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
@@ -41,122 +41,7 @@ namespace Internal {
|
|||||||
|
|
||||||
class CdbDebugEngine;
|
class CdbDebugEngine;
|
||||||
|
|
||||||
// Base class for event callbacks that takes care
|
class CdbDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||||
// Active X magic. Provides base implementations with
|
|
||||||
// the exception of GetInterestMask
|
|
||||||
class CdbDebugEventCallbackBase : public IDebugEventCallbacksWide
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
CdbDebugEventCallbackBase();
|
|
||||||
public:
|
|
||||||
// IUnknown.
|
|
||||||
STDMETHOD(QueryInterface)(
|
|
||||||
THIS_
|
|
||||||
IN REFIID InterfaceId,
|
|
||||||
OUT PVOID* Interface
|
|
||||||
);
|
|
||||||
STDMETHOD_(ULONG, AddRef)(
|
|
||||||
THIS
|
|
||||||
);
|
|
||||||
STDMETHOD_(ULONG, Release)(
|
|
||||||
THIS
|
|
||||||
);
|
|
||||||
|
|
||||||
// IDebugEventCallbacks.
|
|
||||||
|
|
||||||
STDMETHOD(Breakpoint)(
|
|
||||||
THIS_
|
|
||||||
__in PDEBUG_BREAKPOINT2 Bp
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(Exception)(
|
|
||||||
THIS_
|
|
||||||
__in PEXCEPTION_RECORD64 Exception,
|
|
||||||
__in ULONG FirstChance
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(CreateThread)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 Handle,
|
|
||||||
__in ULONG64 DataOffset,
|
|
||||||
__in ULONG64 StartOffset
|
|
||||||
);
|
|
||||||
STDMETHOD(ExitThread)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG ExitCode
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(CreateProcess)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 ImageFileHandle,
|
|
||||||
__in ULONG64 Handle,
|
|
||||||
__in ULONG64 BaseOffset,
|
|
||||||
__in ULONG ModuleSize,
|
|
||||||
__in_opt PCWSTR ModuleName,
|
|
||||||
__in_opt PCWSTR ImageName,
|
|
||||||
__in ULONG CheckSum,
|
|
||||||
__in ULONG TimeDateStamp,
|
|
||||||
__in ULONG64 InitialThreadHandle,
|
|
||||||
__in ULONG64 ThreadDataOffset,
|
|
||||||
__in ULONG64 StartOffset
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(ExitProcess)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG ExitCode
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(LoadModule)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG64 ImageFileHandle,
|
|
||||||
__in ULONG64 BaseOffset,
|
|
||||||
__in ULONG ModuleSize,
|
|
||||||
__in_opt PCWSTR ModuleName,
|
|
||||||
__in_opt PCWSTR ImageName,
|
|
||||||
__in ULONG CheckSum,
|
|
||||||
__in ULONG TimeDateStamp
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(UnloadModule)(
|
|
||||||
THIS_
|
|
||||||
__in_opt PCWSTR ImageBaseName,
|
|
||||||
__in ULONG64 BaseOffset
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(SystemError)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG Error,
|
|
||||||
__in ULONG Level
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(SessionStatus)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG Status
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(ChangeDebuggeeState)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG Flags,
|
|
||||||
__in ULONG64 Argument
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(ChangeEngineState)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG Flags,
|
|
||||||
__in ULONG64 Argument
|
|
||||||
);
|
|
||||||
|
|
||||||
STDMETHOD(ChangeSymbolState)(
|
|
||||||
THIS_
|
|
||||||
__in ULONG Flags,
|
|
||||||
__in ULONG64 Argument
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt);
|
|
||||||
};
|
|
||||||
|
|
||||||
class CdbDebugEventCallback : public CdbDebugEventCallbackBase
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CdbDebugEventCallback(CdbDebugEngine* dbg);
|
explicit CdbDebugEventCallback(CdbDebugEngine* dbg);
|
||||||
@@ -239,7 +124,7 @@ private:
|
|||||||
|
|
||||||
// Event handler logs exceptions to the debugger window
|
// Event handler logs exceptions to the debugger window
|
||||||
// and ignores the rest. To be used for running dumper calls.
|
// and ignores the rest. To be used for running dumper calls.
|
||||||
class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase
|
class CdbExceptionLoggerEventCallback : public CdbCore::DebugEventCallbackBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CdbExceptionLoggerEventCallback(int logChannel,
|
CdbExceptionLoggerEventCallback(int logChannel,
|
||||||
@@ -270,7 +155,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Event handler that ignores everything
|
// Event handler that ignores everything
|
||||||
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
|
class IgnoreDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit IgnoreDebugEventCallback();
|
explicit IgnoreDebugEventCallback();
|
||||||
@@ -281,19 +166,6 @@ public:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility class to temporarily redirect events to another handler
|
|
||||||
// as long as in scope
|
|
||||||
class EventCallbackRedirector {
|
|
||||||
Q_DISABLE_COPY(EventCallbackRedirector)
|
|
||||||
public:
|
|
||||||
explicit EventCallbackRedirector(CIDebugClient *client, IDebugEventCallbacksWide *cb);
|
|
||||||
~EventCallbackRedirector();
|
|
||||||
private:
|
|
||||||
CIDebugClient *m_client;
|
|
||||||
IDebugEventCallbacksWide *m_oldCb;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -37,62 +37,6 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
CdbDebugOutputBase::CdbDebugOutputBase()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugOutputBase::QueryInterface(
|
|
||||||
THIS_
|
|
||||||
IN REFIID InterfaceId,
|
|
||||||
OUT PVOID* Interface
|
|
||||||
)
|
|
||||||
{
|
|
||||||
*Interface = NULL;
|
|
||||||
|
|
||||||
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
|
||||||
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide)))
|
|
||||||
{
|
|
||||||
*Interface = (IDebugOutputCallbacksWide*)this;
|
|
||||||
AddRef();
|
|
||||||
return S_OK;
|
|
||||||
} else {
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP_(ULONG) CdbDebugOutputBase::AddRef(THIS)
|
|
||||||
{
|
|
||||||
// This class is designed to be static so
|
|
||||||
// there's no true refcount.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP_(ULONG) CdbDebugOutputBase::Release(THIS)
|
|
||||||
{
|
|
||||||
// This class is designed to be static so
|
|
||||||
// there's no true refcount.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHODIMP CdbDebugOutputBase::Output(
|
|
||||||
THIS_
|
|
||||||
IN ULONG mask,
|
|
||||||
IN PCWSTR text
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const QString msg = QString::fromUtf16(reinterpret_cast<const ushort *>(text));
|
|
||||||
output(mask, msg);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
IDebugOutputCallbacksWide *CdbDebugOutputBase::getOutputCallback(CIDebugClient *client)
|
|
||||||
{
|
|
||||||
IDebugOutputCallbacksWide *rc;
|
|
||||||
if (FAILED(client->GetOutputCallbacksWide(&rc)))
|
|
||||||
return 0;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------- CdbDebugOutput
|
// ------------------------- CdbDebugOutput
|
||||||
|
|
||||||
// Return a prefix for debugger messages
|
// Return a prefix for debugger messages
|
||||||
@@ -145,18 +89,5 @@ void CdbDebugOutput::output(ULONG mask, const QString &msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility class to temporarily redirect output to another handler
|
|
||||||
// as long as in scope
|
|
||||||
OutputRedirector::OutputRedirector(CIDebugClient *client, IDebugOutputCallbacksWide *newHandler) :
|
|
||||||
m_client(client),
|
|
||||||
m_oldHandler(CdbDebugOutputBase::getOutputCallback(client))
|
|
||||||
{
|
|
||||||
m_client->SetOutputCallbacksWide(newHandler);
|
|
||||||
}
|
|
||||||
OutputRedirector::~OutputRedirector()
|
|
||||||
{
|
|
||||||
m_client->SetOutputCallbacksWide(m_oldHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|||||||
@@ -30,49 +30,15 @@
|
|||||||
#ifndef DEBUGGER_CDBOUTPUT_H
|
#ifndef DEBUGGER_CDBOUTPUT_H
|
||||||
#define DEBUGGER_CDBOUTPUT_H
|
#define DEBUGGER_CDBOUTPUT_H
|
||||||
|
|
||||||
#include "cdbcom.h"
|
#include "debugoutputbase.h"
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// CdbDebugOutputBase is a base class for output handlers
|
|
||||||
// that takes care of the Active X magic and conversion to QString.
|
|
||||||
|
|
||||||
class CdbDebugOutputBase : public IDebugOutputCallbacksWide
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// IUnknown.
|
|
||||||
STDMETHOD(QueryInterface)(
|
|
||||||
THIS_
|
|
||||||
IN REFIID InterfaceId,
|
|
||||||
OUT PVOID* Interface
|
|
||||||
);
|
|
||||||
STDMETHOD_(ULONG, AddRef)(
|
|
||||||
THIS
|
|
||||||
);
|
|
||||||
STDMETHOD_(ULONG, Release)(
|
|
||||||
THIS
|
|
||||||
);
|
|
||||||
|
|
||||||
// IDebugOutputCallbacks.
|
|
||||||
STDMETHOD(Output)(
|
|
||||||
THIS_
|
|
||||||
IN ULONG mask,
|
|
||||||
IN PCWSTR text
|
|
||||||
);
|
|
||||||
|
|
||||||
// Helpers to retrieve the output callbacks IF
|
|
||||||
static IDebugOutputCallbacksWide *getOutputCallback(CIDebugClient *client);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
CdbDebugOutputBase();
|
|
||||||
virtual void output(ULONG mask, const QString &message) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Standard CDB output handler
|
// Standard CDB output handler
|
||||||
class CdbDebugOutput : public QObject, public CdbDebugOutputBase
|
class CdbDebugOutput : public QObject, public CdbCore::DebugOutputBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -88,34 +54,6 @@ signals:
|
|||||||
void debuggeeInputPrompt(const QString &message);
|
void debuggeeInputPrompt(const QString &message);
|
||||||
};
|
};
|
||||||
|
|
||||||
// An output handler that adds lines to a string (to be
|
|
||||||
// used for cases in which linebreaks occur in-between calls
|
|
||||||
// to output).
|
|
||||||
class StringOutputHandler : public CdbDebugOutputBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
StringOutputHandler() {}
|
|
||||||
QString result() const { return m_result; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void output(ULONG, const QString &message) { m_result += message; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utility class to temporarily redirect output to another handler
|
|
||||||
// as long as in scope
|
|
||||||
class OutputRedirector {
|
|
||||||
Q_DISABLE_COPY(OutputRedirector)
|
|
||||||
public:
|
|
||||||
explicit OutputRedirector(CIDebugClient *client, IDebugOutputCallbacksWide *newHandler);
|
|
||||||
~OutputRedirector();
|
|
||||||
private:
|
|
||||||
CIDebugClient *m_client;
|
|
||||||
IDebugOutputCallbacksWide *m_oldHandler;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -94,59 +94,13 @@ namespace Debugger {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// ------- Call load helpers
|
// ------- Call load helpers
|
||||||
// Alloc memory in debuggee using the ".dvalloc" command as
|
|
||||||
// there seems to be no API for it.
|
|
||||||
static bool allocDebuggeeMemory(CdbComInterfaces *cif,
|
|
||||||
int size, ULONG64 *address, QString *errorMessage)
|
|
||||||
{
|
|
||||||
*address = 0;
|
|
||||||
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
|
|
||||||
StringOutputHandler stringHandler;
|
|
||||||
OutputRedirector redir(cif->debugClient, &stringHandler);
|
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, allocCmd, errorMessage))
|
|
||||||
return false;
|
|
||||||
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
|
||||||
bool ok = false;
|
|
||||||
const QString output = stringHandler.result();
|
|
||||||
const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
|
|
||||||
if (lastBlank != -1) {
|
|
||||||
const qint64 addri = output.mid(lastBlank + 1).toLongLong(&ok, 16);
|
|
||||||
if (ok)
|
|
||||||
*address = addri;
|
|
||||||
}
|
|
||||||
if (loadDebug > 1)
|
|
||||||
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
|
||||||
if (!ok) {
|
|
||||||
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alloc an AscII string in debuggee
|
// Load a library into the debuggee. Currently requires
|
||||||
static bool createDebuggeeAscIIString(CdbComInterfaces *cif,
|
|
||||||
const QString &s,
|
|
||||||
ULONG64 *address,
|
|
||||||
QString *errorMessage)
|
|
||||||
{
|
|
||||||
QByteArray sAsciiData = s.toLocal8Bit();
|
|
||||||
sAsciiData += '\0';
|
|
||||||
if (!allocDebuggeeMemory(cif, sAsciiData.size(), address, errorMessage))
|
|
||||||
return false;
|
|
||||||
const HRESULT hr = cif->debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage= msgComFailed("WriteVirtual", hr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a library into the debuggee. Currently requires
|
|
||||||
// the QtCored4.pdb file to be present as we need "qstrdup"
|
// the QtCored4.pdb file to be present as we need "qstrdup"
|
||||||
// as dummy symbol. This is ok ATM since dumpers only
|
// as dummy symbol. This is ok ATM since dumpers only
|
||||||
// make sense for Qt apps.
|
// make sense for Qt apps.
|
||||||
static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
||||||
CdbComInterfaces *cif,
|
CdbCore::CoreEngine *engine,
|
||||||
unsigned long threadId,
|
unsigned long threadId,
|
||||||
const QString &moduleName,
|
const QString &moduleName,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
@@ -154,12 +108,13 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
|||||||
if (loadDebug > 1)
|
if (loadDebug > 1)
|
||||||
qDebug() << Q_FUNC_INFO << moduleName;
|
qDebug() << Q_FUNC_INFO << moduleName;
|
||||||
// Try to ignore the breakpoints, skip stray startup-complete trap exceptions
|
// Try to ignore the breakpoints, skip stray startup-complete trap exceptions
|
||||||
CdbExceptionLoggerEventCallback exLogger(LogWarning, true, manager);
|
QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, manager));
|
||||||
EventCallbackRedirector eventRedir(cif->debugClient, &exLogger);
|
CdbCore::EventCallbackRedirector eventRedir(engine, exLogger);
|
||||||
|
Q_UNUSED(eventRedir)
|
||||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||||
// and copy name over.
|
// and copy name over.
|
||||||
ULONG64 nameAddress;
|
ULONG64 nameAddress;
|
||||||
if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage))
|
if (!engine->createDebuggeeAscIIString(moduleName, &nameAddress, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
||||||
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
||||||
@@ -168,7 +123,7 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
|||||||
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
|
||||||
// reason, the symbol is present in QtGui as well without type information.
|
// reason, the symbol is present in QtGui as well without type information.
|
||||||
QString dummyFunc = QLatin1String("*qstrdup");
|
QString dummyFunc = QLatin1String("*qstrdup");
|
||||||
if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
if (resolveSymbol(engine->interfaces().debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QString callCmd; {
|
QString callCmd; {
|
||||||
@@ -179,16 +134,16 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
|||||||
if (loadDebug)
|
if (loadDebug)
|
||||||
qDebug() << "Calling" << callCmd;
|
qDebug() << "Calling" << callCmd;
|
||||||
|
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
|
if (!engine->executeDebuggerCommand(callCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
// Execute current thread. This will hit a breakpoint.
|
// Execute current thread. This will hit a breakpoint.
|
||||||
QString goCmd;
|
QString goCmd;
|
||||||
QTextStream(&goCmd) << '~' << threadId << " g";
|
QTextStream(&goCmd) << '~' << threadId << " g";
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, goCmd, errorMessage))
|
if (!engine->executeDebuggerCommand(goCmd, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
const HRESULT hr = engine->waitForEvent(waitTimeOutMS);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -339,13 +294,13 @@ void CdbDumperInitThread ::run()
|
|||||||
// ------------------- CdbDumperHelper
|
// ------------------- CdbDumperHelper
|
||||||
|
|
||||||
CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager,
|
CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager,
|
||||||
CdbComInterfaces *cif) :
|
CdbCore::CoreEngine *coreEngine) :
|
||||||
m_tryInjectLoad(true),
|
m_tryInjectLoad(true),
|
||||||
m_msgDisabled(QLatin1String("Dumpers are disabled")),
|
m_msgDisabled(QLatin1String("Dumpers are disabled")),
|
||||||
m_msgNotInScope(QLatin1String("Data not in scope")),
|
m_msgNotInScope(QLatin1String("Data not in scope")),
|
||||||
m_state(NotLoaded),
|
m_state(NotLoaded),
|
||||||
m_manager(manager),
|
m_manager(manager),
|
||||||
m_cif(cif),
|
m_coreEngine(coreEngine),
|
||||||
m_inBufferAddress(0),
|
m_inBufferAddress(0),
|
||||||
m_inBufferSize(0),
|
m_inBufferSize(0),
|
||||||
m_outBufferAddress(0),
|
m_outBufferAddress(0),
|
||||||
@@ -433,7 +388,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess
|
|||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
// Do we have Qt and are we already loaded by accident?
|
// Do we have Qt and are we already loaded by accident?
|
||||||
QStringList modules;
|
QStringList modules;
|
||||||
if (!getModuleNameList(m_cif->debugSymbols, &modules, errorMessage))
|
if (!getModuleNameList(m_coreEngine->interfaces().debugSymbols, &modules, errorMessage))
|
||||||
return CallLoadError;
|
return CallLoadError;
|
||||||
// Are we already loaded by some accident?
|
// Are we already loaded by some accident?
|
||||||
if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty())
|
if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty())
|
||||||
@@ -442,7 +397,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess
|
|||||||
if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty())
|
if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty())
|
||||||
return CallLoadNoQtApp;
|
return CallLoadNoQtApp;
|
||||||
// Try to load
|
// Try to load
|
||||||
if (!debuggeeLoadLibrary(m_manager, m_cif, m_dumperCallThread, m_library, errorMessage))
|
if (!debuggeeLoadLibrary(m_manager, m_coreEngine, m_dumperCallThread, m_library, errorMessage))
|
||||||
return CallLoadError;
|
return CallLoadError;
|
||||||
return CallLoadOk;
|
return CallLoadOk;
|
||||||
}
|
}
|
||||||
@@ -457,7 +412,7 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
|||||||
// Get address
|
// Get address
|
||||||
HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address);
|
HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetOffsetByNameWide", hr);
|
*errorMessage = CdbCore::msgComFailed("GetOffsetByNameWide", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Get size. Even works for arrays
|
// Get size. Even works for arrays
|
||||||
@@ -466,12 +421,12 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
|||||||
ULONG type;
|
ULONG type;
|
||||||
hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
|
hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetOffsetTypeId", hr);
|
*errorMessage = CdbCore::msgComFailed("GetOffsetTypeId", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hr = sg->GetTypeSize(moduleAddress, type, size);
|
hr = sg->GetTypeSize(moduleAddress, type, size);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetTypeSize", hr);
|
*errorMessage = CdbCore::msgComFailed("GetTypeSize", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // size desired
|
} // size desired
|
||||||
@@ -488,14 +443,14 @@ bool CdbDumperHelper::initResolveSymbols(QString *errorMessage)
|
|||||||
QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
|
QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
|
||||||
QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
|
QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
|
||||||
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
|
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
|
||||||
bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
bool rc = resolveSymbol(m_coreEngine->interfaces().debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
||||||
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
||||||
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
||||||
if (!rc)
|
if (!rc)
|
||||||
return false;
|
return false;
|
||||||
// Determine buffer addresses, sizes and alloc buffer
|
// Determine buffer addresses, sizes and alloc buffer
|
||||||
rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
rc = getSymbolAddress(m_coreEngine->interfaces().debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
||||||
&& getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
&& getSymbolAddress(m_coreEngine->interfaces().debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
return false;
|
return false;
|
||||||
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
|
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
|
||||||
@@ -532,41 +487,24 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to debuggee memory in chunks
|
|
||||||
bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage)
|
|
||||||
{
|
|
||||||
char *ptr = const_cast<char*>(buffer.data());
|
|
||||||
ULONG bytesToWrite = buffer.size();
|
|
||||||
while (bytesToWrite > 0) {
|
|
||||||
ULONG bytesWritten = 0;
|
|
||||||
const HRESULT hr = ds->WriteVirtual(address, ptr, bytesToWrite, &bytesWritten);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
*errorMessage = msgComFailed("WriteVirtual", hr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bytesToWrite -= bytesWritten;
|
|
||||||
ptr += bytesWritten;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CdbDumperHelper::CallResult
|
CdbDumperHelper::CallResult
|
||||||
CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
|
CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
|
||||||
bool ignoreAccessViolation, QString *errorMessage)
|
bool ignoreAccessViolation, QString *errorMessage)
|
||||||
{
|
{
|
||||||
*outDataPtr = 0;
|
*outDataPtr = 0;
|
||||||
// Skip stray startup-complete trap exceptions.
|
// Skip stray startup-complete trap exceptions.
|
||||||
CdbExceptionLoggerEventCallback exLogger(LogWarning, true, m_manager);
|
QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, m_manager));
|
||||||
EventCallbackRedirector eventRedir(m_cif->debugClient, &exLogger);
|
CdbCore::EventCallbackRedirector eventRedir(m_coreEngine, exLogger);
|
||||||
|
Q_UNUSED(eventRedir)
|
||||||
// write input buffer
|
// write input buffer
|
||||||
if (!inBuffer.isEmpty()) {
|
if (!inBuffer.isEmpty()) {
|
||||||
if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage))
|
if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage))
|
||||||
return CallFailed;
|
return CallFailed;
|
||||||
}
|
}
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage)) {
|
if (!m_coreEngine->executeDebuggerCommand(callCmd, errorMessage)) {
|
||||||
// Clear the outstanding call in case we triggered a debug library assert with a message box
|
// Clear the outstanding call in case we triggered a debug library assert with a message box
|
||||||
QString clearError;
|
QString clearError;
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QLatin1String(".call /c"), &clearError)) {
|
if (!m_coreEngine->executeDebuggerCommand(QLatin1String(".call /c"), &clearError)) {
|
||||||
*errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError);
|
*errorMessage += QString::fromLatin1("/Unable to clear call %1").arg(clearError);
|
||||||
}
|
}
|
||||||
return CallSyntaxError;
|
return CallSyntaxError;
|
||||||
@@ -575,40 +513,40 @@ CdbDumperHelper::CallResult
|
|||||||
// Try to skip debuggee crash exceptions and dumper exceptions
|
// Try to skip debuggee crash exceptions and dumper exceptions
|
||||||
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
|
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
const int oldExceptionCount = exLogger.exceptionCount();
|
const int oldExceptionCount = exLogger->exceptionCount();
|
||||||
// Go in current thread. If an exception occurs in loop 2,
|
// Go in current thread. If an exception occurs in loop 2,
|
||||||
// let the dumper handle it.
|
// let the dumper handle it.
|
||||||
QString goCmd = m_goCommand;
|
QString goCmd = m_goCommand;
|
||||||
if (i)
|
if (i)
|
||||||
goCmd = QLatin1Char('N');
|
goCmd = QLatin1Char('N');
|
||||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
|
if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage))
|
||||||
return CallFailed;
|
return CallFailed;
|
||||||
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||||
return CallFailed;
|
return CallFailed;
|
||||||
}
|
}
|
||||||
const int newExceptionCount = exLogger.exceptionCount();
|
const int newExceptionCount = exLogger->exceptionCount();
|
||||||
// no new exceptions? -> break
|
// no new exceptions? -> break
|
||||||
if (oldExceptionCount == newExceptionCount)
|
if (oldExceptionCount == newExceptionCount)
|
||||||
break;
|
break;
|
||||||
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
|
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
|
||||||
// else occurred.
|
// else occurred.
|
||||||
if (ignoreAccessViolation) {
|
if (ignoreAccessViolation) {
|
||||||
const QList<ULONG> newExceptionCodes = exLogger.exceptionCodes().mid(oldExceptionCount);
|
const QList<ULONG> newExceptionCodes = exLogger->exceptionCodes().mid(oldExceptionCount);
|
||||||
if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
|
if (newExceptionCodes.count(EXCEPTION_ACCESS_VIOLATION) == newExceptionCodes.size())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exLogger.exceptionCount()) {
|
if (exLogger->exceptionCount()) {
|
||||||
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
|
const QString exMsgs = exLogger->exceptionMessages().join(QString(QLatin1Char(',')));
|
||||||
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
||||||
return CallFailed;
|
return CallFailed;
|
||||||
}
|
}
|
||||||
// Read output
|
// Read output
|
||||||
const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
const HRESULT hr = m_coreEngine->interfaces().debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("ReadVirtual", hr);
|
*errorMessage = CdbCore::msgComFailed("ReadVirtual", hr);
|
||||||
return CallFailed;
|
return CallFailed;
|
||||||
}
|
}
|
||||||
// see QDumper implementation
|
// see QDumper implementation
|
||||||
@@ -742,7 +680,7 @@ CdbDumperHelper::DumpExecuteResult
|
|||||||
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
|
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
|
||||||
const char *outputData;
|
const char *outputData;
|
||||||
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
|
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
|
||||||
ExceptionBlocker eb(m_cif->debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException);
|
ExceptionBlocker eb(m_coreEngine->interfaces().debugControl, EXCEPTION_ACCESS_VIOLATION, ExceptionBlocker::IgnoreException);
|
||||||
if (!eb) {
|
if (!eb) {
|
||||||
*errorMessage = eb.errorString();
|
*errorMessage = eb.errorString();
|
||||||
return DumpExecuteCallFailed;
|
return DumpExecuteCallFailed;
|
||||||
@@ -808,11 +746,10 @@ bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QStri
|
|||||||
QString expression = QLatin1String("sizeof(");
|
QString expression = QLatin1String("sizeof(");
|
||||||
expression += typeName;
|
expression += typeName;
|
||||||
expression += QLatin1Char(')');
|
expression += QLatin1Char(')');
|
||||||
if (!CdbDebugEnginePrivate::evaluateExpression(m_cif->debugControl,
|
if (!m_coreEngine->evaluateExpression(expression, &sizeValue, errorMessage))
|
||||||
expression, &sizeValue, errorMessage))
|
|
||||||
return false;
|
return false;
|
||||||
qint64 size64;
|
qint64 size64;
|
||||||
if (!CdbSymbolGroupContext::debugValueToInteger(sizeValue, &size64)) {
|
if (!CdbCore::debugValueToInteger(sizeValue, &size64)) {
|
||||||
*errorMessage = QLatin1String("Expression result is not an integer");
|
*errorMessage = QLatin1String("Expression result is not an integer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -833,6 +770,11 @@ void CdbDumperHelper::setDumperCallThread(unsigned long t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CdbCore::ComInterfaces *CdbDumperHelper::comInterfaces() const
|
||||||
|
{
|
||||||
|
return &m_coreEngine->interfaces();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,15 @@
|
|||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
#include <QtCore/QMap>
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
class CoreEngine;
|
||||||
|
struct ComInterfaces;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
class DebuggerManager;
|
class DebuggerManager;
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
struct CdbComInterfaces;
|
|
||||||
class CdbDumperInitThread;
|
class CdbDumperInitThread;
|
||||||
|
|
||||||
/* For code clarity, all the stuff related to custom dumpers goes here.
|
/* For code clarity, all the stuff related to custom dumpers goes here.
|
||||||
@@ -81,8 +85,8 @@ public:
|
|||||||
Initialized, // List of types, etc. retrieved
|
Initialized, // List of types, etc. retrieved
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit CdbDumperHelper(DebuggerManager *manager,
|
explicit CdbDumperHelper(DebuggerManager *manager,
|
||||||
CdbComInterfaces *cif);
|
CdbCore::CoreEngine *coreEngine);
|
||||||
~CdbDumperHelper();
|
~CdbDumperHelper();
|
||||||
|
|
||||||
State state() const { return m_state; }
|
State state() const { return m_state; }
|
||||||
@@ -102,7 +106,7 @@ public:
|
|||||||
DumpResult dumpType(const WatchData &d, bool dumpChildren,
|
DumpResult dumpType(const WatchData &d, bool dumpChildren,
|
||||||
QList<WatchData> *result, QString *errorMessage);
|
QList<WatchData> *result, QString *errorMessage);
|
||||||
|
|
||||||
inline CdbComInterfaces *comInterfaces() const { return m_cif; }
|
const CdbCore::ComInterfaces *comInterfaces() const;
|
||||||
|
|
||||||
enum { InvalidDumperCallThread = 0xFFFFFFFF };
|
enum { InvalidDumperCallThread = 0xFFFFFFFF };
|
||||||
unsigned long dumperCallThread();
|
unsigned long dumperCallThread();
|
||||||
@@ -131,14 +135,12 @@ private:
|
|||||||
const QtDumperHelper::TypeData& td, bool dumpChildren,
|
const QtDumperHelper::TypeData& td, bool dumpChildren,
|
||||||
QList<WatchData> *result, QString *errorMessage);
|
QList<WatchData> *result, QString *errorMessage);
|
||||||
|
|
||||||
static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage);
|
|
||||||
|
|
||||||
const bool m_tryInjectLoad;
|
const bool m_tryInjectLoad;
|
||||||
const QString m_msgDisabled;
|
const QString m_msgDisabled;
|
||||||
const QString m_msgNotInScope;
|
const QString m_msgNotInScope;
|
||||||
State m_state;
|
State m_state;
|
||||||
DebuggerManager *m_manager;
|
DebuggerManager *m_manager;
|
||||||
CdbComInterfaces *m_cif;
|
CdbCore::CoreEngine *m_coreEngine;
|
||||||
|
|
||||||
QString m_library;
|
QString m_library;
|
||||||
QString m_dumpObjectSymbol;
|
QString m_dumpObjectSymbol;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ bool ExceptionBlocker::getExceptionParameters(CIDebugControl *ctrl, ULONG exCode
|
|||||||
{
|
{
|
||||||
const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result);
|
const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result);
|
||||||
if (FAILED(ihr)) {
|
if (FAILED(ihr)) {
|
||||||
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
|
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -132,7 +132,7 @@ bool ExceptionBlocker::setExceptionParameters(CIDebugControl *ctrl, const DEBUG_
|
|||||||
{
|
{
|
||||||
const HRESULT ihr = ctrl->SetExceptionFilterParameters(1, const_cast<DEBUG_EXCEPTION_FILTER_PARAMETERS*>(&p));
|
const HRESULT ihr = ctrl->SetExceptionFilterParameters(1, const_cast<DEBUG_EXCEPTION_FILTER_PARAMETERS*>(&p));
|
||||||
if (FAILED(ihr)) {
|
if (FAILED(ihr)) {
|
||||||
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
|
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *e
|
|||||||
ULONG loadedCount, unloadedCount;
|
ULONG loadedCount, unloadedCount;
|
||||||
const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
|
const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("GetNumberModules", hr);
|
*errorMessage= CdbCore::msgComFailed("GetNumberModules", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*count = loadedCount + unloadedCount;
|
*count = loadedCount + unloadedCount;
|
||||||
@@ -75,7 +75,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM
|
|||||||
HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
|
HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
|
||||||
// E_INVALIDARG indicates 'Partial results' according to docu
|
// E_INVALIDARG indicates 'Partial results' according to docu
|
||||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||||
*errorMessage= msgComFailed("GetModuleParameters", hr);
|
*errorMessage= CdbCore::msgComFailed("GetModuleParameters", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// fill array
|
// fill array
|
||||||
@@ -91,7 +91,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM
|
|||||||
module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16);
|
module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16);
|
||||||
hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
|
hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
|
||||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||||
*errorMessage= msgComFailed("GetModuleNameStringWide", hr);
|
*errorMessage= CdbCore::msgComFailed("GetModuleNameStringWide", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
module.moduleName = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
module.moduleName = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||||
@@ -116,7 +116,7 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgComFailed("StartSymbolMatchWide", hr);
|
*errorMessage= CdbCore::msgComFailed("StartSymbolMatchWide", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WCHAR wszBuf[MAX_PATH];
|
WCHAR wszBuf[MAX_PATH];
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
#include "cdboptions.h"
|
#include "cdboptions.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
|
||||||
#include <QtCore/QSettings>
|
#include <QtCore/QSettings>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
@@ -65,7 +66,7 @@ void CdbOptions::fromSettings(const QSettings *s)
|
|||||||
const QString enabledKey = keyRoot + QLatin1String(enabledKeyC);
|
const QString enabledKey = keyRoot + QLatin1String(enabledKeyC);
|
||||||
const bool firstTime = !s->contains(enabledKey);
|
const bool firstTime = !s->contains(enabledKey);
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
enabled = autoDetectPath(&path);
|
enabled = CdbCore::CoreEngine::autoDetectPath(&path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = s->value(enabledKey, false).toBool();
|
enabled = s->value(enabledKey, false).toBool();
|
||||||
@@ -86,46 +87,6 @@ void CdbOptions::toSettings(QSettings *s) const
|
|||||||
s->endGroup();
|
s->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdbOptions::autoDetectPath(QString *outPath, QStringList *checkedDirectories /* = 0 */)
|
|
||||||
{
|
|
||||||
// Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>" and its
|
|
||||||
// " (x86)", " (x64)" variations. Qt Creator needs 64/32 bit depending
|
|
||||||
// on how it was built.
|
|
||||||
#ifdef Q_OS_WIN64
|
|
||||||
static const char *postFixes[] = {" (x64)", " 64-bit" };
|
|
||||||
#else
|
|
||||||
static const char *postFixes[] = { " (x86)", " (x32)" };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
outPath->clear();
|
|
||||||
const QByteArray programDirB = qgetenv("ProgramFiles");
|
|
||||||
if (programDirB.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const QString programDir = QString::fromLocal8Bit(programDirB) + QDir::separator();
|
|
||||||
const QString installDir = QLatin1String("Debugging Tools For Windows");
|
|
||||||
|
|
||||||
QString path = programDir + installDir;
|
|
||||||
if (checkedDirectories)
|
|
||||||
checkedDirectories->push_back(path);
|
|
||||||
if (QFileInfo(path).isDir()) {
|
|
||||||
*outPath = QDir::toNativeSeparators(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const int rootLength = path.size();
|
|
||||||
for (int i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) {
|
|
||||||
path.truncate(rootLength);
|
|
||||||
path += QLatin1String(postFixes[i]);
|
|
||||||
if (checkedDirectories)
|
|
||||||
checkedDirectories->push_back(path);
|
|
||||||
if (QFileInfo(path).isDir()) {
|
|
||||||
*outPath = QDir::toNativeSeparators(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned CdbOptions::compare(const CdbOptions &rhs) const
|
unsigned CdbOptions::compare(const CdbOptions &rhs) const
|
||||||
{
|
{
|
||||||
unsigned rc = 0;
|
unsigned rc = 0;
|
||||||
|
|||||||
@@ -54,9 +54,6 @@ public:
|
|||||||
SymbolOptionsChanged = 0x4 };
|
SymbolOptionsChanged = 0x4 };
|
||||||
unsigned compare(const CdbOptions &s) const;
|
unsigned compare(const CdbOptions &s) const;
|
||||||
|
|
||||||
// Locate the debugging tools
|
|
||||||
static bool autoDetectPath(QString *path, QStringList *checkedDirectories = 0);
|
|
||||||
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
QString path;
|
QString path;
|
||||||
QStringList symbolPaths;
|
QStringList symbolPaths;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "cdboptionspage.h"
|
#include "cdboptionspage.h"
|
||||||
#include "cdboptions.h"
|
#include "cdboptions.h"
|
||||||
#include "debuggerconstants.h"
|
#include "debuggerconstants.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ void CdbOptionsPageWidget::autoDetect()
|
|||||||
{
|
{
|
||||||
QString path;
|
QString path;
|
||||||
QStringList checkedDirectories;
|
QStringList checkedDirectories;
|
||||||
const bool ok = CdbOptions::autoDetectPath(&path, &checkedDirectories);
|
const bool ok = CdbCore::CoreEngine::autoDetectPath(&path, &checkedDirectories);
|
||||||
m_ui.cdbPathGroupBox->setChecked(ok);
|
m_ui.cdbPathGroupBox->setChecked(ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
m_ui.pathChooser->setPath(path);
|
m_ui.pathChooser->setPath(path);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "cdbdumperhelper.h"
|
#include "cdbdumperhelper.h"
|
||||||
#include "debuggeractions.h"
|
#include "debuggeractions.h"
|
||||||
#include "watchhandler.h"
|
#include "watchhandler.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
#include "cdbstacktracecontext.h"
|
#include "cdbstacktracecontext.h"
|
||||||
|
#include "coreengine.h"
|
||||||
#include "cdbstackframecontext.h"
|
#include "cdbstackframecontext.h"
|
||||||
#include "cdbbreakpoint.h"
|
#include "cdbbreakpoint.h"
|
||||||
#include "cdbsymbolgroupcontext.h"
|
#include "cdbsymbolgroupcontext.h"
|
||||||
@@ -67,7 +68,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumpe
|
|||||||
const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
const HRESULT hr = dumper->comInterfaces()->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
delete ctx;
|
delete ctx;
|
||||||
*errorMessage = msgComFailed("GetStackTrace", hr);
|
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!ctx->init(frameCount, errorMessage)) {
|
if (!ctx->init(frameCount, errorMessage)) {
|
||||||
@@ -189,20 +190,20 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *
|
|||||||
CIDebugSymbolGroup *sg = 0;
|
CIDebugSymbolGroup *sg = 0;
|
||||||
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
|
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
|
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
|
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("SetScope", hr);
|
*errorMessage = CdbCore::msgComFailed("SetScope", hr);
|
||||||
sg->Release();
|
sg->Release();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// refresh with current frame
|
// refresh with current frame
|
||||||
hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
|
hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
|
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||||
sg->Release();
|
sg->Release();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -247,7 +248,7 @@ static inline QString msgGetThreadStateFailed(unsigned long threadId, const QStr
|
|||||||
|
|
||||||
// Determine information about thread. This changes the
|
// Determine information about thread. This changes the
|
||||||
// current thread to thread->id.
|
// current thread to thread->id.
|
||||||
static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif,
|
||||||
ThreadData *t,
|
ThreadData *t,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
{
|
{
|
||||||
@@ -255,13 +256,13 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
|||||||
ULONG currentThread;
|
ULONG currentThread;
|
||||||
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread);
|
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetCurrentThreadId", hr));
|
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (currentThread != t->id) {
|
if (currentThread != t->id) {
|
||||||
hr = cif.debugSystemObjects->SetCurrentThreadId(t->id);
|
hr = cif.debugSystemObjects->SetCurrentThreadId(t->id);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("SetCurrentThreadId", hr));
|
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +272,7 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
|||||||
DEBUG_STACK_FRAME frames[MaxFrames];
|
DEBUG_STACK_FRAME frames[MaxFrames];
|
||||||
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetStackTrace", hr));
|
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetStackTrace", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
|
||||||
@@ -307,7 +308,7 @@ static inline QString msgGetThreadsFailed(const QString &why)
|
|||||||
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
|
return QString::fromLatin1("Unable to determine the thread information: %1").arg(why);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||||
bool isStopped,
|
bool isStopped,
|
||||||
QList<ThreadData> *threads,
|
QList<ThreadData> *threads,
|
||||||
ULONG *currentThreadId,
|
ULONG *currentThreadId,
|
||||||
@@ -318,7 +319,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
|||||||
*currentThreadId = 0;
|
*currentThreadId = 0;
|
||||||
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetNumberThreads", hr));
|
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Get ids and index of current
|
// Get ids and index of current
|
||||||
@@ -326,14 +327,14 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
|||||||
return true;
|
return true;
|
||||||
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetCurrentThreadId", hr));
|
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<ULONG> threadIds(threadCount);
|
QVector<ULONG> threadIds(threadCount);
|
||||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0);
|
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetThreadIdsByIndex", hr));
|
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (ULONG i = 0; i < threadCount; i++) {
|
for (ULONG i = 0; i < threadCount; i++) {
|
||||||
@@ -350,7 +351,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
|||||||
if (isStopped && threads->back().id != *currentThreadId) {
|
if (isStopped && threads->back().id != *currentThreadId) {
|
||||||
hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
|
hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage= msgGetThreadsFailed(msgComFailed("SetCurrentThreadId", hr));
|
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,13 @@ QT_BEGIN_NAMESPACE
|
|||||||
class QTextStream;
|
class QTextStream;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
struct ComInterfaces;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
struct CdbComInterfaces;
|
|
||||||
class CdbSymbolGroupContext;
|
class CdbSymbolGroupContext;
|
||||||
class CdbStackFrameContext;
|
class CdbStackFrameContext;
|
||||||
class CdbDumperHelper;
|
class CdbDumperHelper;
|
||||||
@@ -93,7 +96,7 @@ public:
|
|||||||
|
|
||||||
// Retrieve information about threads. When stopped, add
|
// Retrieve information about threads. When stopped, add
|
||||||
// current stack frame.
|
// current stack frame.
|
||||||
static bool getThreads(const CdbComInterfaces &cif,
|
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
||||||
bool isStopped,
|
bool isStopped,
|
||||||
QList<ThreadData> *threads,
|
QList<ThreadData> *threads,
|
||||||
ULONG *currentThreadId,
|
ULONG *currentThreadId,
|
||||||
@@ -104,7 +107,7 @@ private:
|
|||||||
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
||||||
|
|
||||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||||
CdbComInterfaces *m_cif;
|
const CdbCore::ComInterfaces *m_cif;
|
||||||
|
|
||||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||||
QVector <CdbStackFrameContext*> m_frameContexts;
|
QVector <CdbStackFrameContext*> m_frameContexts;
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ bool CdbSymbolGroupContext::init(QString *errorMessage)
|
|||||||
ULONG count;
|
ULONG count;
|
||||||
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
|
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgComFailed("GetNumberSymbols", hr);
|
*errorMessage = CdbCore::msgComFailed("GetNumberSymbols", hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,8 @@ bool CdbSymbolGroupContext::init(QString *errorMessage)
|
|||||||
|
|
||||||
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
|
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO), msgComFailed("GetSymbolParameters", hr)).arg(count);
|
*errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO),
|
||||||
|
CdbCore::msgComFailed("GetSymbolParameters", hr)).arg(count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
populateINameIndexMap(m_prefix, DEBUG_ANY_ID, count);
|
populateINameIndexMap(m_prefix, DEBUG_ANY_ID, count);
|
||||||
@@ -355,7 +356,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in
|
|||||||
|
|
||||||
HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
|
HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("ExpandSymbol", hr));
|
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("ExpandSymbol", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Hopefully, this will never fail, else data structure will be foobar.
|
// Hopefully, this will never fail, else data structure will be foobar.
|
||||||
@@ -363,7 +364,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in
|
|||||||
ULONG newSize;
|
ULONG newSize;
|
||||||
hr = m_symbolGroup->GetNumberSymbols(&newSize);
|
hr = m_symbolGroup->GetNumberSymbols(&newSize);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetNumberSymbols", hr));
|
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetNumberSymbols", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +374,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in
|
|||||||
|
|
||||||
hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters());
|
hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters());
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetSymbolParameters", hr));
|
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetSymbolParameters", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// The new symbols are inserted after the parent symbol.
|
// The new symbols are inserted after the parent symbol.
|
||||||
@@ -531,7 +532,7 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val
|
|||||||
const HRESULT hr = m_symbolGroup->WriteSymbolWide(index, reinterpret_cast<PCWSTR>(value.utf16()));
|
const HRESULT hr = m_symbolGroup->WriteSymbolWide(index, reinterpret_cast<PCWSTR>(value.utf16()));
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
*errorMessage = QString::fromLatin1("Unable to assign '%1' to '%2': %3").
|
*errorMessage = QString::fromLatin1("Unable to assign '%1' to '%2': %3").
|
||||||
arg(value, iname, msgComFailed("WriteSymbolWide", hr));
|
arg(value, iname, CdbCore::msgComFailed("WriteSymbolWide", hr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (newValue)
|
if (newValue)
|
||||||
@@ -539,126 +540,6 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// format an array of integers as "0x323, 0x2322, ..."
|
|
||||||
template <class Integer>
|
|
||||||
static QString formatArrayHelper(const Integer *array, int size, int base = 10)
|
|
||||||
{
|
|
||||||
QString rc;
|
|
||||||
const QString hexPrefix = QLatin1String("0x");
|
|
||||||
const QString separator = QLatin1String(", ");
|
|
||||||
const bool hex = base == 16;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
if (i)
|
|
||||||
rc += separator;
|
|
||||||
if (hex)
|
|
||||||
rc += hexPrefix;
|
|
||||||
rc += QString::number(array[i], base);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CdbSymbolGroupContext::hexFormatArray(const unsigned short *array, int size)
|
|
||||||
{
|
|
||||||
return formatArrayHelper(array, size, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to format an integer with
|
|
||||||
// a hex prefix in case base = 16
|
|
||||||
template <class Integer>
|
|
||||||
inline QString formatInteger(Integer value, int base)
|
|
||||||
{
|
|
||||||
QString rc;
|
|
||||||
if (base == 16)
|
|
||||||
rc = QLatin1String("0x");
|
|
||||||
rc += QString::number(value, base);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl,
|
|
||||||
QString *qType,
|
|
||||||
int integerBase)
|
|
||||||
{
|
|
||||||
switch (dv.Type) {
|
|
||||||
case DEBUG_VALUE_INT8:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("char");
|
|
||||||
return formatInteger(dv.I8, integerBase);
|
|
||||||
case DEBUG_VALUE_INT16:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("short");
|
|
||||||
return formatInteger(static_cast<short>(dv.I16), integerBase);
|
|
||||||
case DEBUG_VALUE_INT32:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("long");
|
|
||||||
return formatInteger(static_cast<long>(dv.I32), integerBase);
|
|
||||||
case DEBUG_VALUE_INT64:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("long long");
|
|
||||||
return formatInteger(static_cast<long long>(dv.I64), integerBase);
|
|
||||||
case DEBUG_VALUE_FLOAT32:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("float");
|
|
||||||
return QString::number(dv.F32);
|
|
||||||
case DEBUG_VALUE_FLOAT64:
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("double");
|
|
||||||
return QString::number(dv.F64);
|
|
||||||
case DEBUG_VALUE_FLOAT80:
|
|
||||||
case DEBUG_VALUE_FLOAT128: { // Convert to double
|
|
||||||
DEBUG_VALUE doubleValue;
|
|
||||||
double d = 0.0;
|
|
||||||
if (SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue)))
|
|
||||||
d = dv.F64;
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String(dv.Type == DEBUG_VALUE_FLOAT80 ? "80bit-float" : "128bit-float");
|
|
||||||
return QString::number(d);
|
|
||||||
}
|
|
||||||
case DEBUG_VALUE_VECTOR64: {
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("64bit-vector");
|
|
||||||
QString rc = QLatin1String("bytes: ");
|
|
||||||
rc += formatArrayHelper(dv.VI8, 8, integerBase);
|
|
||||||
rc += QLatin1String(" long: ");
|
|
||||||
rc += formatArrayHelper(dv.VI32, 2, integerBase);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
case DEBUG_VALUE_VECTOR128: {
|
|
||||||
if (qType)
|
|
||||||
*qType = QLatin1String("128bit-vector");
|
|
||||||
QString rc = QLatin1String("bytes: ");
|
|
||||||
rc += formatArrayHelper(dv.VI8, 16, integerBase);
|
|
||||||
rc += QLatin1String(" long long: ");
|
|
||||||
rc += formatArrayHelper(dv.VI64, 2, integerBase);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (qType)
|
|
||||||
*qType = QString::fromLatin1("Unknown type #%1:").arg(dv.Type);
|
|
||||||
return formatArrayHelper(dv.RawBytes, 24, integerBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CdbSymbolGroupContext::debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value)
|
|
||||||
{
|
|
||||||
*value = 0;
|
|
||||||
switch (dv.Type) {
|
|
||||||
case DEBUG_VALUE_INT8:
|
|
||||||
*value = dv.I8;
|
|
||||||
return true;
|
|
||||||
case DEBUG_VALUE_INT16:
|
|
||||||
*value = static_cast<short>(dv.I16);
|
|
||||||
return true;
|
|
||||||
case DEBUG_VALUE_INT32:
|
|
||||||
*value = static_cast<long>(dv.I32);
|
|
||||||
return true;
|
|
||||||
case DEBUG_VALUE_INT64:
|
|
||||||
*value = static_cast<long long>(dv.I64);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The special type dumpers have an integer return code meaning:
|
/* The special type dumpers have an integer return code meaning:
|
||||||
* 0: ok
|
* 0: ok
|
||||||
* 1: Dereferencing or retrieving memory failed, this is out of scope,
|
* 1: Dereferencing or retrieving memory failed, this is out of scope,
|
||||||
|
|||||||
@@ -133,13 +133,6 @@ public:
|
|||||||
inline bool isExpanded(unsigned long index) const { return symbolState(index) == ExpandedSymbol; }
|
inline bool isExpanded(unsigned long index) const { return symbolState(index) == ExpandedSymbol; }
|
||||||
inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; }
|
inline bool isExpanded(const QString &prefix) const { return symbolState(prefix) == ExpandedSymbol; }
|
||||||
|
|
||||||
// Helper to convert a DEBUG_VALUE structure to a string representation
|
|
||||||
static QString debugValueToString(const DEBUG_VALUE &dv, CIDebugControl *ctl, QString *type = 0, int integerBase = 10);
|
|
||||||
static bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value);
|
|
||||||
|
|
||||||
// format an array of unsigned longs as "0x323, 0x2322, ..."
|
|
||||||
static QString hexFormatArray(const unsigned short *array, int size);
|
|
||||||
|
|
||||||
// Dump
|
// Dump
|
||||||
enum DumperResult { DumperOk, DumperError, DumperNotHandled };
|
enum DumperResult { DumperOk, DumperError, DumperNotHandled };
|
||||||
DumperResult dump(CIDebugDataSpaces *ds, WatchData *wd);
|
DumperResult dump(CIDebugDataSpaces *ds, WatchData *wd);
|
||||||
|
|||||||
944
src/plugins/debugger/cdb/coreengine.cpp
Normal file
944
src/plugins/debugger/cdb/coreengine.cpp
Normal file
@@ -0,0 +1,944 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "coreengine.h"
|
||||||
|
#include "debugeventcallbackbase.h"
|
||||||
|
#include "debugoutputbase.h"
|
||||||
|
|
||||||
|
#include <utils/winutils.h>
|
||||||
|
#include <utils/abstractprocess.h>
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QLibrary>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#define DBGHELP_TRANSLATE_TCHAR
|
||||||
|
#include <inc/Dbghelp.h>
|
||||||
|
|
||||||
|
static const char *dbgHelpDllC = "dbghelp";
|
||||||
|
static const char *dbgEngineDllC = "dbgeng";
|
||||||
|
static const char *debugCreateFuncC = "DebugCreate";
|
||||||
|
|
||||||
|
enum { debug = 0 };
|
||||||
|
|
||||||
|
static inline QString msgLibLoadFailed(const QString &lib, const QString &why)
|
||||||
|
{
|
||||||
|
return QCoreApplication::translate("Debugger::Cdb",
|
||||||
|
"Unable to load the debugger engine library '%1': %2").
|
||||||
|
arg(lib, why);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ULONG getInterruptTimeOutSecs(CIDebugControl *ctl)
|
||||||
|
{
|
||||||
|
ULONG rc = 0;
|
||||||
|
ctl->GetInterruptTimeout(&rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
QString msgDebugEngineComResult(HRESULT hr)
|
||||||
|
{
|
||||||
|
switch (hr) {
|
||||||
|
case S_OK:
|
||||||
|
return QLatin1String("S_OK");
|
||||||
|
case S_FALSE:
|
||||||
|
return QLatin1String("S_FALSE");
|
||||||
|
case E_FAIL:
|
||||||
|
break;
|
||||||
|
case E_INVALIDARG:
|
||||||
|
return QLatin1String("E_INVALIDARG");
|
||||||
|
case E_NOINTERFACE:
|
||||||
|
return QLatin1String("E_NOINTERFACE");
|
||||||
|
case E_OUTOFMEMORY:
|
||||||
|
return QLatin1String("E_OUTOFMEMORY");
|
||||||
|
case E_UNEXPECTED:
|
||||||
|
return QLatin1String("E_UNEXPECTED");
|
||||||
|
case E_NOTIMPL:
|
||||||
|
return QLatin1String("E_NOTIMPL");
|
||||||
|
}
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
|
||||||
|
return QLatin1String("ERROR_ACCESS_DENIED");;
|
||||||
|
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
|
||||||
|
return QLatin1String("STATUS_CONTROL_C_EXIT");
|
||||||
|
return QLatin1String("E_FAIL ") + Utils::winErrorMessage(HRESULT_CODE(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString msgComFailed(const char *func, HRESULT hr)
|
||||||
|
{
|
||||||
|
return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(func), msgDebugEngineComResult(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString msgDebuggerCommandFailed(const QString &command, HRESULT hr)
|
||||||
|
{
|
||||||
|
return QString::fromLatin1("Unable to execute '%1': %2").arg(command, msgDebugEngineComResult(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *msgExecutionStatusString(ULONG executionStatus)
|
||||||
|
{
|
||||||
|
switch (executionStatus) {
|
||||||
|
case DEBUG_STATUS_NO_CHANGE:
|
||||||
|
return "DEBUG_STATUS_NO_CHANGE";
|
||||||
|
case DEBUG_STATUS_GO:
|
||||||
|
return "DEBUG_STATUS_GO";
|
||||||
|
case DEBUG_STATUS_GO_HANDLED:
|
||||||
|
return "DEBUG_STATUS_GO_HANDLED";
|
||||||
|
case DEBUG_STATUS_GO_NOT_HANDLED:
|
||||||
|
return "DEBUG_STATUS_GO_NOT_HANDLED";
|
||||||
|
case DEBUG_STATUS_STEP_OVER:
|
||||||
|
return "DEBUG_STATUS_STEP_OVER";
|
||||||
|
case DEBUG_STATUS_STEP_INTO:
|
||||||
|
return "DEBUG_STATUS_STEP_INTO";
|
||||||
|
case DEBUG_STATUS_BREAK:
|
||||||
|
return "DEBUG_STATUS_BREAK";
|
||||||
|
case DEBUG_STATUS_NO_DEBUGGEE:
|
||||||
|
return "DEBUG_STATUS_NO_DEBUGGEE";
|
||||||
|
case DEBUG_STATUS_STEP_BRANCH:
|
||||||
|
return "DEBUG_STATUS_STEP_BRANCH";
|
||||||
|
case DEBUG_STATUS_IGNORE_EVENT:
|
||||||
|
return "DEBUG_STATUS_IGNORE_EVENT";
|
||||||
|
case DEBUG_STATUS_RESTART_REQUESTED:
|
||||||
|
return "DEBUG_STATUS_RESTART_REQUESTED";
|
||||||
|
case DEBUG_STATUS_REVERSE_GO:
|
||||||
|
return "DEBUG_STATUS_REVERSE_GO";
|
||||||
|
case DEBUG_STATUS_REVERSE_STEP_BRANCH:
|
||||||
|
return "DEBUG_STATUS_REVERSE_STEP_BRANCH";
|
||||||
|
case DEBUG_STATUS_REVERSE_STEP_OVER:
|
||||||
|
return "DEBUG_STATUS_REVERSE_STEP_OVER";
|
||||||
|
case DEBUG_STATUS_REVERSE_STEP_INTO:
|
||||||
|
return "DEBUG_STATUS_REVERSE_STEP_INTO";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "<Unknown execution status>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComInterfaces
|
||||||
|
ComInterfaces::ComInterfaces() :
|
||||||
|
debugClient(0),
|
||||||
|
debugControl(0),
|
||||||
|
debugSystemObjects(0),
|
||||||
|
debugSymbols(0),
|
||||||
|
debugRegisters(0),
|
||||||
|
debugDataSpaces(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin wrapper around the 'DBEng' debugger engine shared library
|
||||||
|
// which is loaded at runtime.
|
||||||
|
|
||||||
|
class DebuggerEngineLibrary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DebuggerEngineLibrary();
|
||||||
|
bool init(const QString &path, QString *dbgEngDLL, QString *errorMessage);
|
||||||
|
|
||||||
|
inline HRESULT debugCreate(REFIID interfaceId, PVOID *interfaceHandle) const
|
||||||
|
{ return m_debugCreate(interfaceId, interfaceHandle); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The exported functions of the library
|
||||||
|
typedef HRESULT (STDAPICALLTYPE *DebugCreateFunction)(REFIID, PVOID *);
|
||||||
|
|
||||||
|
DebugCreateFunction m_debugCreate;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------- DebuggerEngineLibrary
|
||||||
|
DebuggerEngineLibrary::DebuggerEngineLibrary() :
|
||||||
|
m_debugCreate(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a lib name as "Path\x.dll"
|
||||||
|
static inline QString libPath(const QString &libName, const QString &path = QString())
|
||||||
|
{
|
||||||
|
QString rc = path;
|
||||||
|
if (!rc.isEmpty())
|
||||||
|
rc += QDir::separator();
|
||||||
|
rc += libName;
|
||||||
|
rc += QLatin1String(".dll");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebuggerEngineLibrary::init(const QString &path,
|
||||||
|
QString *dbgEngDLL,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
// Load the dependent help lib first
|
||||||
|
const QString helpLibPath = libPath(QLatin1String(dbgHelpDllC), path);
|
||||||
|
QLibrary helpLib(helpLibPath, 0);
|
||||||
|
if (!helpLib.isLoaded() && !helpLib.load()) {
|
||||||
|
*errorMessage = msgLibLoadFailed(helpLibPath, helpLib.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Load dbgeng lib
|
||||||
|
const QString engineLibPath = libPath(QLatin1String(dbgEngineDllC), path);
|
||||||
|
QLibrary lib(engineLibPath, 0);
|
||||||
|
if (!lib.isLoaded() && !lib.load()) {
|
||||||
|
*errorMessage = msgLibLoadFailed(engineLibPath, lib.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*dbgEngDLL = engineLibPath;
|
||||||
|
// Locate symbols
|
||||||
|
void *createFunc = lib.resolve(debugCreateFuncC);
|
||||||
|
if (!createFunc) {
|
||||||
|
*errorMessage = QCoreApplication::translate("Debugger::Cdb", "Unable to resolve '%1' in the debugger engine library '%2'").
|
||||||
|
arg(QLatin1String(debugCreateFuncC), QLatin1String(dbgEngineDllC));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_debugCreate = static_cast<DebugCreateFunction>(createFunc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------ Engine
|
||||||
|
CoreEngine::CoreEngine(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_watchTimer(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::~CoreEngine()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (m_cif.debugClient) {
|
||||||
|
m_cif.debugClient->SetOutputCallbacksWide(0);
|
||||||
|
m_cif.debugClient->SetEventCallbacksWide(0);
|
||||||
|
m_cif.debugClient->Release();
|
||||||
|
}
|
||||||
|
if (m_cif.debugControl)
|
||||||
|
m_cif.debugControl->Release();
|
||||||
|
if (m_cif.debugSystemObjects)
|
||||||
|
m_cif.debugSystemObjects->Release();
|
||||||
|
if (m_cif.debugSymbols)
|
||||||
|
m_cif.debugSymbols->Release();
|
||||||
|
if (m_cif.debugRegisters)
|
||||||
|
m_cif.debugRegisters->Release();
|
||||||
|
if (m_cif.debugDataSpaces)
|
||||||
|
m_cif.debugDataSpaces->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::init(const QString &dllEnginePath, QString *errorMessage)
|
||||||
|
{
|
||||||
|
enum { bufLen = 10240 };
|
||||||
|
// Load the DLL
|
||||||
|
DebuggerEngineLibrary lib;
|
||||||
|
if (!lib.init(dllEnginePath, &m_dbengDLL, errorMessage))
|
||||||
|
return false;
|
||||||
|
// Initialize the COM interfaces
|
||||||
|
HRESULT hr;
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_cif.debugClient));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugClient5 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugControl4), reinterpret_cast<void**>(&m_cif.debugControl));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugControl4 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugSystemObjects4), reinterpret_cast<void**>(&m_cif.debugSystemObjects));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugSystemObjects4 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugSymbols3), reinterpret_cast<void**>(&m_cif.debugSymbols));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugSymbols3 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WCHAR buf[bufLen];
|
||||||
|
hr = m_cif.debugSymbols->GetImagePathWide(buf, bufLen, 0);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgComFailed("GetImagePathWide", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_baseImagePath = QString::fromUtf16(buf);
|
||||||
|
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_cif.debugRegisters));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugRegisters2 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = lib.debugCreate( __uuidof(IDebugDataSpaces4), reinterpret_cast<void**>(&m_cif.debugDataSpaces));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Creation of IDebugDataSpaces4 failed: %1").arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
qDebug() << QString::fromLatin1("CDB Initialization succeeded, interrupt time out %1s.").arg(getInterruptTimeOutSecs(m_cif.debugControl));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::DebugOutputBasePtr
|
||||||
|
CoreEngine::setDebugOutput(const DebugOutputBasePtr &o)
|
||||||
|
{
|
||||||
|
const CoreEngine::DebugOutputBasePtr old = m_debugOutput;
|
||||||
|
m_debugOutput = o;
|
||||||
|
m_cif.debugClient->SetOutputCallbacksWide(m_debugOutput.data());
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::DebugEventCallbackBasePtr
|
||||||
|
CoreEngine::setDebugEventCallback(const DebugEventCallbackBasePtr &e)
|
||||||
|
{
|
||||||
|
const CoreEngine::DebugEventCallbackBasePtr old = m_debugEventCallback;
|
||||||
|
m_debugEventCallback = e;
|
||||||
|
m_cif.debugClient->SetEventCallbacksWide(m_debugEventCallback.data());
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreEngine::startWatchTimer()
|
||||||
|
{
|
||||||
|
if (debug)
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
|
if (m_watchTimer == -1)
|
||||||
|
m_watchTimer = startTimer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreEngine::killWatchTimer()
|
||||||
|
{
|
||||||
|
if (debug)
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
|
if (m_watchTimer != -1) {
|
||||||
|
killTimer(m_watchTimer);
|
||||||
|
m_watchTimer = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CoreEngine::waitForEvent(int timeOutMS)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugControl->WaitForEvent(0, timeOutMS);
|
||||||
|
if (debug)
|
||||||
|
if (debug > 1 || hr != S_FALSE)
|
||||||
|
qDebug() << Q_FUNC_INFO << "WaitForEvent" << timeOutMS << msgDebugEngineComResult(hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreEngine::timerEvent(QTimerEvent* te)
|
||||||
|
{
|
||||||
|
// Fetch away the debug events and notify if debuggee
|
||||||
|
// stops. Note that IDebugEventCallback does not
|
||||||
|
// cover all cases of a debuggee stopping execution
|
||||||
|
// (such as step over,etc).
|
||||||
|
if (te->timerId() != m_watchTimer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (waitForEvent(1)) {
|
||||||
|
case S_OK:
|
||||||
|
killWatchTimer();
|
||||||
|
emit watchTimerDebugEvent();
|
||||||
|
break;
|
||||||
|
case S_FALSE:
|
||||||
|
case E_PENDING:
|
||||||
|
case E_FAIL:
|
||||||
|
break;
|
||||||
|
case E_UNEXPECTED: // Occurs on ExitProcess.
|
||||||
|
killWatchTimer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::startDebuggerWithExecutable(const QString &workingDirectory,
|
||||||
|
const QString &filename,
|
||||||
|
const QStringList &args,
|
||||||
|
const QStringList &envList,
|
||||||
|
bool verboseSymbolLoading,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
DEBUG_CREATE_PROCESS_OPTIONS dbgopts;
|
||||||
|
memset(&dbgopts, 0, sizeof(dbgopts));
|
||||||
|
dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
|
||||||
|
|
||||||
|
// Set image path
|
||||||
|
const QFileInfo fi(filename);
|
||||||
|
QString imagePath = QDir::toNativeSeparators(fi.absolutePath());
|
||||||
|
if (!m_baseImagePath.isEmpty()) {
|
||||||
|
imagePath += QLatin1Char(';');
|
||||||
|
imagePath += m_baseImagePath;
|
||||||
|
}
|
||||||
|
HRESULT hr = m_cif.debugSymbols->SetImagePathWide(reinterpret_cast<PCWSTR>(imagePath.utf16()));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = tr("Unable to set the image path to %1: %2").arg(imagePath, msgComFailed("SetImagePathWide", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
qDebug() << Q_FUNC_INFO <<'\n' << filename << imagePath;
|
||||||
|
|
||||||
|
ULONG symbolOptions = SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS;
|
||||||
|
if (verboseSymbolLoading)
|
||||||
|
symbolOptions |= SYMOPT_DEBUG;
|
||||||
|
m_cif.debugSymbols->SetSymbolOptions(symbolOptions);
|
||||||
|
|
||||||
|
const QString cmd = Utils::AbstractProcess::createWinCommandline(filename, args);
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "Starting " << cmd;
|
||||||
|
PCWSTR env = 0;
|
||||||
|
QByteArray envData;
|
||||||
|
if (!envList.empty()) {
|
||||||
|
envData = Utils::AbstractProcess::createWinEnvironment(Utils::AbstractProcess::fixWinEnvironment(envList));
|
||||||
|
env = reinterpret_cast<PCWSTR>(envData.data());
|
||||||
|
}
|
||||||
|
// The working directory cannot be empty.
|
||||||
|
PCWSTR workingDirC = 0;
|
||||||
|
const QString workingDirN = workingDirectory.isEmpty() ? QString() : QDir::toNativeSeparators(workingDirectory);
|
||||||
|
if (!workingDirN.isEmpty())
|
||||||
|
workingDirC = workingDirN.utf16();
|
||||||
|
hr = m_cif.debugClient->CreateProcess2Wide(NULL,
|
||||||
|
reinterpret_cast<PWSTR>(const_cast<ushort *>(cmd.utf16())),
|
||||||
|
&dbgopts, sizeof(dbgopts),
|
||||||
|
workingDirC, env);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = tr("Unable to create a process '%1': %2").arg(cmd, msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::startAttachDebugger(qint64 pid,
|
||||||
|
bool suppressInitialBreakPoint,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
// Need to attrach invasively, otherwise, no notification signals
|
||||||
|
// for for CreateProcess/ExitProcess occur.
|
||||||
|
// Initial breakpoint occur:
|
||||||
|
// 1) Desired: When attaching to a crashed process
|
||||||
|
// 2) Undesired: When starting up a console process, in conjunction
|
||||||
|
// with the 32bit Wow-engine
|
||||||
|
// As of version 6.11, the flag only affects 1). 2) Still needs to be suppressed
|
||||||
|
// by lookup at the state of the application (startup trap). However,
|
||||||
|
// there is no startup trap when attaching to a process that has been
|
||||||
|
// running for a while. (see notifyException).
|
||||||
|
ULONG flags = DEBUG_ATTACH_INVASIVE_RESUME_PROCESS;
|
||||||
|
if (suppressInitialBreakPoint)
|
||||||
|
flags |= DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK;
|
||||||
|
const HRESULT hr = m_cif.debugClient->AttachProcess(NULL, pid, flags);
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "Attaching to " << pid << " using flags" << flags << " returns " << hr;
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = tr("Attaching to a process failed for process id %1: %2").arg(pid).arg(msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString pathString(const QStringList &s)
|
||||||
|
{ return s.join(QString(QLatin1Char(';'))); }
|
||||||
|
|
||||||
|
QStringList CoreEngine::sourcePaths() const
|
||||||
|
{
|
||||||
|
WCHAR wszBuf[MAX_PATH];
|
||||||
|
if (SUCCEEDED(m_cif.debugSymbols->GetSourcePathWide(wszBuf, MAX_PATH, 0)))
|
||||||
|
return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';'));
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::setSourcePaths(const QStringList &s, QString *errorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugSymbols->SetSourcePathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16()));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = msgComFailed("SetSourcePathWide", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList CoreEngine::symbolPaths() const
|
||||||
|
{
|
||||||
|
WCHAR wszBuf[MAX_PATH];
|
||||||
|
if (SUCCEEDED(m_cif.debugSymbols->GetSymbolPathWide(wszBuf, MAX_PATH, 0)))
|
||||||
|
return QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)).split(QLatin1Char(';'));
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::setSymbolPaths(const QStringList &s, QString *errorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugSymbols->SetSymbolPathWide(reinterpret_cast<PCWSTR>(pathString(s).utf16()));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = msgComFailed("SetSymbolPathWide", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::executeDebuggerCommand(const QString &command, QString *errorMessage)
|
||||||
|
{
|
||||||
|
// output to all clients, else we do not see anything
|
||||||
|
const HRESULT hr = m_cif.debugControl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, reinterpret_cast<PCWSTR>(command.utf16()), 0);
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
|
||||||
|
arg(command, msgDebugEngineComResult(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Those should not fail
|
||||||
|
CoreEngine::ExpressionSyntax CoreEngine::expressionSyntax() const
|
||||||
|
{
|
||||||
|
ULONG expressionSyntax = DEBUG_EXPR_MASM;
|
||||||
|
const HRESULT hr = m_cif.debugControl->GetExpressionSyntax(&expressionSyntax);
|
||||||
|
if (FAILED(hr))
|
||||||
|
qWarning("Unable to retrieve expression syntax: %s", qPrintable(msgComFailed("GetExpressionSyntax", hr)));
|
||||||
|
return expressionSyntax == DEBUG_EXPR_MASM ? AssemblerExpressionSyntax : CppExpressionSyntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::ExpressionSyntax CoreEngine::setExpressionSyntax(ExpressionSyntax es)
|
||||||
|
{
|
||||||
|
const ExpressionSyntax old = expressionSyntax();
|
||||||
|
if (old != es) {
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "Setting expression syntax" << es;
|
||||||
|
const HRESULT hr = m_cif.debugControl->SetExpressionSyntax(es == AssemblerExpressionSyntax ? DEBUG_EXPR_MASM : DEBUG_EXPR_CPLUSPLUS);
|
||||||
|
if (FAILED(hr))
|
||||||
|
qWarning("Unable to set expression syntax: %s", qPrintable(msgComFailed("SetExpressionSyntax", hr)));
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::CodeLevel CoreEngine::codeLevel() const
|
||||||
|
{
|
||||||
|
ULONG currentCodeLevel = DEBUG_LEVEL_ASSEMBLY;
|
||||||
|
HRESULT hr = m_cif.debugControl->GetCodeLevel(¤tCodeLevel);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
qWarning("Cannot determine code level: %s", qPrintable(msgComFailed("GetCodeLevel", hr)));
|
||||||
|
}
|
||||||
|
return currentCodeLevel == DEBUG_LEVEL_ASSEMBLY ? CodeLevelAssembly : CodeLevelSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreEngine::CodeLevel CoreEngine::setCodeLevel(CodeLevel cl)
|
||||||
|
{
|
||||||
|
const CodeLevel old = codeLevel();
|
||||||
|
if (old != cl) {
|
||||||
|
if (debug)
|
||||||
|
qDebug() << "Setting code level" << cl;
|
||||||
|
const HRESULT hr = m_cif.debugControl->SetCodeLevel(cl == CodeLevelAssembly ? DEBUG_LEVEL_ASSEMBLY : DEBUG_LEVEL_SOURCE);
|
||||||
|
if (FAILED(hr))
|
||||||
|
qWarning("Unable to set code level: %s", qPrintable(msgComFailed("SetCodeLevel", hr)));
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::evaluateExpression(const QString &expression,
|
||||||
|
QString *value,
|
||||||
|
QString *type,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
DEBUG_VALUE debugValue;
|
||||||
|
if (!evaluateExpression(expression, &debugValue, errorMessage))
|
||||||
|
return false;
|
||||||
|
*value = debugValueToString(debugValue, type, 10, m_cif.debugControl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::evaluateExpression(const QString &expression,
|
||||||
|
DEBUG_VALUE *debugValue,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
if (debug > 1)
|
||||||
|
qDebug() << Q_FUNC_INFO << expression;
|
||||||
|
|
||||||
|
memset(debugValue, 0, sizeof(DEBUG_VALUE));
|
||||||
|
// Use CPP syntax, original syntax must be restored, else setting breakpoints will fail.
|
||||||
|
SyntaxSetter syntaxSetter(this, CppExpressionSyntax);
|
||||||
|
Q_UNUSED(syntaxSetter)
|
||||||
|
ULONG errorPosition = 0;
|
||||||
|
const HRESULT hr = m_cif.debugControl->EvaluateWide(reinterpret_cast<PCWSTR>(expression.utf16()),
|
||||||
|
DEBUG_VALUE_INVALID, debugValue,
|
||||||
|
&errorPosition);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
if (HRESULT_CODE(hr) == 517) {
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
|
||||||
|
arg(expression);
|
||||||
|
} else {
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Error at %2: %3").
|
||||||
|
arg(expression).arg(errorPosition).arg(msgDebugEngineComResult(hr));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG CoreEngine::executionStatus() const
|
||||||
|
{
|
||||||
|
ULONG ex = DEBUG_STATUS_NO_CHANGE;
|
||||||
|
const HRESULT hr = m_cif.debugControl->GetExecutionStatus(&ex);
|
||||||
|
if (FAILED(hr))
|
||||||
|
qWarning("Cannot determine execution status: %s", qPrintable(msgComFailed("GetExecutionStatus", hr)));
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::setExecutionStatus(ULONG ex, QString *errorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugControl->SetExecutionStatus(ex);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Cannot set execution status to %1: %2")
|
||||||
|
.arg(QLatin1String(msgExecutionStatusString(ex)), msgComFailed("SetExecutionStatus", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void appendError(const QString &what, QString *appendableErrorMessage)
|
||||||
|
{
|
||||||
|
if (!appendableErrorMessage->isEmpty())
|
||||||
|
appendableErrorMessage->append(QLatin1Char('\n'));
|
||||||
|
appendableErrorMessage->append(what);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::detachCurrentProcess(QString *appendableErrorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugClient->DetachCurrentProcess();
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
appendError(msgComFailed("DetachCurrentProcess", hr), appendableErrorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::terminateCurrentProcess(QString *appendableErrorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugClient->TerminateCurrentProcess();
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
appendError(msgComFailed("TerminateCurrentProcess", hr), appendableErrorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::terminateProcesses(QString *appendableErrorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugClient->TerminateProcesses();
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
appendError(msgComFailed("TerminateProcesses", hr), appendableErrorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::endSession(QString *appendableErrorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
appendError(msgComFailed("EndSession", hr), appendableErrorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::allocDebuggeeMemory(int size, ULONG64 *address, QString *errorMessage)
|
||||||
|
{
|
||||||
|
*address = 0;
|
||||||
|
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
|
||||||
|
|
||||||
|
QSharedPointer<StringOutputHandler> outputHandler(new StringOutputHandler);
|
||||||
|
OutputRedirector redir(this, outputHandler);
|
||||||
|
Q_UNUSED(redir)
|
||||||
|
if (!executeDebuggerCommand(allocCmd, errorMessage))
|
||||||
|
return false;
|
||||||
|
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
|
||||||
|
bool ok = false;
|
||||||
|
const QString output = outputHandler->result();
|
||||||
|
const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
|
||||||
|
if (lastBlank != -1) {
|
||||||
|
const qint64 addri = output.mid(lastBlank + 1).toLongLong(&ok, 16);
|
||||||
|
if (ok)
|
||||||
|
*address = addri;
|
||||||
|
}
|
||||||
|
if (debug > 1)
|
||||||
|
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
||||||
|
if (!ok) {
|
||||||
|
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc an AscII string in debuggee
|
||||||
|
bool CoreEngine::createDebuggeeAscIIString(const QString &s,
|
||||||
|
ULONG64 *address,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
QByteArray sAsciiData = s.toLocal8Bit();
|
||||||
|
sAsciiData += '\0';
|
||||||
|
if (!allocDebuggeeMemory(sAsciiData.size(), address, errorMessage))
|
||||||
|
return false;
|
||||||
|
const HRESULT hr = m_cif.debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage= msgComFailed("WriteVirtual", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to debuggee memory in chunks
|
||||||
|
bool CoreEngine::writeToDebuggee(const QByteArray &buffer, quint64 address, QString *errorMessage)
|
||||||
|
{
|
||||||
|
char *ptr = const_cast<char*>(buffer.data());
|
||||||
|
ULONG bytesToWrite = buffer.size();
|
||||||
|
while (bytesToWrite > 0) {
|
||||||
|
ULONG bytesWritten = 0;
|
||||||
|
const HRESULT hr = m_cif.debugDataSpaces->WriteVirtual(address, ptr, bytesToWrite, &bytesWritten);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = msgComFailed("WriteVirtual", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bytesToWrite -= bytesWritten;
|
||||||
|
ptr += bytesWritten;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::dissassemble(ULONG64 offset,
|
||||||
|
unsigned long beforeLines,
|
||||||
|
unsigned long afterLines,
|
||||||
|
QString *target,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
const ULONG flags = DEBUG_DISASM_MATCHING_SYMBOLS|DEBUG_DISASM_SOURCE_LINE_NUMBER|DEBUG_DISASM_SOURCE_FILE_NAME;
|
||||||
|
// Catch the output by temporarily setting another handler.
|
||||||
|
// We use the method that outputs to the output handler as it
|
||||||
|
// conveniently provides the 'beforeLines' context (stepping back
|
||||||
|
// in assembler code). We build a complete string first as line breaks
|
||||||
|
// may occur in-between messages.
|
||||||
|
QSharedPointer<StringOutputHandler> outputHandler(new StringOutputHandler);
|
||||||
|
OutputRedirector redir(this, outputHandler);
|
||||||
|
// For some reason, we need to output to "all clients"
|
||||||
|
const HRESULT hr = m_cif.debugControl->OutputDisassemblyLines(DEBUG_OUTCTL_ALL_CLIENTS,
|
||||||
|
beforeLines, beforeLines + afterLines,
|
||||||
|
offset, flags, 0, 0, 0, 0);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage= QString::fromLatin1("Unable to dissamble at 0x%1: %2").
|
||||||
|
arg(offset, 0, 16).arg(msgComFailed("OutputDisassemblyLines", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*target = outputHandler->result();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::autoDetectPath(QString *outPath,
|
||||||
|
QStringList *checkedDirectories /* = 0 */)
|
||||||
|
{
|
||||||
|
// Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>" and its
|
||||||
|
// " (x86)", " (x64)" variations. Qt Creator needs 64/32 bit depending
|
||||||
|
// on how it was built.
|
||||||
|
#ifdef Q_OS_WIN64
|
||||||
|
static const char *postFixes[] = {" (x64)", " 64-bit" };
|
||||||
|
#else
|
||||||
|
static const char *postFixes[] = { " (x86)", " (x32)" };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outPath->clear();
|
||||||
|
const QByteArray programDirB = qgetenv("ProgramFiles");
|
||||||
|
if (programDirB.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QString programDir = QString::fromLocal8Bit(programDirB) + QDir::separator();
|
||||||
|
const QString installDir = QLatin1String("Debugging Tools For Windows");
|
||||||
|
|
||||||
|
QString path = programDir + installDir;
|
||||||
|
if (checkedDirectories)
|
||||||
|
checkedDirectories->push_back(path);
|
||||||
|
if (QFileInfo(path).isDir()) {
|
||||||
|
*outPath = QDir::toNativeSeparators(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const int rootLength = path.size();
|
||||||
|
for (int i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) {
|
||||||
|
path.truncate(rootLength);
|
||||||
|
path += QLatin1String(postFixes[i]);
|
||||||
|
if (checkedDirectories)
|
||||||
|
checkedDirectories->push_back(path);
|
||||||
|
if (QFileInfo(path).isDir()) {
|
||||||
|
*outPath = QDir::toNativeSeparators(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::debugBreakProcess(HANDLE hProcess, QString *errorMessage)
|
||||||
|
{
|
||||||
|
const bool rc = DebugBreakProcess(hProcess);
|
||||||
|
if (!rc)
|
||||||
|
*errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError()));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreEngine::setInterrupt(QString *errorMessage)
|
||||||
|
{
|
||||||
|
const HRESULT hr = interfaces().debugControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE|DEBUG_INTERRUPT_EXIT);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to interrupt debuggee after %1s: %2").
|
||||||
|
arg(getInterruptTimeOutSecs(interfaces().debugControl)).arg(msgComFailed("SetInterrupt", hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------- DEBUG_VALUE formatting helpers
|
||||||
|
|
||||||
|
// format an array of integers as "0x323, 0x2322, ..."
|
||||||
|
template <class Integer>
|
||||||
|
static QString formatArrayHelper(const Integer *array, int size, int base = 10)
|
||||||
|
{
|
||||||
|
QString rc;
|
||||||
|
const QString hexPrefix = QLatin1String("0x");
|
||||||
|
const QString separator = QLatin1String(", ");
|
||||||
|
const bool hex = base == 16;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (i)
|
||||||
|
rc += separator;
|
||||||
|
if (hex)
|
||||||
|
rc += hexPrefix;
|
||||||
|
rc += QString::number(array[i], base);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString hexFormatArray(const unsigned short *array, int size)
|
||||||
|
{
|
||||||
|
return formatArrayHelper(array, size, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to format an integer with
|
||||||
|
// a hex prefix in case base = 16
|
||||||
|
template <class Integer>
|
||||||
|
inline QString formatInteger(Integer value, int base)
|
||||||
|
{
|
||||||
|
QString rc;
|
||||||
|
if (base == 16)
|
||||||
|
rc = QLatin1String("0x");
|
||||||
|
rc += QString::number(value, base);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString debugValueToString(const DEBUG_VALUE &dv,
|
||||||
|
QString *qType /* =0 */,
|
||||||
|
int integerBase,
|
||||||
|
CIDebugControl *ctl /* =0 */)
|
||||||
|
{
|
||||||
|
switch (dv.Type) {
|
||||||
|
case DEBUG_VALUE_INT8:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("char");
|
||||||
|
return formatInteger(dv.I8, integerBase);
|
||||||
|
case DEBUG_VALUE_INT16:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("short");
|
||||||
|
return formatInteger(static_cast<short>(dv.I16), integerBase);
|
||||||
|
case DEBUG_VALUE_INT32:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("long");
|
||||||
|
return formatInteger(static_cast<long>(dv.I32), integerBase);
|
||||||
|
case DEBUG_VALUE_INT64:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("long long");
|
||||||
|
return formatInteger(static_cast<long long>(dv.I64), integerBase);
|
||||||
|
case DEBUG_VALUE_FLOAT32:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("float");
|
||||||
|
return QString::number(dv.F32);
|
||||||
|
case DEBUG_VALUE_FLOAT64:
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("double");
|
||||||
|
return QString::number(dv.F64);
|
||||||
|
case DEBUG_VALUE_FLOAT80:
|
||||||
|
case DEBUG_VALUE_FLOAT128: { // Convert to double
|
||||||
|
DEBUG_VALUE doubleValue;
|
||||||
|
double d = 0.0;
|
||||||
|
if (ctl && SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue))) {
|
||||||
|
d = dv.F64;
|
||||||
|
} else {
|
||||||
|
qWarning("Unable to convert DEBUG_VALUE_FLOAT80/DEBUG_VALUE_FLOAT128 values");
|
||||||
|
}
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String(dv.Type == DEBUG_VALUE_FLOAT80 ? "80bit-float" : "128bit-float");
|
||||||
|
return QString::number(d);
|
||||||
|
}
|
||||||
|
case DEBUG_VALUE_VECTOR64: {
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("64bit-vector");
|
||||||
|
QString rc = QLatin1String("bytes: ");
|
||||||
|
rc += formatArrayHelper(dv.VI8, 8, integerBase);
|
||||||
|
rc += QLatin1String(" long: ");
|
||||||
|
rc += formatArrayHelper(dv.VI32, 2, integerBase);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
case DEBUG_VALUE_VECTOR128: {
|
||||||
|
if (qType)
|
||||||
|
*qType = QLatin1String("128bit-vector");
|
||||||
|
QString rc = QLatin1String("bytes: ");
|
||||||
|
rc += formatArrayHelper(dv.VI8, 16, integerBase);
|
||||||
|
rc += QLatin1String(" long long: ");
|
||||||
|
rc += formatArrayHelper(dv.VI64, 2, integerBase);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (qType)
|
||||||
|
*qType = QString::fromLatin1("Unknown type #%1:").arg(dv.Type);
|
||||||
|
return formatArrayHelper(dv.RawBytes, 24, integerBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value)
|
||||||
|
{
|
||||||
|
*value = 0;
|
||||||
|
switch (dv.Type) {
|
||||||
|
case DEBUG_VALUE_INT8:
|
||||||
|
*value = dv.I8;
|
||||||
|
return true;
|
||||||
|
case DEBUG_VALUE_INT16:
|
||||||
|
*value = static_cast<short>(dv.I16);
|
||||||
|
return true;
|
||||||
|
case DEBUG_VALUE_INT32:
|
||||||
|
*value = static_cast<long>(dv.I32);
|
||||||
|
return true;
|
||||||
|
case DEBUG_VALUE_INT64:
|
||||||
|
*value = static_cast<long long>(dv.I64);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
191
src/plugins/debugger/cdb/coreengine.h
Normal file
191
src/plugins/debugger/cdb/coreengine.h
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef COREENGINE_H
|
||||||
|
#define COREENGINE_H
|
||||||
|
|
||||||
|
#include "cdbcom.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
class DebugOutputBase;
|
||||||
|
class DebugEventCallbackBase;
|
||||||
|
|
||||||
|
// helper struct to pass interfaces around
|
||||||
|
struct ComInterfaces
|
||||||
|
{
|
||||||
|
ComInterfaces();
|
||||||
|
|
||||||
|
CIDebugClient* debugClient;
|
||||||
|
CIDebugControl* debugControl;
|
||||||
|
CIDebugSystemObjects* debugSystemObjects;
|
||||||
|
CIDebugSymbols* debugSymbols;
|
||||||
|
CIDebugRegisters* debugRegisters;
|
||||||
|
CIDebugDataSpaces* debugDataSpaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CoreEngine : public QObject
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(CoreEngine)
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum ExpressionSyntax { AssemblerExpressionSyntax, CppExpressionSyntax };
|
||||||
|
enum CodeLevel { CodeLevelAssembly, CodeLevelSource };
|
||||||
|
|
||||||
|
typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr;
|
||||||
|
typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr;
|
||||||
|
|
||||||
|
explicit CoreEngine(QObject *parent = 0);
|
||||||
|
virtual ~CoreEngine();
|
||||||
|
|
||||||
|
bool init(const QString &dllEnginePath, QString *errorMessage);
|
||||||
|
// code level/output
|
||||||
|
|
||||||
|
inline const ComInterfaces &interfaces() const { return m_cif; }
|
||||||
|
|
||||||
|
// Set handlers
|
||||||
|
DebugOutputBasePtr setDebugOutput(const DebugOutputBasePtr &);
|
||||||
|
DebugEventCallbackBasePtr setDebugEventCallback(const DebugEventCallbackBasePtr &);
|
||||||
|
|
||||||
|
// Start functions
|
||||||
|
bool startDebuggerWithExecutable(const QString &workingDirectory,
|
||||||
|
const QString &filename,
|
||||||
|
const QStringList &args,
|
||||||
|
const QStringList &env,
|
||||||
|
bool verboseSymbolLoading,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
|
bool startAttachDebugger(qint64 pid, bool suppressInitialBreakPoint,
|
||||||
|
QString *errorMessage);
|
||||||
|
|
||||||
|
ULONG executionStatus() const;
|
||||||
|
bool setExecutionStatus(ULONG ex, QString *errorMessage);
|
||||||
|
|
||||||
|
// break & interrupt
|
||||||
|
bool debugBreakProcess(HANDLE hProcess, QString *errorMessage);
|
||||||
|
// Currently does not interrupt debuggee
|
||||||
|
bool setInterrupt(QString *errorMessage);
|
||||||
|
|
||||||
|
// Helpers for terminating the debuggees and ending the session
|
||||||
|
bool detachCurrentProcess(QString *appendableErrorMessage);
|
||||||
|
bool terminateCurrentProcess(QString *appendableErrorMessage);
|
||||||
|
bool terminateProcesses(QString *appendableErrorMessage);
|
||||||
|
bool endSession(QString *appendableErrorMessage);
|
||||||
|
|
||||||
|
// Watch timer: Listen for debug events and emit watchTimerDebugEvent() should one
|
||||||
|
// occur.
|
||||||
|
void startWatchTimer();
|
||||||
|
void killWatchTimer();
|
||||||
|
inline bool isWatchTimerRunning() const { return m_watchTimer != -1; }
|
||||||
|
// Synchronous wait
|
||||||
|
HRESULT waitForEvent(int timeOutMS);
|
||||||
|
|
||||||
|
// Commands and expressions
|
||||||
|
bool executeDebuggerCommand(const QString &command, QString *errorMessage);
|
||||||
|
|
||||||
|
bool evaluateExpression(const QString &expression,
|
||||||
|
DEBUG_VALUE *debugValue,
|
||||||
|
QString *errorMessage);
|
||||||
|
bool evaluateExpression(const QString &expression, QString *value,
|
||||||
|
QString *type /* =0 */, QString *errorMessage);
|
||||||
|
|
||||||
|
// Path getters/setters
|
||||||
|
QStringList sourcePaths() const;
|
||||||
|
bool setSourcePaths(const QStringList &s, QString *errorMessage);
|
||||||
|
QStringList symbolPaths() const;
|
||||||
|
bool setSymbolPaths(const QStringList &s, QString *errorMessage);
|
||||||
|
|
||||||
|
// Options
|
||||||
|
ExpressionSyntax expressionSyntax() const;
|
||||||
|
ExpressionSyntax setExpressionSyntax(ExpressionSyntax es);
|
||||||
|
|
||||||
|
CodeLevel codeLevel() const;
|
||||||
|
CodeLevel setCodeLevel(CodeLevel);
|
||||||
|
|
||||||
|
QString dbengDLL() const { return m_dbengDLL; }
|
||||||
|
|
||||||
|
// Debuggee memory conveniences
|
||||||
|
bool allocDebuggeeMemory(int size, ULONG64 *address, QString *errorMessage);
|
||||||
|
bool createDebuggeeAscIIString(const QString &s, ULONG64 *address, QString *errorMessage);
|
||||||
|
bool writeToDebuggee(const QByteArray &buffer, quint64 address, QString *errorMessage);
|
||||||
|
// Write to debuggee memory in chunks
|
||||||
|
bool dissassemble(ULONG64 offset, unsigned long beforeLines, unsigned long afterLines,
|
||||||
|
QString *target, QString *errorMessage);
|
||||||
|
|
||||||
|
static bool autoDetectPath(QString *outPath,
|
||||||
|
QStringList *checkedDirectories = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void watchTimerDebugEvent();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void timerEvent(QTimerEvent* te);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComInterfaces m_cif;
|
||||||
|
DebugOutputBasePtr m_debugOutput;
|
||||||
|
DebugEventCallbackBasePtr m_debugEventCallback;
|
||||||
|
QString m_dbengDLL;
|
||||||
|
QString m_baseImagePath;
|
||||||
|
int m_watchTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility messages
|
||||||
|
QString msgDebugEngineComResult(HRESULT hr);
|
||||||
|
QString msgComFailed(const char *func, HRESULT hr);
|
||||||
|
QString msgDebuggerCommandFailed(const QString &command, HRESULT hr);
|
||||||
|
const char *msgExecutionStatusString(ULONG executionStatus);
|
||||||
|
|
||||||
|
// A class that sets an expression syntax on the debug control while in scope.
|
||||||
|
// Can be nested as it checks for the old value.
|
||||||
|
class SyntaxSetter {
|
||||||
|
Q_DISABLE_COPY(SyntaxSetter)
|
||||||
|
public:
|
||||||
|
explicit inline SyntaxSetter(CoreEngine *engine, CoreEngine::ExpressionSyntax es) :
|
||||||
|
m_oldSyntax(engine->setExpressionSyntax(es)),
|
||||||
|
m_engine(engine) {}
|
||||||
|
inline ~SyntaxSetter() { m_engine->setExpressionSyntax(m_oldSyntax); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CoreEngine::ExpressionSyntax m_oldSyntax;
|
||||||
|
CoreEngine *m_engine;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helpers to convert DEBUG_VALUE structs. The optional control is required to
|
||||||
|
// convert large floating values.
|
||||||
|
QString debugValueToString(const DEBUG_VALUE &dv, QString *qType =0, int integerBase = 10, CIDebugControl *ctl = 0);
|
||||||
|
bool debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value);
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
|
|
||||||
|
#endif // COREENGINE_H
|
||||||
219
src/plugins/debugger/cdb/debugeventcallbackbase.cpp
Normal file
219
src/plugins/debugger/cdb/debugeventcallbackbase.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "debugeventcallbackbase.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
// DebugEventCallbackBase
|
||||||
|
DebugEventCallbackBase::DebugEventCallbackBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugEventCallbackBase::~DebugEventCallbackBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::QueryInterface(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface)
|
||||||
|
{
|
||||||
|
*Interface = NULL;
|
||||||
|
|
||||||
|
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||||
|
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) {
|
||||||
|
*Interface = (IDebugOutputCallbacks *)this;
|
||||||
|
AddRef();
|
||||||
|
return S_OK;
|
||||||
|
} else {
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) DebugEventCallbackBase::AddRef(THIS)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) DebugEventCallbackBase::Release(THIS)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::Exception(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64,
|
||||||
|
__in ULONG /* FirstChance */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::CreateThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* Handle */,
|
||||||
|
__in ULONG64 /* DataOffset */,
|
||||||
|
__in ULONG64 /* StartOffset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::ExitThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* ExitCode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::CreateProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* ImageFileHandle */,
|
||||||
|
__in ULONG64 /* Handle */,
|
||||||
|
__in ULONG64 /* BaseOffset */,
|
||||||
|
__in ULONG /* ModuleSize */,
|
||||||
|
__in_opt PCWSTR /* ModuleName */,
|
||||||
|
__in_opt PCWSTR /* ImageName */,
|
||||||
|
__in ULONG /* CheckSum */,
|
||||||
|
__in ULONG /* TimeDateStamp */,
|
||||||
|
__in ULONG64 /* InitialThreadHandle */,
|
||||||
|
__in ULONG64 /* ThreadDataOffset */,
|
||||||
|
__in ULONG64 /* StartOffset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::ExitProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* ExitCode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::LoadModule(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* ImageFileHandle */,
|
||||||
|
__in ULONG64 /* BaseOffset */,
|
||||||
|
__in ULONG /* ModuleSize */,
|
||||||
|
__in_opt PCWSTR /* ModuleName */,
|
||||||
|
__in_opt PCWSTR /* ImageName */,
|
||||||
|
__in ULONG /* CheckSum */,
|
||||||
|
__in ULONG /* TimeDateStamp */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::UnloadModule(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCWSTR /* ImageBaseName */,
|
||||||
|
__in ULONG64 /* BaseOffset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::SystemError(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* Error */,
|
||||||
|
__in ULONG /* Level */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::SessionStatus(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* Status */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::ChangeDebuggeeState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* Flags */,
|
||||||
|
__in ULONG64 /* Argument */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::ChangeEngineState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* Flags */,
|
||||||
|
__in ULONG64 /* Argument */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallbackBase::ChangeSymbolState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* Flags */,
|
||||||
|
__in ULONG64 /* Argument */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDebugEventCallbacksWide *DebugEventCallbackBase::getEventCallback(CIDebugClient *clnt)
|
||||||
|
{
|
||||||
|
IDebugEventCallbacksWide *rc = 0;
|
||||||
|
if (SUCCEEDED(clnt->GetEventCallbacksWide(&rc)))
|
||||||
|
return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventCallbackRedirector::EventCallbackRedirector(CoreEngine *engine,
|
||||||
|
const DebugEventCallbackBasePtr &cb) :
|
||||||
|
m_engine(engine),
|
||||||
|
m_oldCallback(engine->setDebugEventCallback(cb))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
EventCallbackRedirector::~EventCallbackRedirector()
|
||||||
|
{
|
||||||
|
m_engine->setDebugEventCallback(m_oldCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
174
src/plugins/debugger/cdb/debugeventcallbackbase.h
Normal file
174
src/plugins/debugger/cdb/debugeventcallbackbase.h
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DEBUGEVENTCALLBACKBASE_H
|
||||||
|
#define DEBUGEVENTCALLBACKBASE_H
|
||||||
|
|
||||||
|
#include "cdbcom.h"
|
||||||
|
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
class CoreEngine;
|
||||||
|
|
||||||
|
// Base class for event callbacks that takes care
|
||||||
|
// Active X magic. Provides base implementations with
|
||||||
|
// the exception of GetInterestMask
|
||||||
|
class DebugEventCallbackBase : public IDebugEventCallbacksWide
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
DebugEventCallbackBase();
|
||||||
|
public:
|
||||||
|
virtual ~DebugEventCallbackBase();
|
||||||
|
// IUnknown.
|
||||||
|
STDMETHOD(QueryInterface)(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, AddRef)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, Release)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
|
||||||
|
// IDebugEventCallbacks.
|
||||||
|
|
||||||
|
STDMETHOD(Breakpoint)(
|
||||||
|
THIS_
|
||||||
|
__in PDEBUG_BREAKPOINT2 Bp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Exception)(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG FirstChance
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 DataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
STDMETHOD(ExitThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCWSTR ModuleName,
|
||||||
|
__in_opt PCWSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp,
|
||||||
|
__in ULONG64 InitialThreadHandle,
|
||||||
|
__in ULONG64 ThreadDataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ExitProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(LoadModule)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCWSTR ModuleName,
|
||||||
|
__in_opt PCWSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(UnloadModule)(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCWSTR ImageBaseName,
|
||||||
|
__in ULONG64 BaseOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(SystemError)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Error,
|
||||||
|
__in ULONG Level
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(SessionStatus)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Status
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeDebuggeeState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeEngineState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeSymbolState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility class to temporarily redirect events to another handler
|
||||||
|
// as long as in scope
|
||||||
|
class EventCallbackRedirector {
|
||||||
|
Q_DISABLE_COPY(EventCallbackRedirector)
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<DebugEventCallbackBase> DebugEventCallbackBasePtr;
|
||||||
|
|
||||||
|
explicit EventCallbackRedirector(CoreEngine *engine, const DebugEventCallbackBasePtr &cb);
|
||||||
|
~EventCallbackRedirector();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CoreEngine *m_engine;
|
||||||
|
const DebugEventCallbackBasePtr m_oldCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
|
|
||||||
|
#endif // DEBUGEVENTCALLBACKBASE_H
|
||||||
134
src/plugins/debugger/cdb/debugoutputbase.cpp
Normal file
134
src/plugins/debugger/cdb/debugoutputbase.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "debugoutputbase.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
DebugOutputBase::DebugOutputBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugOutputBase::~DebugOutputBase() // must be present to avoid exit crashes
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugOutputBase::QueryInterface(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*Interface = NULL;
|
||||||
|
|
||||||
|
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
||||||
|
IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide)))
|
||||||
|
{
|
||||||
|
*Interface = (IDebugOutputCallbacksWide*)this;
|
||||||
|
AddRef();
|
||||||
|
return S_OK;
|
||||||
|
} else {
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) DebugOutputBase::AddRef(THIS)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP_(ULONG) DebugOutputBase::Release(THIS)
|
||||||
|
{
|
||||||
|
// This class is designed to be static so
|
||||||
|
// there's no true refcount.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugOutputBase::Output(
|
||||||
|
THIS_
|
||||||
|
IN ULONG mask,
|
||||||
|
IN PCWSTR text
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const QString msg = QString::fromUtf16(reinterpret_cast<const ushort *>(text));
|
||||||
|
output(mask, msg);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDebugOutputCallbacksWide *DebugOutputBase::getOutputCallback(CIDebugClient *client)
|
||||||
|
{
|
||||||
|
IDebugOutputCallbacksWide *rc;
|
||||||
|
if (FAILED(client->GetOutputCallbacksWide(&rc)))
|
||||||
|
return 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *DebugOutputBase::maskDescription(ULONG m)
|
||||||
|
{
|
||||||
|
switch (m) {
|
||||||
|
case DEBUG_OUTPUT_NORMAL:
|
||||||
|
break;
|
||||||
|
case DEBUG_OUTPUT_ERROR:
|
||||||
|
return "error";
|
||||||
|
case DEBUG_OUTPUT_WARNING:
|
||||||
|
return "warn";
|
||||||
|
case DEBUG_OUTPUT_VERBOSE:
|
||||||
|
return "verbose";
|
||||||
|
case DEBUG_OUTPUT_PROMPT_REGISTERS:
|
||||||
|
return "register";
|
||||||
|
case DEBUG_OUTPUT_EXTENSION_WARNING:
|
||||||
|
return "extwarn";
|
||||||
|
case DEBUG_OUTPUT_DEBUGGEE:
|
||||||
|
return "target";
|
||||||
|
case DEBUG_OUTPUT_DEBUGGEE_PROMPT:
|
||||||
|
return "input";
|
||||||
|
case DEBUG_OUTPUT_SYMBOLS:
|
||||||
|
return "symbol";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "misc";
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputRedirector::OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o) :
|
||||||
|
m_engine(engine),
|
||||||
|
m_oldOutput(engine->setDebugOutput(o))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputRedirector::~OutputRedirector()
|
||||||
|
{
|
||||||
|
m_engine->setDebugOutput(m_oldOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
111
src/plugins/debugger/cdb/debugoutputbase.h
Normal file
111
src/plugins/debugger/cdb/debugoutputbase.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DEBUGOUTPUTBASE_H
|
||||||
|
#define DEBUGOUTPUTBASE_H
|
||||||
|
|
||||||
|
#include "cdbcom.h"
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
|
||||||
|
class CoreEngine;
|
||||||
|
|
||||||
|
// CdbDebugOutputBase is a base class for output handlers
|
||||||
|
// that takes care of the Active X magic and conversion to QString.
|
||||||
|
|
||||||
|
class DebugOutputBase : public IDebugOutputCallbacksWide
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~DebugOutputBase();
|
||||||
|
// IUnknown.
|
||||||
|
STDMETHOD(QueryInterface)(
|
||||||
|
THIS_
|
||||||
|
IN REFIID InterfaceId,
|
||||||
|
OUT PVOID* Interface
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, AddRef)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
STDMETHOD_(ULONG, Release)(
|
||||||
|
THIS
|
||||||
|
);
|
||||||
|
|
||||||
|
// IDebugOutputCallbacks.
|
||||||
|
STDMETHOD(Output)(
|
||||||
|
THIS_
|
||||||
|
IN ULONG mask,
|
||||||
|
IN PCWSTR text
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helpers to retrieve the output callbacks IF
|
||||||
|
static IDebugOutputCallbacksWide *getOutputCallback(CIDebugClient *client);
|
||||||
|
static const char *maskDescription(ULONG m);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DebugOutputBase();
|
||||||
|
virtual void output(ULONG mask, const QString &message) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output handler that adds lines to a string (to be
|
||||||
|
// used for cases in which linebreaks occur in-between calls
|
||||||
|
// to output).
|
||||||
|
class StringOutputHandler : public DebugOutputBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StringOutputHandler() {}
|
||||||
|
QString result() const { return m_result; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void output(ULONG, const QString &message) { m_result += message; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility class to temporarily redirect output to another handler
|
||||||
|
// as long as in scope
|
||||||
|
class OutputRedirector {
|
||||||
|
Q_DISABLE_COPY(OutputRedirector)
|
||||||
|
public:
|
||||||
|
typedef QSharedPointer<DebugOutputBase> DebugOutputBasePtr;
|
||||||
|
|
||||||
|
explicit OutputRedirector(CoreEngine *engine, const DebugOutputBasePtr &o);
|
||||||
|
~OutputRedirector();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CoreEngine *m_engine;
|
||||||
|
const DebugOutputBasePtr m_oldOutput;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CdbCore
|
||||||
|
|
||||||
|
#endif // DEBUGOUTPUTBASE_H
|
||||||
@@ -85,7 +85,7 @@ class IDebuggerEngine;
|
|||||||
class GdbEngine;
|
class GdbEngine;
|
||||||
class ScriptEngine;
|
class ScriptEngine;
|
||||||
class CdbDebugEngine;
|
class CdbDebugEngine;
|
||||||
struct CdbDebugEnginePrivate;
|
class CdbDebugEnginePrivate;
|
||||||
struct DebuggerManagerActions;
|
struct DebuggerManagerActions;
|
||||||
class DebuggerPlugin;
|
class DebuggerPlugin;
|
||||||
class CdbDebugEventCallback;
|
class CdbDebugEventCallback;
|
||||||
@@ -94,7 +94,7 @@ class CdbDumperInitThread;
|
|||||||
class CdbExceptionLoggerEventCallback;
|
class CdbExceptionLoggerEventCallback;
|
||||||
class GdbEngine;
|
class GdbEngine;
|
||||||
class CdbDebugEngine;
|
class CdbDebugEngine;
|
||||||
struct CdbDebugEnginePrivate;
|
class CdbDebugEnginePrivate;
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|
||||||
class DEBUGGER_EXPORT DebuggerStartParameters
|
class DEBUGGER_EXPORT DebuggerStartParameters
|
||||||
@@ -169,7 +169,7 @@ public:
|
|||||||
friend class Internal::GdbEngine;
|
friend class Internal::GdbEngine;
|
||||||
friend class Internal::ScriptEngine;
|
friend class Internal::ScriptEngine;
|
||||||
friend class Internal::CdbDebugEngine;
|
friend class Internal::CdbDebugEngine;
|
||||||
friend struct Internal::CdbDebugEnginePrivate;
|
friend class Internal::CdbDebugEnginePrivate;
|
||||||
|
|
||||||
DebuggerState state() const;
|
DebuggerState state() const;
|
||||||
QList<Core::IOptionsPage*> initializeEngines(unsigned enabledTypeFlags);
|
QList<Core::IOptionsPage*> initializeEngines(unsigned enabledTypeFlags);
|
||||||
|
|||||||
2
tests/manual/ccdb/README
Normal file
2
tests/manual/ccdb/README
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This directory contains a command line tool to the Debugging Tools for
|
||||||
|
Windows for testing/scripting purposes.
|
||||||
29
tests/manual/ccdb/ccdb.pro
Normal file
29
tests/manual/ccdb/ccdb.pro
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -------------------------------------------------
|
||||||
|
# Project created by QtCreator 2010-01-22T10:11:10
|
||||||
|
# -------------------------------------------------
|
||||||
|
QT += core
|
||||||
|
TARGET = ccdb
|
||||||
|
CONFIG += console
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
# -- Add CDB core engine
|
||||||
|
CDB_CORE = ../../../src/plugins/debugger/cdb
|
||||||
|
include($$CDB_CORE/cdbcore.pri)
|
||||||
|
INCLUDEPATH *= $$CDB_CORE
|
||||||
|
|
||||||
|
# -- Add creator 'utils' lib
|
||||||
|
CREATOR_LIB_LIB = ../../../lib/qtcreator
|
||||||
|
LIBS *= -L$$CREATOR_LIB_LIB
|
||||||
|
LIBS *= -l$$qtLibraryTarget(Utilsd)
|
||||||
|
CREATOR_LIB_SRC = ../../../src/libs
|
||||||
|
INCLUDEPATH *= $$CREATOR_LIB_SRC
|
||||||
|
|
||||||
|
# -- App sources
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
cdbapplication.cpp \
|
||||||
|
debugeventcallback.cpp \
|
||||||
|
cdbpromptthread.cpp
|
||||||
|
HEADERS += cdbapplication.h \
|
||||||
|
debugeventcallback.h \
|
||||||
|
cdbpromptthread.h
|
||||||
213
tests/manual/ccdb/cdbapplication.cpp
Normal file
213
tests/manual/ccdb/cdbapplication.cpp
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "cdbapplication.h"
|
||||||
|
#include "coreengine.h"
|
||||||
|
#include "cdbdebugoutput.h"
|
||||||
|
#include "cdbpromptthread.h"
|
||||||
|
#include "debugeventcallback.h"
|
||||||
|
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
const char usage[] =
|
||||||
|
"CDB command line test tool\n\n"
|
||||||
|
"ccdb <Options>\n"
|
||||||
|
"Options: -p engine path\n";
|
||||||
|
|
||||||
|
class PrintfOutputHandler : public CdbCore::DebugOutputBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrintfOutputHandler() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void output(ULONG mask, const QString &message)
|
||||||
|
{ std::printf("%10s: %s\n", maskDescription(mask), qPrintable(message)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- CdbApplication
|
||||||
|
CdbApplication::CdbApplication(int argc, char *argv[]) :
|
||||||
|
QCoreApplication(argc, argv),
|
||||||
|
m_engine(new CdbCore::CoreEngine),
|
||||||
|
m_promptThread(0),
|
||||||
|
m_processHandle(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbApplication::~CdbApplication()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CdbApplication::InitResult CdbApplication::init()
|
||||||
|
{
|
||||||
|
if (!parseOptions()) {
|
||||||
|
printf(usage);
|
||||||
|
return InitUsageShown;
|
||||||
|
}
|
||||||
|
QString errorMessage;
|
||||||
|
std::printf("Initializing engine %s...\n", qPrintable(m_engineDll));
|
||||||
|
if (!m_engine->init(m_engineDll, &errorMessage)) {
|
||||||
|
std::fprintf(stderr, "Failed: %s\n", qPrintable(errorMessage));
|
||||||
|
return InitFailed;
|
||||||
|
}
|
||||||
|
m_engine->setDebugOutput(CdbCore::CoreEngine::DebugOutputBasePtr(new PrintfOutputHandler));
|
||||||
|
DebugEventCallback *evt = new DebugEventCallback;
|
||||||
|
connect(evt, SIGNAL(processAttached(void*)), this, SLOT(processAttached(void*)));
|
||||||
|
m_engine->setDebugEventCallback(CdbCore::CoreEngine::DebugEventCallbackBasePtr(evt));
|
||||||
|
m_engine->setExpressionSyntax(CdbCore::CoreEngine::CppExpressionSyntax);
|
||||||
|
m_engine->setCodeLevel(CdbCore::CoreEngine::CodeLevelSource);
|
||||||
|
connect(m_engine.data(), SIGNAL(watchTimerDebugEvent()), this, SLOT(debugEvent()));
|
||||||
|
std::printf("Succeded.\n");
|
||||||
|
// Prompt
|
||||||
|
m_promptThread = new CdbPromptThread(this);
|
||||||
|
connect(m_promptThread, SIGNAL(finished()), this, SLOT(promptThreadTerminated()));
|
||||||
|
connect(m_promptThread, SIGNAL(asyncCommand(int,QString)),
|
||||||
|
this, SLOT(asyncCommand(int,QString)), Qt::QueuedConnection);
|
||||||
|
connect(m_promptThread, SIGNAL(syncCommand(int,QString)),
|
||||||
|
this, SLOT(syncCommand(int,QString)), Qt::BlockingQueuedConnection);
|
||||||
|
connect(m_promptThread, SIGNAL(executionCommand(int,QString)),
|
||||||
|
this, SLOT(executionCommand(int,QString)), Qt::BlockingQueuedConnection);
|
||||||
|
|
||||||
|
m_promptThread->start();
|
||||||
|
return InitOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::promptThreadTerminated()
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
m_engine->endSession(&errorMessage);
|
||||||
|
std::printf("Terminating.\n");
|
||||||
|
m_promptThread->wait();
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbApplication::parseOptions()
|
||||||
|
{
|
||||||
|
const QStringList args = QCoreApplication::arguments();
|
||||||
|
const QStringList::const_iterator cend = args.constEnd();
|
||||||
|
QStringList::const_iterator it = args.constBegin();
|
||||||
|
for (++it; it != cend ; ++it) {
|
||||||
|
const QString &a = *it;
|
||||||
|
if (a == QLatin1String("-p")) {
|
||||||
|
++it;
|
||||||
|
if (it == cend) {
|
||||||
|
std::fprintf(stderr, "Option -p is missing an argument.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_engineDll = *it;
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "Invalid option %s\n", qPrintable(a));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::asyncCommand(int command, const QString &arg)
|
||||||
|
{
|
||||||
|
Q_UNUSED(arg)
|
||||||
|
QString errorMessage;
|
||||||
|
switch (command) {
|
||||||
|
case Async_Interrupt:
|
||||||
|
if (m_processHandle) {
|
||||||
|
if (m_engine->debugBreakProcess(m_processHandle, &errorMessage)) {
|
||||||
|
std::printf("Stopped\n");
|
||||||
|
} else {
|
||||||
|
std::printf("%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::syncCommand(int command, const QString &arg)
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
switch (command) {
|
||||||
|
case Sync_EvalExpression: {
|
||||||
|
QString value;
|
||||||
|
QString type;
|
||||||
|
if (m_engine->evaluateExpression(arg, &value, &type, &errorMessage)) {
|
||||||
|
std::printf("[%s] %s\n", qPrintable(type), qPrintable(value));
|
||||||
|
} else {
|
||||||
|
std::printf("%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Unknown:
|
||||||
|
if (!m_engine->executeDebuggerCommand(arg, &errorMessage))
|
||||||
|
std::printf("%s\n", qPrintable(errorMessage));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::executionCommand(int command, const QString &arg)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
QString errorMessage;
|
||||||
|
switch (command) {
|
||||||
|
case Execution_StartBinary: {
|
||||||
|
QStringList args = arg.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||||
|
if (args.isEmpty()) {
|
||||||
|
errorMessage = QLatin1String("Specify executable.");
|
||||||
|
} else {
|
||||||
|
std::printf("Starting\n");
|
||||||
|
const QString binary = args.front();
|
||||||
|
args.pop_front();
|
||||||
|
ok = m_engine->startDebuggerWithExecutable(QString(), binary, args,
|
||||||
|
QStringList(), false,
|
||||||
|
&errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Execution_Go:
|
||||||
|
std::printf("Go\n");
|
||||||
|
ok = m_engine->setExecutionStatus(DEBUG_STATUS_GO, &errorMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
m_engine->startWatchTimer();
|
||||||
|
} else {
|
||||||
|
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::debugEvent()
|
||||||
|
{
|
||||||
|
std::printf("Debug event\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbApplication::processAttached(void *handle)
|
||||||
|
{
|
||||||
|
std::printf("pe\n");
|
||||||
|
m_processHandle = handle;
|
||||||
|
}
|
||||||
71
tests/manual/ccdb/cdbapplication.h
Normal file
71
tests/manual/ccdb/cdbapplication.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CDBAPPLICATION_H
|
||||||
|
#define CDBAPPLICATION_H
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
namespace CdbCore {
|
||||||
|
class CoreEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CdbPromptThread;
|
||||||
|
|
||||||
|
class CdbApplication : public QCoreApplication
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(CdbApplication)
|
||||||
|
public:
|
||||||
|
enum InitResult { InitFailed, InitUsageShown, InitOk };
|
||||||
|
|
||||||
|
CdbApplication(int argc, char *argv[]);
|
||||||
|
~CdbApplication();
|
||||||
|
|
||||||
|
InitResult init();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void promptThreadTerminated();
|
||||||
|
void asyncCommand(int command, const QString &arg);
|
||||||
|
void syncCommand(int command, const QString &arg);
|
||||||
|
void executionCommand(int command, const QString &arg);
|
||||||
|
void debugEvent();
|
||||||
|
void processAttached(void *handle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseOptions();
|
||||||
|
|
||||||
|
QString m_engineDll;
|
||||||
|
QSharedPointer<CdbCore::CoreEngine> m_engine;
|
||||||
|
CdbPromptThread *m_promptThread;
|
||||||
|
void *m_processHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CDBAPPLICATION_H
|
||||||
135
tests/manual/ccdb/cdbpromptthread.cpp
Normal file
135
tests/manual/ccdb/cdbpromptthread.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "cdbpromptthread.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
static const char help[] =
|
||||||
|
"Special commands:\n\n"
|
||||||
|
"H Display Help\n"
|
||||||
|
"q Quit\n"
|
||||||
|
"E expression Evaluate C++expression\n"
|
||||||
|
"S binary args Start binary\n"
|
||||||
|
"I Interrupt\n"
|
||||||
|
"G Go\n"
|
||||||
|
"\nThe remaining commands are passed to CDB.\n";
|
||||||
|
|
||||||
|
CdbPromptThread::CdbPromptThread(QObject *parent) :
|
||||||
|
QThread(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CdbPromptThread::run()
|
||||||
|
{
|
||||||
|
enum { bufSize =1024 };
|
||||||
|
|
||||||
|
QString cmd;
|
||||||
|
char buf[bufSize];
|
||||||
|
std::putc('>', stdout);
|
||||||
|
while (true) {
|
||||||
|
if (std::fgets(buf, bufSize, stdin) == NULL)
|
||||||
|
break;
|
||||||
|
cmd += QString::fromLatin1(buf);
|
||||||
|
if (cmd.endsWith(QLatin1Char('\n'))) {
|
||||||
|
cmd.truncate(cmd.size() - 1);
|
||||||
|
if (!cmd.isEmpty() && !handleCommand(cmd.trimmed()))
|
||||||
|
break;
|
||||||
|
cmd.clear();
|
||||||
|
}
|
||||||
|
std::putc('>', stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the command
|
||||||
|
static Command evaluateCommand(const QString &cmdToken)
|
||||||
|
{
|
||||||
|
if (cmdToken.size() == 1) {
|
||||||
|
switch(cmdToken.at(0).toAscii()) {
|
||||||
|
case 'I':
|
||||||
|
return Async_Interrupt;
|
||||||
|
case 'E':
|
||||||
|
return Sync_EvalExpression;
|
||||||
|
case 'G':
|
||||||
|
return Execution_Go;
|
||||||
|
case 'S':
|
||||||
|
return Execution_StartBinary;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return UnknownCommand;
|
||||||
|
}
|
||||||
|
return UnknownCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chop off command and return argument list
|
||||||
|
static Command parseCommand(QString *s)
|
||||||
|
{
|
||||||
|
if (s->isEmpty())
|
||||||
|
return UnknownCommand;
|
||||||
|
int firstBlank = s->indexOf(QLatin1Char(' '));
|
||||||
|
// No further arguments
|
||||||
|
if (firstBlank == -1) {
|
||||||
|
const Command rc1 = evaluateCommand(*s);
|
||||||
|
if (rc1 != UnknownCommand) // pass through debugger cmds
|
||||||
|
s->clear();
|
||||||
|
return rc1;
|
||||||
|
}
|
||||||
|
// Chop
|
||||||
|
const Command rc = evaluateCommand(s->left(firstBlank));
|
||||||
|
if (rc != UnknownCommand) { // pass through debugger cmds)
|
||||||
|
int nextToken = firstBlank + 1;
|
||||||
|
for ( ; nextToken < s->size() && s->at(nextToken).isSpace(); nextToken++) ;
|
||||||
|
s->remove(0, nextToken);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CdbPromptThread::handleCommand(QString cmd)
|
||||||
|
{
|
||||||
|
if (cmd == QLatin1String("q"))
|
||||||
|
return false;
|
||||||
|
if (cmd == QLatin1String("H")) {
|
||||||
|
std::fputs(help, stdout);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const Command c = parseCommand(&cmd);
|
||||||
|
if (c & AsyncCommand) {
|
||||||
|
emit asyncCommand(c, cmd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (c & ExecutionCommand) {
|
||||||
|
emit executionCommand(c, cmd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Let Unknown default to sync exeute
|
||||||
|
emit syncCommand(c, cmd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
71
tests/manual/ccdb/cdbpromptthread.h
Normal file
71
tests/manual/ccdb/cdbpromptthread.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PROMPTTHREAD_H
|
||||||
|
#define PROMPTTHREAD_H
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
enum CommandTypeFlags {
|
||||||
|
// Interrupt or something.
|
||||||
|
AsyncCommand = 0x0010000,
|
||||||
|
// Synchronous execution before next prompt,
|
||||||
|
// eg eval expression. Connect with blocking slot.
|
||||||
|
SyncCommand = 0x0020000,
|
||||||
|
// Starts debuggee. Requires starting the debug event
|
||||||
|
// watch timer afterwards.
|
||||||
|
ExecutionCommand = 0x0040000
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Command {
|
||||||
|
UnknownCommand = 0,
|
||||||
|
Async_Interrupt = AsyncCommand|1,
|
||||||
|
Sync_EvalExpression = SyncCommand|1,
|
||||||
|
Execution_Go = ExecutionCommand|1,
|
||||||
|
Execution_StartBinary = ExecutionCommand|2
|
||||||
|
};
|
||||||
|
|
||||||
|
class CdbPromptThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit CdbPromptThread(QObject *parent = 0);
|
||||||
|
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void asyncCommand(int command, const QString &arg);
|
||||||
|
void syncCommand(int command, const QString &arg);
|
||||||
|
void executionCommand(int command, const QString &arg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool handleCommand(QString);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PROMPTTHREAD_H
|
||||||
156
tests/manual/ccdb/debugeventcallback.cpp
Normal file
156
tests/manual/ccdb/debugeventcallback.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "debugeventcallback.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
DebugEventCallback::DebugEventCallback()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||||
|
{
|
||||||
|
*mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS
|
||||||
|
| DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE
|
||||||
|
| DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD
|
||||||
|
| DEBUG_EVENT_BREAKPOINT
|
||||||
|
| DEBUG_EVENT_EXCEPTION;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT2)
|
||||||
|
{
|
||||||
|
printf("Breakpoint hit\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
STDMETHODIMP DebugEventCallback::Exception(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 exc,
|
||||||
|
__in ULONG firstChance
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Exception %ul occurred first-chance: %ul\n", exc->ExceptionCode, firstChance);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::CreateThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* Handle */,
|
||||||
|
__in ULONG64 /* DataOffset */,
|
||||||
|
__in ULONG64 /* StartOffset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Thread created\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::ExitThread(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* ExitCode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Thread quit\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::CreateProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* ImageFileHandle */,
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 /* Offset */,
|
||||||
|
__in ULONG /* ModuleSize */,
|
||||||
|
__in_opt PCWSTR /* ModuleName */,
|
||||||
|
__in_opt PCWSTR /* ImageName */,
|
||||||
|
__in ULONG /* CheckSum */,
|
||||||
|
__in ULONG /* TimeDateStamp */,
|
||||||
|
__in ULONG64 /* InitialThreadHandle */,
|
||||||
|
__in ULONG64 /* ThreadDataOffset */,
|
||||||
|
__in ULONG64 /* StartOffset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Process created %Ld\n", Handle);
|
||||||
|
emit processAttached(reinterpret_cast<void*>(Handle));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::ExitProcess(
|
||||||
|
THIS_
|
||||||
|
__in ULONG /* ExitCode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Process quit\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::LoadModule(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 /* ImageFileHandle */,
|
||||||
|
__in ULONG64 /* Offset */,
|
||||||
|
__in ULONG /* ModuleSize */,
|
||||||
|
__in_opt PCWSTR /* ModuleName */,
|
||||||
|
__in_opt PCWSTR /* ImageName */,
|
||||||
|
__in ULONG /* CheckSum */,
|
||||||
|
__in ULONG /* TimeDateStamp */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Module loaded\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::UnloadModule(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCWSTR /* ImageName */,
|
||||||
|
__in ULONG64 /* Offset */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Module unloaded\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::SystemError(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Error,
|
||||||
|
__in ULONG Level
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("System error %ul at %ul\n", Error, Level);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
STDMETHODIMP DebugEventCallback::ChangeDebuggeeState(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
)
|
||||||
|
{
|
||||||
|
printf("Debuggee state changed %ul %ul\n", Flags, Argument);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
125
tests/manual/ccdb/debugeventcallback.h
Normal file
125
tests/manual/ccdb/debugeventcallback.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DEBUGEVENTCALLBACK_H
|
||||||
|
#define DEBUGEVENTCALLBACK_H
|
||||||
|
|
||||||
|
#include "debugeventcallbackbase.h"
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class DebugEventCallback :
|
||||||
|
public QObject,
|
||||||
|
public CdbCore::DebugEventCallbackBase
|
||||||
|
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DebugEventCallback();
|
||||||
|
|
||||||
|
STDMETHOD(GetInterestMask)(
|
||||||
|
THIS_
|
||||||
|
__out PULONG mask
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Breakpoint)(
|
||||||
|
THIS_
|
||||||
|
__in PDEBUG_BREAKPOINT2 Bp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(Exception)(
|
||||||
|
THIS_
|
||||||
|
__in PEXCEPTION_RECORD64 Exception,
|
||||||
|
__in ULONG FirstChance
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 DataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
STDMETHOD(ExitThread)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(CreateProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 Handle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCWSTR ModuleName,
|
||||||
|
__in_opt PCWSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp,
|
||||||
|
__in ULONG64 InitialThreadHandle,
|
||||||
|
__in ULONG64 ThreadDataOffset,
|
||||||
|
__in ULONG64 StartOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ExitProcess)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG ExitCode
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(LoadModule)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG64 ImageFileHandle,
|
||||||
|
__in ULONG64 BaseOffset,
|
||||||
|
__in ULONG ModuleSize,
|
||||||
|
__in_opt PCWSTR ModuleName,
|
||||||
|
__in_opt PCWSTR ImageName,
|
||||||
|
__in ULONG CheckSum,
|
||||||
|
__in ULONG TimeDateStamp
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(UnloadModule)(
|
||||||
|
THIS_
|
||||||
|
__in_opt PCWSTR ImageBaseName,
|
||||||
|
__in ULONG64 BaseOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(SystemError)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Error,
|
||||||
|
__in ULONG Level
|
||||||
|
);
|
||||||
|
|
||||||
|
STDMETHOD(ChangeDebuggeeState)(
|
||||||
|
THIS_
|
||||||
|
__in ULONG Flags,
|
||||||
|
__in ULONG64 Argument
|
||||||
|
);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void processAttached(void *h);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DEBUGEVENTCALLBACK_H
|
||||||
49
tests/manual/ccdb/main.cpp
Normal file
49
tests/manual/ccdb/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "cdbapplication.h"
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
CdbApplication app(argc, argv);
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
switch (app.init()) {
|
||||||
|
case CdbApplication::InitOk:
|
||||||
|
rc = app.exec();
|
||||||
|
break;
|
||||||
|
case CdbApplication::InitFailed:
|
||||||
|
rc = 1;
|
||||||
|
break;
|
||||||
|
case CdbApplication::InitUsageShown:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user