Debugger/CDB: Split engine for testing/scripting purposes.

This commit is contained in:
Friedemann Kleint
2010-01-22 17:15:33 +01:00
parent 4537e2be6c
commit d348e5099b
40 changed files with 2906 additions and 1498 deletions

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;

View 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

View File

@@ -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;
};

View File

@@ -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);
~CdbDebugEnginePrivate();
bool init(QString *errorMessage);
~CdbDebugEnginePrivate();
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 };

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
// 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

View File

@@ -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.
@@ -81,8 +85,8 @@ public:
Initialized, // List of types, etc. retrieved
};
explicit CdbDumperHelper(DebuggerManager *manager,
CdbComInterfaces *cif);
explicit CdbDumperHelper(DebuggerManager *manager,
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;

View File

@@ -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;

View File

@@ -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];

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -33,6 +33,7 @@
#include "cdbdumperhelper.h"
#include "debuggeractions.h"
#include "watchhandler.h"
#include "coreengine.h"
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>

View File

@@ -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(&currentThread);
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;
}
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);

View 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(&currentCodeLevel);
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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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
View File

@@ -0,0 +1,2 @@
This directory contains a command line tool to the Debugging Tools for
Windows for testing/scripting purposes.

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}