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"
|
||||
# in case VS compilers are used.
|
||||
include(cdbcore.pri)
|
||||
|
||||
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
|
||||
INCLUDEPATH*=$$PWD
|
||||
DEPENDPATH*=$$PWD
|
||||
|
||||
CDB_LIBPATH=$$CDB_PATH/lib/$$CDB_PLATFORM
|
||||
!isEmpty(CDB_PATH) {
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/cdbcom.h \
|
||||
$$PWD/cdbdebugengine.h \
|
||||
$$PWD/cdbdebugengine_p.h \
|
||||
$$PWD/cdbdebugeventcallback.h \
|
||||
@@ -63,8 +39,4 @@ SOURCES += \
|
||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||
|
||||
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;
|
||||
HRESULT hr = ireg->GetNumberRegisters(&count);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("GetNumberRegisters", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetNumberRegisters", hr);
|
||||
return false;
|
||||
}
|
||||
if (!count)
|
||||
@@ -80,7 +80,7 @@ bool getRegisters(CIDebugControl *ctl,
|
||||
for (ULONG r = 0; r < count; r++) {
|
||||
hr = ireg->GetDescriptionWide(r, wszBuf, MAX_PATH - 1, 0, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("GetDescriptionWide", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetDescriptionWide", hr);
|
||||
return false;
|
||||
}
|
||||
Register reg;
|
||||
@@ -93,13 +93,13 @@ bool getRegisters(CIDebugControl *ctl,
|
||||
memset(valuesPtr, 0, count * sizeof(DEBUG_VALUE));
|
||||
hr = ireg->GetValues(count, 0, 0, valuesPtr);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("GetValues", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetValues", hr);
|
||||
return false;
|
||||
}
|
||||
if (base < 2)
|
||||
base = 10;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -211,8 +211,7 @@ void DisassemblerOutputParser::parse(const QStringList &l)
|
||||
}
|
||||
}
|
||||
|
||||
bool dissassemble(CIDebugClient *client,
|
||||
CIDebugControl *ctl,
|
||||
bool dissassemble(CdbCore::CoreEngine *engine,
|
||||
ULONG64 offset,
|
||||
unsigned long beforeLines,
|
||||
unsigned long afterLines,
|
||||
@@ -222,26 +221,11 @@ bool dissassemble(CIDebugClient *client,
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << offset;
|
||||
|
||||
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.
|
||||
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));
|
||||
QString lines;
|
||||
if (!engine->dissassemble(offset, beforeLines, afterLines, &lines, errorMessage))
|
||||
return false;
|
||||
}
|
||||
DisassemblerOutputParser parser(str, addressFieldWidth);
|
||||
parser.parse(stringHandler.result().split(QLatin1Char('\n')));
|
||||
parser.parse(lines.split(QLatin1Char('\n')));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
class CoreEngine;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
@@ -51,8 +55,7 @@ bool getRegisters(CIDebugControl *ctl,
|
||||
QString *errorMessage,
|
||||
int base = 10 /* 16 for hex, etc */);
|
||||
|
||||
bool dissassemble(CIDebugClient *client,
|
||||
CIDebugControl *ctl,
|
||||
bool dissassemble(CdbCore::CoreEngine *engine,
|
||||
ULONG64 offset,
|
||||
unsigned long beforeLines,
|
||||
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()));
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
|
||||
arg(expr, msgComFailed("SetOffsetExpressionWide", hr));
|
||||
arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
|
||||
return false;
|
||||
}
|
||||
// Pass Count is ignoreCount + 1
|
||||
@@ -192,7 +192,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
||||
*id = 0;
|
||||
HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotAddBreakPoint(msgComFailed("AddBreakpoint2", hr));
|
||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
|
||||
return false;
|
||||
}
|
||||
if (!ibp) {
|
||||
@@ -210,7 +210,7 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
||||
if (id) {
|
||||
hr = ibp->GetId(id);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotAddBreakPoint(msgComFailed("GetId", hr));
|
||||
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("GetId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
|
||||
const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
|
||||
arg(msgComFailed("GetOffsetExpressionWide", hr));
|
||||
arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
|
||||
return false;
|
||||
}
|
||||
// Pass Count is ignoreCount + 1
|
||||
@@ -398,7 +398,7 @@ bool CDBBreakPoint::getBreakPointCount(CIDebugControl* debugControl, ULONG *coun
|
||||
if (FAILED(hr)) {
|
||||
if (errorMessage)
|
||||
*errorMessage = QString::fromLatin1("Cannot determine breakpoint count: %1").
|
||||
arg(msgComFailed("GetNumberBreakpoints", hr));
|
||||
arg(CdbCore::msgComFailed("GetNumberBreakpoints", hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -416,7 +416,7 @@ bool CDBBreakPoint::getBreakPoints(CIDebugControl* debugControl, QList<CDBBreakP
|
||||
const HRESULT hr = debugControl->GetBreakpointByIndex2(b, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("Cannot retrieve breakpoint %1: %2").
|
||||
arg(b).arg(msgComFailed("GetBreakpointByIndex2", hr));
|
||||
arg(b).arg(CdbCore::msgComFailed("GetBreakpointByIndex2", hr));
|
||||
return false;
|
||||
}
|
||||
CDBBreakPoint bp;
|
||||
@@ -438,7 +438,7 @@ static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id,
|
||||
CIDebugBreakpoint *ibp = 0;
|
||||
const HRESULT hr = ctl->GetBreakpointById2(id, &ibp);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgNoBreakPointWithId(id, msgComFailed("GetBreakpointById2", hr));
|
||||
*errorMessage = msgNoBreakPointWithId(id, CdbCore::msgComFailed("GetBreakpointById2", hr));
|
||||
return 0;
|
||||
}
|
||||
if (!ibp) {
|
||||
@@ -458,7 +458,7 @@ static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString
|
||||
return false;
|
||||
const HRESULT hr = ctl->RemoveBreakpoint2(ibp);
|
||||
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 true;
|
||||
@@ -486,7 +486,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool
|
||||
ULONG flags;
|
||||
HRESULT hr = ibp->GetFlags(&flags);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("GetFlags", hr));
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("GetFlags", hr));
|
||||
return false;
|
||||
}
|
||||
const bool wasEnabled = (flags & DEBUG_BREAKPOINT_ENABLED);
|
||||
@@ -500,7 +500,7 @@ static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool
|
||||
}
|
||||
hr = ibp->SetFlags(flags);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, msgComFailed("SetFlags", hr));
|
||||
*errorMessage = msgCannotSetBreakPointEnabled(id, enabled, CdbCore::msgComFailed("SetFlags", hr));
|
||||
return false;
|
||||
}
|
||||
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 CdbDebugEventCallback;
|
||||
class CdbDebugOutput;
|
||||
struct CdbDebugEnginePrivate;
|
||||
class CdbDebugEnginePrivate;
|
||||
struct CdbOptions;
|
||||
|
||||
class CdbDebugEngine : public IDebuggerEngine
|
||||
@@ -104,9 +104,6 @@ public:
|
||||
public slots:
|
||||
void syncDebuggerPaths();
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent*);
|
||||
|
||||
private slots:
|
||||
void slotConsoleStubStarted();
|
||||
void slotConsoleStubError(const QString &msg);
|
||||
@@ -116,19 +113,16 @@ private slots:
|
||||
|
||||
private:
|
||||
void setState(DebuggerState state, const char *func, int line);
|
||||
bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
||||
bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage);
|
||||
void startWatchTimer();
|
||||
void killWatchTimer();
|
||||
inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
||||
inline bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage);
|
||||
void processTerminated(unsigned long exitCode);
|
||||
bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
|
||||
void evaluateWatcher(WatchData *wd);
|
||||
QString editorToolTip(const QString &exp, const QString &function);
|
||||
bool step(unsigned long executionStatus);
|
||||
|
||||
CdbDebugEnginePrivate *m_d;
|
||||
|
||||
friend struct CdbDebugEnginePrivate;
|
||||
friend class CdbDebugEnginePrivate;
|
||||
friend class CdbDebugEventCallback;
|
||||
friend class CdbDebugOutput;
|
||||
};
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
#ifndef DEBUGGER_CDBENGINEPRIVATE_H
|
||||
#define DEBUGGER_CDBENGINEPRIVATE_H
|
||||
|
||||
#include "cdbdebugeventcallback.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "coreengine.h"
|
||||
#include "cdboptions.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "stackhandler.h"
|
||||
@@ -50,52 +49,11 @@ class WatchHandler;
|
||||
class CdbStackFrameContext;
|
||||
class CdbStackTraceContext;
|
||||
|
||||
// Thin wrapper around the 'DBEng' debugger engine shared library
|
||||
// which is loaded at runtime.
|
||||
|
||||
class DebuggerEngineLibrary
|
||||
class CdbDebugEnginePrivate : public CdbCore::CoreEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
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;
|
||||
|
||||
enum HandleBreakEventMode { // Special modes for break event handler.
|
||||
@@ -107,15 +65,14 @@ struct CdbDebugEnginePrivate
|
||||
explicit CdbDebugEnginePrivate(DebuggerManager *parent,
|
||||
const QSharedPointer<CdbOptions> &options,
|
||||
CdbDebugEngine* engine);
|
||||
bool init(QString *errorMessage);
|
||||
~CdbDebugEnginePrivate();
|
||||
bool init(QString *errorMessage);
|
||||
|
||||
void checkVersion();
|
||||
void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
|
||||
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
|
||||
|
||||
bool isDebuggeeRunning() const { return m_watchTimer != -1; }
|
||||
void handleDebugEvent();
|
||||
bool isDebuggeeRunning() const { return isWatchTimerRunning(); }
|
||||
ULONG updateThreadList();
|
||||
bool setCDBThreadId(unsigned long threadId, QString *errorMessage);
|
||||
void updateStackTrace();
|
||||
@@ -143,17 +100,12 @@ struct CdbDebugEnginePrivate
|
||||
enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto };
|
||||
void endDebugging(EndDebuggingMode em = EndDebuggingAuto);
|
||||
|
||||
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
||||
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
|
||||
void updateCodeLevel();
|
||||
|
||||
QStringList sourcePaths() const;
|
||||
bool setSourcePaths(const QStringList &s, QString *errorMessage);
|
||||
|
||||
QStringList symbolPaths() const;
|
||||
bool setSymbolPaths(const QStringList &s, QString *errorMessage);
|
||||
|
||||
bool setCodeLevel();
|
||||
public slots:
|
||||
void handleDebugEvent();
|
||||
|
||||
public:
|
||||
const QSharedPointer<CdbOptions> m_options;
|
||||
HANDLE m_hDebuggeeProcess;
|
||||
HANDLE m_hDebuggeeThread;
|
||||
@@ -164,12 +116,7 @@ struct CdbDebugEnginePrivate
|
||||
bool m_ignoreInitialBreakPoint;
|
||||
HandleBreakEventMode m_breakEventMode;
|
||||
|
||||
int m_watchTimer;
|
||||
CdbComInterfaces m_cif;
|
||||
CdbDebugEventCallback m_debugEventCallBack;
|
||||
CdbDebugOutput m_debugOutputCallBack;
|
||||
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
QString m_baseImagePath;
|
||||
|
||||
CdbDebugEngine *m_engine;
|
||||
inline DebuggerManager *manager() const;
|
||||
@@ -181,19 +128,8 @@ struct CdbDebugEnginePrivate
|
||||
|
||||
DebuggerStartMode m_mode;
|
||||
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 { debugCDB = 0 };
|
||||
|
||||
@@ -40,174 +40,6 @@
|
||||
namespace Debugger {
|
||||
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(CdbDebugEngine* dbg) :
|
||||
@@ -420,18 +252,5 @@ STDMETHODIMP IgnoreDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
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 Debugger
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#ifndef DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||
#define DEBUGGER_CDBDEBUGEVENTCALLBACK_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
#include "debugeventcallbackbase.h"
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
@@ -41,122 +41,7 @@ namespace Internal {
|
||||
|
||||
class CdbDebugEngine;
|
||||
|
||||
// Base class for event callbacks that takes care
|
||||
// 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
|
||||
class CdbDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
explicit CdbDebugEventCallback(CdbDebugEngine* dbg);
|
||||
@@ -239,7 +124,7 @@ private:
|
||||
|
||||
// Event handler logs exceptions to the debugger window
|
||||
// and ignores the rest. To be used for running dumper calls.
|
||||
class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase
|
||||
class CdbExceptionLoggerEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
CdbExceptionLoggerEventCallback(int logChannel,
|
||||
@@ -270,7 +155,7 @@ private:
|
||||
};
|
||||
|
||||
// Event handler that ignores everything
|
||||
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
|
||||
class IgnoreDebugEventCallback : public CdbCore::DebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
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 Debugger
|
||||
|
||||
|
||||
@@ -37,62 +37,6 @@
|
||||
namespace Debugger {
|
||||
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
|
||||
|
||||
// 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 Debugger
|
||||
|
||||
@@ -30,49 +30,15 @@
|
||||
#ifndef DEBUGGER_CDBOUTPUT_H
|
||||
#define DEBUGGER_CDBOUTPUT_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
#include "debugoutputbase.h"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
namespace Debugger {
|
||||
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
|
||||
class CdbDebugOutput : public QObject, public CdbDebugOutputBase
|
||||
class CdbDebugOutput : public QObject, public CdbCore::DebugOutputBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -88,34 +54,6 @@ signals:
|
||||
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 Debugger
|
||||
|
||||
|
||||
@@ -94,59 +94,13 @@ namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// ------- 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
|
||||
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"
|
||||
// as dummy symbol. This is ok ATM since dumpers only
|
||||
// make sense for Qt apps.
|
||||
static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
||||
CdbComInterfaces *cif,
|
||||
CdbCore::CoreEngine *engine,
|
||||
unsigned long threadId,
|
||||
const QString &moduleName,
|
||||
QString *errorMessage)
|
||||
@@ -154,12 +108,13 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << moduleName;
|
||||
// Try to ignore the breakpoints, skip stray startup-complete trap exceptions
|
||||
CdbExceptionLoggerEventCallback exLogger(LogWarning, true, manager);
|
||||
EventCallbackRedirector eventRedir(cif->debugClient, &exLogger);
|
||||
QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, manager));
|
||||
CdbCore::EventCallbackRedirector eventRedir(engine, exLogger);
|
||||
Q_UNUSED(eventRedir)
|
||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||
// and copy name over.
|
||||
ULONG64 nameAddress;
|
||||
if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage))
|
||||
if (!engine->createDebuggeeAscIIString(moduleName, &nameAddress, errorMessage))
|
||||
return false;
|
||||
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
||||
// (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
|
||||
// reason, the symbol is present in QtGui as well without type information.
|
||||
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;
|
||||
|
||||
QString callCmd; {
|
||||
@@ -179,16 +134,16 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
|
||||
if (loadDebug)
|
||||
qDebug() << "Calling" << callCmd;
|
||||
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
|
||||
if (!engine->executeDebuggerCommand(callCmd, errorMessage))
|
||||
return false;
|
||||
// Execute current thread. This will hit a breakpoint.
|
||||
QString goCmd;
|
||||
QTextStream(&goCmd) << '~' << threadId << " g";
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, goCmd, errorMessage))
|
||||
if (!engine->executeDebuggerCommand(goCmd, errorMessage))
|
||||
return false;
|
||||
const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||
const HRESULT hr = engine->waitForEvent(waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -339,13 +294,13 @@ void CdbDumperInitThread ::run()
|
||||
// ------------------- CdbDumperHelper
|
||||
|
||||
CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager,
|
||||
CdbComInterfaces *cif) :
|
||||
CdbCore::CoreEngine *coreEngine) :
|
||||
m_tryInjectLoad(true),
|
||||
m_msgDisabled(QLatin1String("Dumpers are disabled")),
|
||||
m_msgNotInScope(QLatin1String("Data not in scope")),
|
||||
m_state(NotLoaded),
|
||||
m_manager(manager),
|
||||
m_cif(cif),
|
||||
m_coreEngine(coreEngine),
|
||||
m_inBufferAddress(0),
|
||||
m_inBufferSize(0),
|
||||
m_outBufferAddress(0),
|
||||
@@ -433,7 +388,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// Do we have Qt and are we already loaded by accident?
|
||||
QStringList modules;
|
||||
if (!getModuleNameList(m_cif->debugSymbols, &modules, errorMessage))
|
||||
if (!getModuleNameList(m_coreEngine->interfaces().debugSymbols, &modules, errorMessage))
|
||||
return CallLoadError;
|
||||
// Are we already loaded by some accident?
|
||||
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())
|
||||
return CallLoadNoQtApp;
|
||||
// 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 CallLoadOk;
|
||||
}
|
||||
@@ -457,7 +412,7 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
||||
// Get address
|
||||
HRESULT hr = sg->GetOffsetByNameWide(reinterpret_cast<PCWSTR>(name.utf16()), address);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetOffsetByNameWide", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetOffsetByNameWide", hr);
|
||||
return false;
|
||||
}
|
||||
// Get size. Even works for arrays
|
||||
@@ -466,12 +421,12 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
||||
ULONG type;
|
||||
hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetOffsetTypeId", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetOffsetTypeId", hr);
|
||||
return false;
|
||||
}
|
||||
hr = sg->GetTypeSize(moduleAddress, type, size);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetTypeSize", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetTypeSize", hr);
|
||||
return false;
|
||||
}
|
||||
} // size desired
|
||||
@@ -488,14 +443,14 @@ bool CdbDumperHelper::initResolveSymbols(QString *errorMessage)
|
||||
QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
|
||||
QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
|
||||
const QString dumperModuleName = QLatin1String(dumperModuleNameC);
|
||||
bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
||||
bool rc = resolveSymbol(m_coreEngine->interfaces().debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
|
||||
&& resolveSymbol(m_coreEngine->interfaces().debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
|
||||
if (!rc)
|
||||
return false;
|
||||
// Determine buffer addresses, sizes and alloc buffer
|
||||
rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
||||
&& getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
||||
rc = getSymbolAddress(m_coreEngine->interfaces().debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
|
||||
&& getSymbolAddress(m_coreEngine->interfaces().debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
|
||||
if (!rc)
|
||||
return false;
|
||||
m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
|
||||
@@ -532,41 +487,24 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
|
||||
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::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
|
||||
bool ignoreAccessViolation, QString *errorMessage)
|
||||
{
|
||||
*outDataPtr = 0;
|
||||
// Skip stray startup-complete trap exceptions.
|
||||
CdbExceptionLoggerEventCallback exLogger(LogWarning, true, m_manager);
|
||||
EventCallbackRedirector eventRedir(m_cif->debugClient, &exLogger);
|
||||
QSharedPointer<CdbExceptionLoggerEventCallback> exLogger(new CdbExceptionLoggerEventCallback(LogWarning, true, m_manager));
|
||||
CdbCore::EventCallbackRedirector eventRedir(m_coreEngine, exLogger);
|
||||
Q_UNUSED(eventRedir)
|
||||
// write input buffer
|
||||
if (!inBuffer.isEmpty()) {
|
||||
if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage))
|
||||
if (!m_coreEngine->writeToDebuggee(inBuffer, m_inBufferAddress, errorMessage))
|
||||
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
|
||||
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);
|
||||
}
|
||||
return CallSyntaxError;
|
||||
@@ -575,40 +513,40 @@ CdbDumperHelper::CallResult
|
||||
// Try to skip debuggee crash exceptions and dumper exceptions
|
||||
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
|
||||
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,
|
||||
// let the dumper handle it.
|
||||
QString goCmd = m_goCommand;
|
||||
if (i)
|
||||
goCmd = QLatin1Char('N');
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
|
||||
if (!m_coreEngine->executeDebuggerCommand(goCmd, errorMessage))
|
||||
return CallFailed;
|
||||
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||
HRESULT hr = m_coreEngine->waitForEvent(waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("WaitForEvent", hr);
|
||||
return CallFailed;
|
||||
}
|
||||
const int newExceptionCount = exLogger.exceptionCount();
|
||||
const int newExceptionCount = exLogger->exceptionCount();
|
||||
// no new exceptions? -> break
|
||||
if (oldExceptionCount == newExceptionCount)
|
||||
break;
|
||||
// If we are to ignore EXCEPTION_ACCESS_VIOLATION, check if anything
|
||||
// else occurred.
|
||||
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())
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exLogger.exceptionCount()) {
|
||||
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
|
||||
if (exLogger->exceptionCount()) {
|
||||
const QString exMsgs = exLogger->exceptionMessages().join(QString(QLatin1Char(',')));
|
||||
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
||||
return CallFailed;
|
||||
}
|
||||
// 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)) {
|
||||
*errorMessage = msgComFailed("ReadVirtual", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("ReadVirtual", hr);
|
||||
return CallFailed;
|
||||
}
|
||||
// see QDumper implementation
|
||||
@@ -742,7 +680,7 @@ CdbDumperHelper::DumpExecuteResult
|
||||
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
|
||||
const char *outputData;
|
||||
// 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) {
|
||||
*errorMessage = eb.errorString();
|
||||
return DumpExecuteCallFailed;
|
||||
@@ -808,11 +746,10 @@ bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QStri
|
||||
QString expression = QLatin1String("sizeof(");
|
||||
expression += typeName;
|
||||
expression += QLatin1Char(')');
|
||||
if (!CdbDebugEnginePrivate::evaluateExpression(m_cif->debugControl,
|
||||
expression, &sizeValue, errorMessage))
|
||||
if (!m_coreEngine->evaluateExpression(expression, &sizeValue, errorMessage))
|
||||
return false;
|
||||
qint64 size64;
|
||||
if (!CdbSymbolGroupContext::debugValueToInteger(sizeValue, &size64)) {
|
||||
if (!CdbCore::debugValueToInteger(sizeValue, &size64)) {
|
||||
*errorMessage = QLatin1String("Expression result is not an integer");
|
||||
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 Debugger
|
||||
|
||||
|
||||
@@ -36,11 +36,15 @@
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
namespace CdbCore {
|
||||
class CoreEngine;
|
||||
struct ComInterfaces;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
class DebuggerManager;
|
||||
|
||||
namespace Internal {
|
||||
struct CdbComInterfaces;
|
||||
class CdbDumperInitThread;
|
||||
|
||||
/* For code clarity, all the stuff related to custom dumpers goes here.
|
||||
@@ -82,7 +86,7 @@ public:
|
||||
};
|
||||
|
||||
explicit CdbDumperHelper(DebuggerManager *manager,
|
||||
CdbComInterfaces *cif);
|
||||
CdbCore::CoreEngine *coreEngine);
|
||||
~CdbDumperHelper();
|
||||
|
||||
State state() const { return m_state; }
|
||||
@@ -102,7 +106,7 @@ public:
|
||||
DumpResult dumpType(const WatchData &d, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
inline CdbComInterfaces *comInterfaces() const { return m_cif; }
|
||||
const CdbCore::ComInterfaces *comInterfaces() const;
|
||||
|
||||
enum { InvalidDumperCallThread = 0xFFFFFFFF };
|
||||
unsigned long dumperCallThread();
|
||||
@@ -131,14 +135,12 @@ private:
|
||||
const QtDumperHelper::TypeData& td, bool dumpChildren,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage);
|
||||
|
||||
const bool m_tryInjectLoad;
|
||||
const QString m_msgDisabled;
|
||||
const QString m_msgNotInScope;
|
||||
State m_state;
|
||||
DebuggerManager *m_manager;
|
||||
CdbComInterfaces *m_cif;
|
||||
CdbCore::CoreEngine *m_coreEngine;
|
||||
|
||||
QString m_library;
|
||||
QString m_dumpObjectSymbol;
|
||||
|
||||
@@ -122,7 +122,7 @@ bool ExceptionBlocker::getExceptionParameters(CIDebugControl *ctrl, ULONG exCode
|
||||
{
|
||||
const HRESULT ihr = ctrl->GetExceptionFilterParameters(1, &exCode, 0, result);
|
||||
if (FAILED(ihr)) {
|
||||
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
if (FAILED(ihr)) {
|
||||
*errorMessage = msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetExceptionFilterParameters", ihr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -43,7 +43,7 @@ static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *e
|
||||
ULONG loadedCount, unloadedCount;
|
||||
const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("GetNumberModules", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetNumberModules", hr);
|
||||
return false;
|
||||
}
|
||||
*count = loadedCount + unloadedCount;
|
||||
@@ -75,7 +75,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM
|
||||
HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
|
||||
// E_INVALIDARG indicates 'Partial results' according to docu
|
||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||
*errorMessage= msgComFailed("GetModuleParameters", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleParameters", hr);
|
||||
return false;
|
||||
}
|
||||
// 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);
|
||||
hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
|
||||
if (FAILED(hr) && hr != E_INVALIDARG) {
|
||||
*errorMessage= msgComFailed("GetModuleNameStringWide", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("GetModuleNameStringWide", hr);
|
||||
return false;
|
||||
}
|
||||
module.moduleName = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
|
||||
@@ -116,7 +116,7 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
|
||||
return true;
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("StartSymbolMatchWide", hr);
|
||||
*errorMessage= CdbCore::msgComFailed("StartSymbolMatchWide", hr);
|
||||
return false;
|
||||
}
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdboptions.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QDir>
|
||||
@@ -65,7 +66,7 @@ void CdbOptions::fromSettings(const QSettings *s)
|
||||
const QString enabledKey = keyRoot + QLatin1String(enabledKeyC);
|
||||
const bool firstTime = !s->contains(enabledKey);
|
||||
if (firstTime) {
|
||||
enabled = autoDetectPath(&path);
|
||||
enabled = CdbCore::CoreEngine::autoDetectPath(&path);
|
||||
return;
|
||||
}
|
||||
enabled = s->value(enabledKey, false).toBool();
|
||||
@@ -86,46 +87,6 @@ void CdbOptions::toSettings(QSettings *s) const
|
||||
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 rc = 0;
|
||||
|
||||
@@ -54,9 +54,6 @@ public:
|
||||
SymbolOptionsChanged = 0x4 };
|
||||
unsigned compare(const CdbOptions &s) const;
|
||||
|
||||
// Locate the debugging tools
|
||||
static bool autoDetectPath(QString *path, QStringList *checkedDirectories = 0);
|
||||
|
||||
bool enabled;
|
||||
QString path;
|
||||
QStringList symbolPaths;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "cdboptionspage.h"
|
||||
#include "cdboptions.h"
|
||||
#include "debuggerconstants.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -102,7 +103,7 @@ void CdbOptionsPageWidget::autoDetect()
|
||||
{
|
||||
QString path;
|
||||
QStringList checkedDirectories;
|
||||
const bool ok = CdbOptions::autoDetectPath(&path, &checkedDirectories);
|
||||
const bool ok = CdbCore::CoreEngine::autoDetectPath(&path, &checkedDirectories);
|
||||
m_ui.cdbPathGroupBox->setChecked(ok);
|
||||
if (ok) {
|
||||
m_ui.pathChooser->setPath(path);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "debuggeractions.h"
|
||||
#include "watchhandler.h"
|
||||
#include "coreengine.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstacktracecontext.h"
|
||||
#include "coreengine.h"
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbbreakpoint.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);
|
||||
if (FAILED(hr)) {
|
||||
delete ctx;
|
||||
*errorMessage = msgComFailed("GetStackTrace", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetStackTrace", hr);
|
||||
return 0;
|
||||
}
|
||||
if (!ctx->init(frameCount, errorMessage)) {
|
||||
@@ -189,20 +190,20 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *
|
||||
CIDebugSymbolGroup *sg = 0;
|
||||
HRESULT hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hr = m_cif->debugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("SetScope", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("SetScope", hr);
|
||||
sg->Release();
|
||||
return 0;
|
||||
}
|
||||
// refresh with current frame
|
||||
hr = m_cif->debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetScopeSymbolGroup", hr);
|
||||
sg->Release();
|
||||
return 0;
|
||||
}
|
||||
@@ -247,7 +248,7 @@ static inline QString msgGetThreadStateFailed(unsigned long threadId, const QStr
|
||||
|
||||
// Determine information about thread. This changes the
|
||||
// current thread to thread->id.
|
||||
static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
||||
static inline bool getStoppedThreadState(const CdbCore::ComInterfaces &cif,
|
||||
ThreadData *t,
|
||||
QString *errorMessage)
|
||||
{
|
||||
@@ -255,13 +256,13 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
||||
ULONG currentThread;
|
||||
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(¤tThread);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetCurrentThreadId", hr));
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
if (currentThread != t->id) {
|
||||
hr = cif.debugSystemObjects->SetCurrentThreadId(t->id);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("SetCurrentThreadId", hr));
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -271,7 +272,7 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
|
||||
DEBUG_STACK_FRAME frames[MaxFrames];
|
||||
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetStackTrace", hr));
|
||||
*errorMessage = msgGetThreadStateFailed(t->id, CdbCore::msgComFailed("GetStackTrace", hr));
|
||||
return false;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
||||
bool CdbStackTraceContext::getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool isStopped,
|
||||
QList<ThreadData> *threads,
|
||||
ULONG *currentThreadId,
|
||||
@@ -318,7 +319,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
||||
*currentThreadId = 0;
|
||||
HRESULT hr= cif.debugSystemObjects->GetNumberThreads(&threadCount);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetNumberThreads", hr));
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetNumberThreads", hr));
|
||||
return false;
|
||||
}
|
||||
// Get ids and index of current
|
||||
@@ -326,14 +327,14 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
||||
return true;
|
||||
hr = cif.debugSystemObjects->GetCurrentThreadId(currentThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetCurrentThreadId", hr));
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<ULONG> threadIds(threadCount);
|
||||
hr = cif.debugSystemObjects->GetThreadIdsByIndex(0, threadCount, &(*threadIds.begin()), 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(msgComFailed("GetThreadIdsByIndex", hr));
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("GetThreadIdsByIndex", hr));
|
||||
return false;
|
||||
}
|
||||
for (ULONG i = 0; i < threadCount; i++) {
|
||||
@@ -350,7 +351,7 @@ bool CdbStackTraceContext::getThreads(const CdbComInterfaces &cif,
|
||||
if (isStopped && threads->back().id != *currentThreadId) {
|
||||
hr = cif.debugSystemObjects->SetCurrentThreadId(*currentThreadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgGetThreadsFailed(msgComFailed("SetCurrentThreadId", hr));
|
||||
*errorMessage= msgGetThreadsFailed(CdbCore::msgComFailed("SetCurrentThreadId", hr));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,13 @@ QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace CdbCore {
|
||||
struct ComInterfaces;
|
||||
}
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
struct CdbComInterfaces;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbStackFrameContext;
|
||||
class CdbDumperHelper;
|
||||
@@ -93,7 +96,7 @@ public:
|
||||
|
||||
// Retrieve information about threads. When stopped, add
|
||||
// current stack frame.
|
||||
static bool getThreads(const CdbComInterfaces &cif,
|
||||
static bool getThreads(const CdbCore::ComInterfaces &cif,
|
||||
bool isStopped,
|
||||
QList<ThreadData> *threads,
|
||||
ULONG *currentThreadId,
|
||||
@@ -104,7 +107,7 @@ private:
|
||||
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
||||
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
CdbComInterfaces *m_cif;
|
||||
const CdbCore::ComInterfaces *m_cif;
|
||||
|
||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||
QVector <CdbStackFrameContext*> m_frameContexts;
|
||||
|
||||
@@ -175,7 +175,7 @@ bool CdbSymbolGroupContext::init(QString *errorMessage)
|
||||
ULONG count;
|
||||
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("GetNumberSymbols", hr);
|
||||
*errorMessage = CdbCore::msgComFailed("GetNumberSymbols", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,8 @@ bool CdbSymbolGroupContext::init(QString *errorMessage)
|
||||
|
||||
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
|
||||
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;
|
||||
}
|
||||
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);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("ExpandSymbol", hr));
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("ExpandSymbol", hr));
|
||||
return false;
|
||||
}
|
||||
// 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;
|
||||
hr = m_symbolGroup->GetNumberSymbols(&newSize);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetNumberSymbols", hr));
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetNumberSymbols", hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -373,7 +374,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in
|
||||
|
||||
hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters());
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgExpandFailed(prefix, index, msgComFailed("GetSymbolParameters", hr));
|
||||
*errorMessage = msgExpandFailed(prefix, index, CdbCore::msgComFailed("GetSymbolParameters", hr));
|
||||
return false;
|
||||
}
|
||||
// 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()));
|
||||
if (FAILED(hr)) {
|
||||
*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;
|
||||
}
|
||||
if (newValue)
|
||||
@@ -539,126 +540,6 @@ bool CdbSymbolGroupContext::assignValue(const QString &iname, const QString &val
|
||||
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:
|
||||
* 0: ok
|
||||
* 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(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
|
||||
enum DumperResult { DumperOk, DumperError, DumperNotHandled };
|
||||
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 ScriptEngine;
|
||||
class CdbDebugEngine;
|
||||
struct CdbDebugEnginePrivate;
|
||||
class CdbDebugEnginePrivate;
|
||||
struct DebuggerManagerActions;
|
||||
class DebuggerPlugin;
|
||||
class CdbDebugEventCallback;
|
||||
@@ -94,7 +94,7 @@ class CdbDumperInitThread;
|
||||
class CdbExceptionLoggerEventCallback;
|
||||
class GdbEngine;
|
||||
class CdbDebugEngine;
|
||||
struct CdbDebugEnginePrivate;
|
||||
class CdbDebugEnginePrivate;
|
||||
} // namespace Internal
|
||||
|
||||
class DEBUGGER_EXPORT DebuggerStartParameters
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
friend class Internal::GdbEngine;
|
||||
friend class Internal::ScriptEngine;
|
||||
friend class Internal::CdbDebugEngine;
|
||||
friend struct Internal::CdbDebugEnginePrivate;
|
||||
friend class Internal::CdbDebugEnginePrivate;
|
||||
|
||||
DebuggerState state() const;
|
||||
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