forked from qt-creator/qt-creator
Initial work on dumpers for CDB.
Add dumper calls, parser etc. Catch crashes in the dumper functions.
This commit is contained in:
@@ -60,6 +60,10 @@ int qtGhVersion = QT_VERSION;
|
||||
# include <QtGui/QImage>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -231,11 +235,17 @@ static QByteArray stripPointerType(QByteArray type)
|
||||
}
|
||||
|
||||
// This is used to abort evaluation of custom data dumpers in a "coordinated"
|
||||
// way. Abortion will happen anyway when we try to access a non-initialized
|
||||
// way. Abortion will happen at the latest when we try to access a non-initialized
|
||||
// non-trivial object, so there is no way to prevent this from occuring at all
|
||||
// conceptionally. Gdb will catch SIGSEGV and return to the calling frame.
|
||||
// This is just fine provided we only _read_ memory in the custom handlers
|
||||
// below.
|
||||
// conceptionally. Ideally, if there is API to check memory access, it should
|
||||
// be used to terminate nicely, especially with CDB.
|
||||
// 1) Gdb will catch SIGSEGV and return to the calling frame.
|
||||
// This is just fine provided we only _read_ memory in the custom handlers
|
||||
// below.
|
||||
// 2) For MSVC/CDB, exceptions must be handled in the dumper, which is
|
||||
// achieved using __try/__except. The exception will be reported in the
|
||||
// debugger, which will then execute a 'gN' command, passing handling back
|
||||
// to the __except clause.
|
||||
|
||||
volatile int qProvokeSegFaultHelper;
|
||||
|
||||
@@ -269,11 +279,16 @@ static bool startsWith(const char *s, const char *t)
|
||||
return qstrncmp(s, t, qstrlen(t)) == 0;
|
||||
}
|
||||
|
||||
// provoke segfault when address is not readable
|
||||
#define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
#define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
// provoke segfault unconditionally
|
||||
#define qCheck(b) do { if (!(b)) qProvokeSegFaultHelper = *(char*)0; } while (0)
|
||||
// Check memory for read access and provoke segfault if nothing else helps.
|
||||
// On Windows, try to be less crash-prone by checking memory using WinAPI
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# define qCheckAccess(d) if (IsBadReadPtr(d, 1)) return false; do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
# define qCheckPointer(d) if (d && IsBadReadPtr(d, 1)) return false; do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
#else
|
||||
# define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
# define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
|
||||
#endif
|
||||
|
||||
const char *stripNamespace(const char *type)
|
||||
{
|
||||
@@ -692,11 +707,14 @@ void QDumper::putEllipsis()
|
||||
#define TT(type, value) \
|
||||
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>"
|
||||
|
||||
static void qDumpUnknown(QDumper &d)
|
||||
#define DUMPUNKNOWN_MESSAGE "<internal error>"
|
||||
static void qDumpUnknown(QDumper &d, const char *why = 0)
|
||||
{
|
||||
P(d, "iname", d.iname);
|
||||
P(d, "addr", d.data);
|
||||
P(d, "value", "<internal error>");
|
||||
if (!why)
|
||||
why = DUMPUNKNOWN_MESSAGE;
|
||||
P(d, "value", why);
|
||||
P(d, "type", d.outertype);
|
||||
P(d, "numchild", "0");
|
||||
d.disarm();
|
||||
@@ -815,7 +833,7 @@ static void qDumpInnerValueOrPointer(QDumper &d,
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void qDumpQByteArray(QDumper &d)
|
||||
static bool qDumpQByteArray(QDumper &d)
|
||||
{
|
||||
const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
|
||||
|
||||
@@ -848,6 +866,7 @@ static void qDumpQByteArray(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQDateTime(QDumper &d)
|
||||
@@ -1063,7 +1082,7 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val
|
||||
}
|
||||
|
||||
|
||||
static void qDumpQHash(QDumper &d)
|
||||
static bool qDumpQHash(QDumper &d)
|
||||
{
|
||||
QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
|
||||
const char *keyType = d.templateParameters[0];
|
||||
@@ -1078,7 +1097,7 @@ static void qDumpQHash(QDumper &d)
|
||||
int n = h->size;
|
||||
|
||||
if (n < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (n > 0) {
|
||||
qCheckPointer(h->fakeNext);
|
||||
qCheckPointer(*h->buckets);
|
||||
@@ -1127,6 +1146,7 @@ static void qDumpQHash(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQHashNode(QDumper &d)
|
||||
@@ -1177,23 +1197,25 @@ static void qDumpQImage(QDumper &d)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void qDumpQList(QDumper &d)
|
||||
static bool qDumpQList(QDumper &d)
|
||||
{
|
||||
// This uses the knowledge that QList<T> has only a single member
|
||||
// of type union { QListData p; QListData::Data *d; };
|
||||
|
||||
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
|
||||
const QListData::Data *pdata =
|
||||
*reinterpret_cast<const QListData::Data* const*>(d.data);
|
||||
qCheckAccess(pdata);
|
||||
int nn = ldata.size();
|
||||
if (nn < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (nn > 0) {
|
||||
qCheckAccess(ldata.d->array);
|
||||
//qCheckAccess(ldata.d->array[0]);
|
||||
//qCheckAccess(ldata.d->array[nn - 1]);
|
||||
#if QT_VERSION >= 0x040400
|
||||
if (ldata.d->ref._q_value <= 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1252,9 +1274,10 @@ static void qDumpQList(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQLinkedList(QDumper &d)
|
||||
static bool qDumpQLinkedList(QDumper &d)
|
||||
{
|
||||
// This uses the knowledge that QLinkedList<T> has only a single member
|
||||
// of type union { QLinkedListData *d; QLinkedListNode<T> *e; };
|
||||
@@ -1262,7 +1285,7 @@ static void qDumpQLinkedList(QDumper &d)
|
||||
reinterpret_cast<const QLinkedListData*>(deref(d.data));
|
||||
int nn = ldata->size;
|
||||
if (nn < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
|
||||
int n = nn;
|
||||
P(d, "value", "<" << n << " items>");
|
||||
@@ -1294,6 +1317,7 @@ static void qDumpQLinkedList(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQLocale(QDumper &d)
|
||||
@@ -1341,7 +1365,7 @@ static void qDumpQLocale(QDumper &d)
|
||||
d.disarm();
|
||||
}
|
||||
|
||||
static void qDumpQMapNode(QDumper &d)
|
||||
static bool qDumpQMapNode(QDumper &d)
|
||||
{
|
||||
const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
|
||||
const char *keyType = d.templateParameters[0];
|
||||
@@ -1375,9 +1399,10 @@ static void qDumpQMapNode(QDumper &d)
|
||||
}
|
||||
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQMap(QDumper &d)
|
||||
static bool qDumpQMap(QDumper &d)
|
||||
{
|
||||
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
|
||||
const char *keyType = d.templateParameters[0];
|
||||
@@ -1386,7 +1411,7 @@ static void qDumpQMap(QDumper &d)
|
||||
int n = h->size;
|
||||
|
||||
if (n < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (n > 0) {
|
||||
qCheckAccess(h->backward);
|
||||
qCheckAccess(h->forward[0]);
|
||||
@@ -1455,11 +1480,12 @@ static void qDumpQMap(QDumper &d)
|
||||
}
|
||||
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQMultiMap(QDumper &d)
|
||||
static bool qDumpQMultiMap(QDumper &d)
|
||||
{
|
||||
qDumpQMap(d);
|
||||
return qDumpQMap(d);
|
||||
}
|
||||
|
||||
static void qDumpQModelIndex(QDumper &d)
|
||||
@@ -1890,7 +1916,7 @@ static void qDumpQPixmap(QDumper &d)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void qDumpQSet(QDumper &d)
|
||||
static bool qDumpQSet(QDumper &d)
|
||||
{
|
||||
// This uses the knowledge that QHash<T> has only a single member
|
||||
// of union { QHashData *d; QHashNode<Key, T> *e; };
|
||||
@@ -1899,7 +1925,7 @@ static void qDumpQSet(QDumper &d)
|
||||
|
||||
int n = hd->size;
|
||||
if (n < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (n > 0) {
|
||||
qCheckAccess(node);
|
||||
qCheckPointer(node->next);
|
||||
@@ -1933,6 +1959,7 @@ static void qDumpQSet(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQSharedPointer(QDumper &d)
|
||||
@@ -1959,7 +1986,7 @@ static void qDumpQSharedPointer(QDumper &d)
|
||||
d.disarm();
|
||||
}
|
||||
|
||||
static void qDumpQString(QDumper &d)
|
||||
static bool qDumpQString(QDumper &d)
|
||||
{
|
||||
const QString &str = *reinterpret_cast<const QString *>(d.data);
|
||||
|
||||
@@ -1975,14 +2002,15 @@ static void qDumpQString(QDumper &d)
|
||||
P(d, "numchild", "0");
|
||||
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQStringList(QDumper &d)
|
||||
static bool qDumpQStringList(QDumper &d)
|
||||
{
|
||||
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
|
||||
int n = list.size();
|
||||
if (n < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (n > 0) {
|
||||
qCheckAccess(&list.front());
|
||||
qCheckAccess(&list.back());
|
||||
@@ -2009,6 +2037,7 @@ static void qDumpQStringList(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpQTextCodec(QDumper &d)
|
||||
@@ -2109,7 +2138,7 @@ static void qDumpQVariant(QDumper &d)
|
||||
d.disarm();
|
||||
}
|
||||
|
||||
static void qDumpQVector(QDumper &d)
|
||||
static bool qDumpQVector(QDumper &d)
|
||||
{
|
||||
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
|
||||
|
||||
@@ -2117,7 +2146,7 @@ static void qDumpQVector(QDumper &d)
|
||||
// from asking for unavailable child details
|
||||
int nn = v->size;
|
||||
if (nn < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (nn > 0) {
|
||||
//qCheckAccess(&vec.front());
|
||||
//qCheckAccess(&vec.back());
|
||||
@@ -2149,9 +2178,10 @@ static void qDumpQVector(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdList(QDumper &d)
|
||||
static bool qDumpStdList(QDumper &d)
|
||||
{
|
||||
const std::list<int> &list = *reinterpret_cast<const std::list<int> *>(d.data);
|
||||
const void *p = d.data;
|
||||
@@ -2196,9 +2226,10 @@ static void qDumpStdList(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdMap(QDumper &d)
|
||||
static bool qDumpStdMap(QDumper &d)
|
||||
{
|
||||
typedef std::map<int, int> DummyType;
|
||||
const DummyType &map = *reinterpret_cast<const DummyType*>(d.data);
|
||||
@@ -2209,7 +2240,8 @@ static void qDumpStdMap(QDumper &d)
|
||||
p = deref(p);
|
||||
|
||||
int nn = map.size();
|
||||
qCheck(nn >= 0);
|
||||
if (nn < 0)
|
||||
return false;
|
||||
DummyType::const_iterator it = map.begin();
|
||||
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
|
||||
qCheckAccess(it.operator->());
|
||||
@@ -2263,9 +2295,10 @@ static void qDumpStdMap(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdSet(QDumper &d)
|
||||
static bool qDumpStdSet(QDumper &d)
|
||||
{
|
||||
typedef std::set<int> DummyType;
|
||||
const DummyType &set = *reinterpret_cast<const DummyType*>(d.data);
|
||||
@@ -2274,7 +2307,8 @@ static void qDumpStdSet(QDumper &d)
|
||||
p = deref(p);
|
||||
|
||||
int nn = set.size();
|
||||
qCheck(nn >= 0);
|
||||
if (nn < 0)
|
||||
return false;
|
||||
DummyType::const_iterator it = set.begin();
|
||||
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
|
||||
qCheckAccess(it.operator->());
|
||||
@@ -2306,9 +2340,10 @@ static void qDumpStdSet(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdString(QDumper &d)
|
||||
static bool qDumpStdString(QDumper &d)
|
||||
{
|
||||
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
|
||||
|
||||
@@ -2325,9 +2360,10 @@ static void qDumpStdString(QDumper &d)
|
||||
P(d, "numchild", "0");
|
||||
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdWString(QDumper &d)
|
||||
static bool qDumpStdWString(QDumper &d)
|
||||
{
|
||||
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
|
||||
|
||||
@@ -2344,9 +2380,10 @@ static void qDumpStdWString(QDumper &d)
|
||||
P(d, "numchild", "0");
|
||||
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdVector(QDumper &d)
|
||||
static bool qDumpStdVector(QDumper &d)
|
||||
{
|
||||
// Correct type would be something like:
|
||||
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl
|
||||
@@ -2361,7 +2398,7 @@ static void qDumpStdVector(QDumper &d)
|
||||
// from asking for unavailable child details
|
||||
int nn = (v->finish - v->start) / d.extraInt[0];
|
||||
if (nn < 0)
|
||||
qCheck(false);
|
||||
return false;
|
||||
if (nn > 0) {
|
||||
qCheckAccess(v->start);
|
||||
qCheckAccess(v->finish);
|
||||
@@ -2392,9 +2429,10 @@ static void qDumpStdVector(QDumper &d)
|
||||
d << "]";
|
||||
}
|
||||
d.disarm();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qDumpStdVectorBool(QDumper &d)
|
||||
static bool qDumpStdVectorBool(QDumper &d)
|
||||
{
|
||||
// FIXME
|
||||
return qDumpStdVector(d);
|
||||
@@ -2402,10 +2440,14 @@ static void qDumpStdVectorBool(QDumper &d)
|
||||
|
||||
static void handleProtocolVersion2and3(QDumper & d)
|
||||
{
|
||||
|
||||
if (!d.outertype[0]) {
|
||||
qDumpUnknown(d);
|
||||
return;
|
||||
}
|
||||
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
|
||||
__try {
|
||||
#endif
|
||||
|
||||
d.setupTemplateParameters();
|
||||
P(d, "iname", d.iname);
|
||||
@@ -2551,6 +2593,12 @@ static void handleProtocolVersion2and3(QDumper & d)
|
||||
|
||||
if (!d.success)
|
||||
qDumpUnknown(d);
|
||||
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
|
||||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
qDumpUnknown(d, DUMPUNKNOWN_MESSAGE" <exception>");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
@@ -34,6 +34,7 @@ HEADERS += \
|
||||
$$PWD/cdbdebugoutput.h \
|
||||
$$PWD/cdbsymbolgroupcontext.h \
|
||||
$$PWD/cdbstacktracecontext.h \
|
||||
$$PWD/cdbstackframecontext.h \
|
||||
$$PWD/cdbbreakpoint.h \
|
||||
$$PWD/cdbmodules.h \
|
||||
$$PWD/cdbassembler.h \
|
||||
@@ -46,6 +47,7 @@ SOURCES += \
|
||||
$$PWD/cdbdebugeventcallback.cpp \
|
||||
$$PWD/cdbdebugoutput.cpp \
|
||||
$$PWD/cdbsymbolgroupcontext.cpp \
|
||||
$$PWD/cdbstackframecontext.cpp \
|
||||
$$PWD/cdbstacktracecontext.cpp \
|
||||
$$PWD/cdbbreakpoint.cpp \
|
||||
$$PWD/cdbmodules.cpp \
|
||||
|
@@ -29,8 +29,9 @@
|
||||
|
||||
#include "cdbdebugengine.h"
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbstacktracecontext.h"
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbbreakpoint.h"
|
||||
#include "cdbmodules.h"
|
||||
#include "cdbassembler.h"
|
||||
@@ -274,7 +275,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
|
||||
m_hDebuggeeProcess(0),
|
||||
m_hDebuggeeThread(0),
|
||||
m_breakEventMode(BreakEventHandle),
|
||||
m_dumper(&m_cif),
|
||||
m_dumper(new CdbDumperHelper(parent, &m_cif)),
|
||||
m_watchTimer(-1),
|
||||
m_debugEventCallBack(engine),
|
||||
m_engine(engine),
|
||||
@@ -464,7 +465,7 @@ bool CdbDebugEngine::startDebugger()
|
||||
dumperEnabled = false;
|
||||
}
|
||||
}
|
||||
m_d->m_dumper.reset(dumperLibName, dumperEnabled);
|
||||
m_d->m_dumper->reset(dumperLibName, dumperEnabled);
|
||||
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
|
||||
QString errorMessage;
|
||||
bool rc = false;
|
||||
@@ -590,19 +591,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
|
||||
} else {
|
||||
m_currentThreadId = 0;
|
||||
}
|
||||
// Set initial breakpoints
|
||||
// Clear any saved breakpoints and set initial breakpoints
|
||||
m_engine->executeDebuggerCommand(QLatin1String("bc"));
|
||||
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
|
||||
m_engine->attemptBreakpointSynchronization();
|
||||
// At any event, we want a temporary breakpoint at main() to load
|
||||
// the dumpers.
|
||||
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
|
||||
if (m_dumper->state() == CdbDumperHelper::NotLoaded) {
|
||||
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
|
||||
QString errorMessage;
|
||||
CDBBreakPoint mainBP;
|
||||
// Do not resolve at this point in the rare event someone
|
||||
// has main in a module
|
||||
mainBP.funcName = QLatin1String("main");
|
||||
mainBP.oneShot = true;
|
||||
QString errorMessage;
|
||||
if (!mainBP.add(m_cif.debugControl, &errorMessage))
|
||||
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
|
||||
}
|
||||
@@ -675,13 +677,13 @@ void CdbDebugEngine::exitDebugger()
|
||||
killWatchTimer();
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const
|
||||
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
|
||||
{
|
||||
if (!m_currentStackTrace) {
|
||||
*errorMessage = QLatin1String(msgNoStackTraceC);
|
||||
return 0;
|
||||
}
|
||||
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage))
|
||||
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
|
||||
return sg;
|
||||
return 0;
|
||||
}
|
||||
@@ -718,8 +720,8 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage))
|
||||
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage);
|
||||
if (CdbStackFrameContext *sgc = getStackFrameContext(frameIndex, errorMessage))
|
||||
success = sgc->populateModelInitially(wh, errorMessage);
|
||||
|
||||
wh->rebuildModel();
|
||||
return success;
|
||||
@@ -800,8 +802,8 @@ void CdbDebugEngine::updateWatchModel()
|
||||
filterEvaluateWatchers(&incomplete, watchHandler);
|
||||
// Do locals. We might get called while running when someone enters watchers
|
||||
if (!incomplete.empty()) {
|
||||
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage);
|
||||
if (!sg || !CdbSymbolGroupContext::completeModel(sg, incomplete, watchHandler, &errorMessage))
|
||||
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
||||
if (!sg || !sg->completeModel(incomplete, watchHandler, &errorMessage))
|
||||
break;
|
||||
}
|
||||
watchHandler->rebuildModel();
|
||||
@@ -1016,7 +1018,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
|
||||
bool success = false;
|
||||
do {
|
||||
QString newValue;
|
||||
CdbSymbolGroupContext *sg = m_d->getStackFrameSymbolGroupContext(frameIndex, &errorMessage);
|
||||
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
|
||||
if (!sg)
|
||||
break;
|
||||
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
||||
@@ -1061,17 +1063,30 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
|
||||
QString *value,
|
||||
QString *type,
|
||||
QString *errorMessage)
|
||||
{
|
||||
DEBUG_VALUE debugValue;
|
||||
if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage))
|
||||
return false;
|
||||
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl,
|
||||
const QString &expression,
|
||||
DEBUG_VALUE *debugValue,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDB > 1)
|
||||
qDebug() << Q_FUNC_INFO << expression;
|
||||
DEBUG_VALUE debugValue;
|
||||
memset(&debugValue, 0, sizeof(DEBUG_VALUE));
|
||||
|
||||
memset(debugValue, 0, sizeof(DEBUG_VALUE));
|
||||
// Original syntax must be restored, else setting breakpoints will fail.
|
||||
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
|
||||
SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS);
|
||||
ULONG errorPosition = 0;
|
||||
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(),
|
||||
DEBUG_VALUE_INVALID, &debugValue,
|
||||
&errorPosition); if (FAILED(hr)) {
|
||||
const HRESULT hr = ctrl->EvaluateWide(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);
|
||||
@@ -1081,7 +1096,6 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1355,14 +1369,14 @@ void CdbDebugEnginePrivate::handleDebugEvent()
|
||||
case BreakEventHandle:
|
||||
case BreakEventMain:
|
||||
if (mode == BreakEventMain)
|
||||
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
|
||||
m_dumper->load(m_debuggerManager);
|
||||
m_debuggerManagerAccess->notifyInferiorStopped();
|
||||
updateThreadList();
|
||||
updateStackTrace();
|
||||
break;
|
||||
case BreakEventMainLoadDumpers:
|
||||
// Temp stop to load dumpers
|
||||
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
|
||||
m_dumper->load(m_debuggerManager);
|
||||
m_engine->startWatchTimer();
|
||||
continueInferiorProcess();
|
||||
break;
|
||||
@@ -1436,7 +1450,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
|
||||
QString errorMessage;
|
||||
m_engine->reloadRegisters();
|
||||
m_currentStackTrace =
|
||||
CdbStackTraceContext::create(&m_cif, m_currentThreadId, &errorMessage);
|
||||
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
|
||||
if (!m_currentStackTrace) {
|
||||
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
|
||||
return;
|
||||
|
@@ -46,7 +46,7 @@ namespace Internal {
|
||||
class DebuggerManager;
|
||||
class IDebuggerManagerAccessForEngines;
|
||||
class WatchHandler;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbStackFrameContext;
|
||||
class CdbStackTraceContext;
|
||||
|
||||
// Thin wrapper around the 'DBEng' debugger engine shared library
|
||||
@@ -125,7 +125,7 @@ struct CdbDebugEnginePrivate
|
||||
void cleanStackTrace();
|
||||
void clearForRun();
|
||||
void handleModuleLoad(const QString &);
|
||||
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const;
|
||||
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
|
||||
void clearDisplay();
|
||||
|
||||
bool interruptInterferiorProcess(QString *errorMessage);
|
||||
@@ -136,6 +136,7 @@ struct CdbDebugEnginePrivate
|
||||
bool attemptBreakpointSynchronization(QString *errorMessage);
|
||||
|
||||
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
||||
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
|
||||
|
||||
const QSharedPointer<CdbOptions> m_options;
|
||||
HANDLE m_hDebuggeeProcess;
|
||||
@@ -147,7 +148,7 @@ struct CdbDebugEnginePrivate
|
||||
CdbComInterfaces m_cif;
|
||||
CdbDebugEventCallback m_debugEventCallBack;
|
||||
CdbDebugOutput m_debugOutputCallBack;
|
||||
CdbDumperHelper m_dumper;
|
||||
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
|
||||
CdbDebugEngine* m_engine;
|
||||
DebuggerManager *m_debuggerManager;
|
||||
|
@@ -318,14 +318,16 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
|
||||
}
|
||||
|
||||
// Format exception with stacktrace in case of C++ exception
|
||||
void formatException(const EXCEPTION_RECORD64 *e, CdbComInterfaces &cif, QTextStream &str)
|
||||
void formatException(const EXCEPTION_RECORD64 *e,
|
||||
const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
QTextStream &str)
|
||||
{
|
||||
formatException(e, str);
|
||||
if (e->ExceptionCode == cppExceptionCode) {
|
||||
QString errorMessage;
|
||||
ULONG currentThreadId = 0;
|
||||
cif.debugSystemObjects->GetCurrentThreadId(¤tThreadId);
|
||||
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(&cif, currentThreadId, &errorMessage)) {
|
||||
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(¤tThreadId);
|
||||
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
|
||||
str << "at:\n";
|
||||
stc->format(str);
|
||||
str <<'\n';
|
||||
@@ -343,7 +345,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
|
||||
QString msg;
|
||||
{
|
||||
QTextStream str(&msg);
|
||||
formatException(Exception, m_pEngine->m_d->m_cif, str);
|
||||
formatException(Exception, m_pEngine->m_d->m_dumper, str);
|
||||
}
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << msg;
|
||||
@@ -469,6 +471,37 @@ STDMETHODIMP CdbDebugEventCallback::SystemError(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// -----------ExceptionLoggerEventCallback
|
||||
CdbExceptionLoggerEventCallback::CdbExceptionLoggerEventCallback(const QString &logPrefix,
|
||||
IDebuggerManagerAccessForEngines *access) :
|
||||
m_logPrefix(logPrefix),
|
||||
m_access(access)
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbExceptionLoggerEventCallback::GetInterestMask(THIS_ __out PULONG mask)
|
||||
{
|
||||
*mask = DEBUG_EVENT_EXCEPTION;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CdbExceptionLoggerEventCallback::Exception(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG /* FirstChance */
|
||||
)
|
||||
{
|
||||
m_exceptionMessages.push_back(QString());
|
||||
{
|
||||
QTextStream str(&m_exceptionMessages.back());
|
||||
formatException(Exception, str);
|
||||
}
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << m_exceptionMessages.back();
|
||||
m_access->showDebuggerOutput(m_logPrefix, m_exceptionMessages.back());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// -----------IgnoreDebugEventCallback
|
||||
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
|
||||
{
|
||||
|
@@ -32,12 +32,13 @@
|
||||
|
||||
#include "cdbcom.h"
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbDebugEngine;
|
||||
class IDebuggerManagerAccessForEngines;
|
||||
|
||||
// Base class for event callbacks that takes care
|
||||
// Active X magic. Provides base implementations with
|
||||
@@ -235,6 +236,34 @@ private:
|
||||
CdbDebugEngine *m_pEngine;
|
||||
};
|
||||
|
||||
// Event handler logs exceptions to the debugger window
|
||||
// and ignores the rest. To be used for running dumper calls.
|
||||
class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase
|
||||
{
|
||||
public:
|
||||
explicit CdbExceptionLoggerEventCallback(const QString &logPrefix,
|
||||
IDebuggerManagerAccessForEngines *access);
|
||||
|
||||
STDMETHOD(GetInterestMask)(
|
||||
THIS_
|
||||
__out PULONG mask
|
||||
);
|
||||
|
||||
STDMETHOD(Exception)(
|
||||
THIS_
|
||||
__in PEXCEPTION_RECORD64 Exception,
|
||||
__in ULONG FirstChance
|
||||
);
|
||||
|
||||
int exceptionCount() const { return m_exceptionMessages.size(); }
|
||||
QStringList exceptionMessages() const { return m_exceptionMessages; }
|
||||
|
||||
private:
|
||||
const QString m_logPrefix;
|
||||
IDebuggerManagerAccessForEngines *m_access;
|
||||
QStringList m_exceptionMessages;
|
||||
};
|
||||
|
||||
// Event handler that ignores everything
|
||||
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
|
||||
{
|
||||
|
@@ -80,7 +80,8 @@ STDMETHODIMP CdbDebugOutputBase::Output(
|
||||
IN PCWSTR text
|
||||
)
|
||||
{
|
||||
output(mask, QString::fromUtf16(text));
|
||||
const QString msg = QString::fromUtf16(text);
|
||||
output(mask, msg.trimmed());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,8 @@
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbdebugoutput.h"
|
||||
#include "cdbdebugeventcallback.h"
|
||||
#include "watchutils.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "watchhandler.h"
|
||||
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QCoreApplication>
|
||||
@@ -43,21 +44,20 @@ enum { loadDebug = 0 };
|
||||
static const char *dumperModuleNameC = "gdbmacros";
|
||||
static const char *qtCoreModuleNameC = "QtCore";
|
||||
static const ULONG waitTimeOutMS = 30000;
|
||||
|
||||
static const char *dumperPrefixC = "dumper:";
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Alloc memory in debuggee using the ".dvalloc" command as
|
||||
// there seems to be no API for it.
|
||||
static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
||||
CIDebugClient *client,
|
||||
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(client, &stringHandler);
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
|
||||
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;
|
||||
@@ -68,7 +68,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
||||
if (ok)
|
||||
*address = addri;
|
||||
}
|
||||
if (loadDebug)
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
|
||||
if (!ok) {
|
||||
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
|
||||
@@ -78,18 +78,16 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
|
||||
}
|
||||
|
||||
// Alloc an AscII string in debuggee
|
||||
static bool createDebuggeeAscIIString(CIDebugControl *ctl,
|
||||
CIDebugClient *client,
|
||||
CIDebugDataSpaces *data,
|
||||
const QString &s,
|
||||
ULONG64 *address,
|
||||
QString *errorMessage)
|
||||
static bool createDebuggeeAscIIString(CdbComInterfaces *cif,
|
||||
const QString &s,
|
||||
ULONG64 *address,
|
||||
QString *errorMessage)
|
||||
{
|
||||
QByteArray sAsciiData = s.toLocal8Bit();
|
||||
sAsciiData += '\0';
|
||||
if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage))
|
||||
if (!allocDebuggeeMemory(cif, sAsciiData.size(), address, errorMessage))
|
||||
return false;
|
||||
const HRESULT hr = data->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
||||
const HRESULT hr = cif->debugDataSpaces->WriteVirtual(*address, sAsciiData.data(), sAsciiData.size(), 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage= msgComFailed("WriteVirtual", hr);
|
||||
return false;
|
||||
@@ -101,21 +99,20 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
|
||||
// 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(CIDebugControl *ctl,
|
||||
CIDebugClient *client,
|
||||
CIDebugSymbols *syms,
|
||||
CIDebugDataSpaces *data,
|
||||
const QString &moduleName, QString *errorMessage)
|
||||
static bool debuggeeLoadLibrary(IDebuggerManagerAccessForEngines *access,
|
||||
CdbComInterfaces *cif,
|
||||
const QString &moduleName,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (loadDebug)
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << moduleName;
|
||||
// Try to ignore the breakpoints
|
||||
IgnoreDebugEventCallback devNull;
|
||||
EventCallbackRedirector eventRedir(client, &devNull);
|
||||
CdbExceptionLoggerEventCallback exLogger(QLatin1String(dumperPrefixC), access);
|
||||
EventCallbackRedirector eventRedir(cif->debugClient, &exLogger);
|
||||
// Make a call to LoadLibraryA. First, reserve memory in debugger
|
||||
// and copy name over.
|
||||
ULONG64 nameAddress;
|
||||
if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage))
|
||||
if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage))
|
||||
return false;
|
||||
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
|
||||
// (void* LoadLibraryA(char*)). However, despite providing a symbol
|
||||
@@ -125,21 +122,21 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
||||
// 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(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
||||
if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
|
||||
return false;
|
||||
QString callCmd = QLatin1String(".call ");
|
||||
callCmd += dummyFunc;
|
||||
callCmd += QLatin1String("(0x");
|
||||
callCmd += QString::number(nameAddress, 16);
|
||||
callCmd += QLatin1Char(')');
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage))
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
|
||||
return false;
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
|
||||
return false;
|
||||
// This will hit a breakpoint.
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QString(QLatin1Char('g')), errorMessage))
|
||||
return false;
|
||||
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
|
||||
const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||
return false;
|
||||
@@ -147,66 +144,13 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------- CdbDumperHelper::DumperInputParameters
|
||||
struct CdbDumperHelper::DumperInputParameters {
|
||||
DumperInputParameters(int protocolVersion_ = 1,
|
||||
int token_ = 1,
|
||||
quint64 Address_ = 0,
|
||||
bool dumpChildren_ = false,
|
||||
int extraInt0_ = 0,
|
||||
int extraInt1_ = 0,
|
||||
int extraInt2_ = 0,
|
||||
int extraInt3_ = 0);
|
||||
|
||||
QString command(const QString &dumpFunction) const;
|
||||
|
||||
int protocolVersion;
|
||||
int token;
|
||||
quint64 address;
|
||||
bool dumpChildren;
|
||||
int extraInt0;
|
||||
int extraInt1;
|
||||
int extraInt2;
|
||||
int extraInt3;
|
||||
};
|
||||
|
||||
CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
|
||||
int token_,
|
||||
quint64 address_,
|
||||
bool dumpChildren_,
|
||||
int extraInt0_,
|
||||
int extraInt1_,
|
||||
int extraInt2_,
|
||||
int extraInt3_) :
|
||||
protocolVersion(protocolVersion_),
|
||||
token(token_),
|
||||
address(address_),
|
||||
dumpChildren(dumpChildren_),
|
||||
extraInt0(extraInt0_),
|
||||
extraInt1(extraInt1_),
|
||||
extraInt2(extraInt2_),
|
||||
extraInt3(extraInt3_)
|
||||
{
|
||||
}
|
||||
|
||||
QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
|
||||
{
|
||||
QString rc;
|
||||
QTextStream str(&rc);
|
||||
str.setIntegerBase(16);
|
||||
str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
|
||||
str.setIntegerBase(16);
|
||||
str << "0x" << address;
|
||||
str.setIntegerBase(10);
|
||||
str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
|
||||
<< ',' << extraInt2 << ',' << extraInt3 << ')';
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ------------------- CdbDumperHelper
|
||||
|
||||
CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
|
||||
CdbDumperHelper::CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
|
||||
CdbComInterfaces *cif) :
|
||||
m_messagePrefix(QLatin1String(dumperPrefixC)),
|
||||
m_state(NotLoaded),
|
||||
m_access(access),
|
||||
m_cif(cif),
|
||||
m_inBufferAddress(0),
|
||||
m_inBufferSize(0),
|
||||
@@ -234,24 +178,23 @@ void CdbDumperHelper::reset(const QString &library, bool enabled)
|
||||
m_library = library;
|
||||
m_state = enabled ? NotLoaded : Disabled;
|
||||
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
|
||||
m_knownTypes.clear();
|
||||
m_qtVersion.clear();
|
||||
m_qtNamespace.clear();
|
||||
m_helper.clear();
|
||||
m_inBufferAddress = m_outBufferAddress = 0;
|
||||
m_inBufferSize = m_outBufferSize = 0;
|
||||
m_typeSizeCache.clear();
|
||||
m_failedTypes.clear();
|
||||
clearBuffer();
|
||||
}
|
||||
|
||||
// Attempt to load and initialize dumpers, give feedback
|
||||
// to user.
|
||||
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
|
||||
void CdbDumperHelper::load(DebuggerManager *manager)
|
||||
{
|
||||
enum Result { Failed, Succeeded, NoQtApp };
|
||||
|
||||
if (m_state != NotLoaded)
|
||||
return;
|
||||
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
|
||||
const QString messagePrefix = QLatin1String("dumper:");
|
||||
QString message;
|
||||
Result result = Failed;
|
||||
do {
|
||||
@@ -266,12 +209,11 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
||||
// Make sure the dumper lib is loaded.
|
||||
if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
|
||||
// Try to load
|
||||
if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
|
||||
m_library, &message)) {
|
||||
if (!debuggeeLoadLibrary(m_access, m_cif, m_library, &message)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
access->showDebuggerOutput(messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
|
||||
m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
|
||||
}
|
||||
// Resolve symbols and do call to get types
|
||||
if (!resolveSymbols(&message))
|
||||
@@ -285,17 +227,17 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
||||
switch (result) {
|
||||
case Failed:
|
||||
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
|
||||
access->showDebuggerOutput(messagePrefix, message);
|
||||
access->showQtDumperLibraryWarning(message);
|
||||
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||
m_access->showQtDumperLibraryWarning(message);
|
||||
m_state = Disabled;
|
||||
break;
|
||||
case Succeeded:
|
||||
access->showDebuggerOutput(messagePrefix, message);
|
||||
access->showDebuggerOutput(messagePrefix, statusMessage());
|
||||
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||
m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString());
|
||||
m_state = Loaded;
|
||||
break;
|
||||
case NoQtApp:
|
||||
access->showDebuggerOutput(messagePrefix, message);
|
||||
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||
m_state = Disabled;
|
||||
break;
|
||||
}
|
||||
@@ -303,15 +245,6 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
|
||||
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
|
||||
}
|
||||
|
||||
QString CdbDumperHelper::statusMessage() const
|
||||
{
|
||||
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("CdbDumperHelper", "<none>") : m_qtNamespace;
|
||||
return QCoreApplication::translate("CdbDumperHelper",
|
||||
"%n known types, Qt version: %1, Qt namespace: %2",
|
||||
0, QCoreApplication::CodecForTr,
|
||||
m_knownTypes.size()).arg(m_qtVersion, nameSpace);
|
||||
}
|
||||
|
||||
// Retrieve address and optionally size of a symbol.
|
||||
static inline bool getSymbolAddress(CIDebugSymbols *sg,
|
||||
const QString &name,
|
||||
@@ -373,41 +306,82 @@ bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
|
||||
bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
|
||||
{
|
||||
QByteArray output;
|
||||
if (!callDumper(DumperInputParameters(1), &output, errorMessage)) {
|
||||
QString callCmd;
|
||||
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
|
||||
const char *outData;
|
||||
if (!callDumper(callCmd, QByteArray(), &outData, errorMessage)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseQueryDumperOutput(output, &m_knownTypes, &m_qtVersion, &m_qtNamespace)) {
|
||||
if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) {
|
||||
*errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
|
||||
}
|
||||
if (loadDebug)
|
||||
qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion << m_qtNamespace;
|
||||
qDebug() << Q_FUNC_INFO << m_helper.toString(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage)
|
||||
// Write to debuggee memory in chunks
|
||||
bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage)
|
||||
{
|
||||
IgnoreDebugEventCallback devNull;
|
||||
EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull);
|
||||
const QString callCmd = p.command(m_dumpObjectSymbol);
|
||||
// Set up call and a temporary breakpoint after it.
|
||||
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;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr, QString *errorMessage)
|
||||
{
|
||||
*outDataPtr = 0;
|
||||
CdbExceptionLoggerEventCallback exLogger(m_messagePrefix, m_access);
|
||||
EventCallbackRedirector eventRedir(m_cif->debugClient, &exLogger);
|
||||
// write input buffer
|
||||
if (!inBuffer.isEmpty()) {
|
||||
if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage))
|
||||
return false;
|
||||
}
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
|
||||
return false;
|
||||
// Go and filter away break event
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage))
|
||||
return false;
|
||||
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||
// Set up call and a temporary breakpoint after it.
|
||||
// Try to skip debuggee crash exceptions and dumper exceptions
|
||||
// by using 'gh' (go handled)
|
||||
for (int i = 0; i < 10; i++) {
|
||||
const int oldExceptionCount = exLogger.exceptionCount();
|
||||
// Go. If an exception occurs in loop 2, let the dumper handle it.
|
||||
const QString goCmd = i ? QString(QLatin1String("gN")) : QString(QLatin1Char('g'));
|
||||
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
|
||||
return false;
|
||||
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("WaitForEvent", hr);
|
||||
return false;
|
||||
}
|
||||
const int newExceptionCount = exLogger.exceptionCount();
|
||||
// no new exceptions? -> break
|
||||
if (oldExceptionCount == newExceptionCount)
|
||||
break;
|
||||
}
|
||||
if (exLogger.exceptionCount()) {
|
||||
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
|
||||
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
|
||||
return false;
|
||||
}
|
||||
// Read output
|
||||
hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
||||
const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = msgComFailed("ReadVirtual", hr);
|
||||
return false;
|
||||
}
|
||||
// see QDumper implementation
|
||||
const char result = m_buffer[0];
|
||||
const char result = m_buffer[0];
|
||||
switch (result) {
|
||||
case 't':
|
||||
break;
|
||||
@@ -421,7 +395,144 @@ bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *out
|
||||
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
|
||||
return false;
|
||||
}
|
||||
*output = QByteArray(m_buffer + 1);
|
||||
*outDataPtr = m_buffer + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, int source,
|
||||
QList<WatchData> *result, QString *errorMessage)
|
||||
{
|
||||
// Check failure cache and supported types
|
||||
if (m_failedTypes.contains(wd.type))
|
||||
return DumpNotHandled;
|
||||
const QtDumperHelper::TypeData td = m_helper.typeData(wd.type);
|
||||
if (td.type == QtDumperHelper::UnknownType)
|
||||
return DumpNotHandled;
|
||||
|
||||
// Now evaluate
|
||||
const QString message = QCoreApplication::translate("CdbDumperHelper",
|
||||
"Querying dumpers for '%1'/'%2' (%3)").
|
||||
arg(wd.name, wd.exp, wd.type);
|
||||
m_access->showDebuggerOutput(m_messagePrefix, message);
|
||||
const DumpExecuteResult der = executeDump(wd, td, dumpChildren, source, result, errorMessage);
|
||||
if (der == DumpExecuteOk)
|
||||
return DumpOk;
|
||||
// Cache types that fail due to complicated template size expressions.
|
||||
// Exceptions OTOH might occur when accessing variables that are not
|
||||
// yet initialized in a particular breakpoint. That should be ignored
|
||||
if (der == DumpExecuteSizeFailed)
|
||||
m_failedTypes.push_back(wd.type);
|
||||
// log error
|
||||
*errorMessage = QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(wd.name, wd.type, *errorMessage);
|
||||
m_access->showDebuggerOutput(m_messagePrefix, *errorMessage);
|
||||
return DumpError;
|
||||
}
|
||||
|
||||
CdbDumperHelper::DumpExecuteResult
|
||||
CdbDumperHelper::executeDump(const WatchData &wd,
|
||||
const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
|
||||
QList<WatchData> *result, QString *errorMessage)
|
||||
{
|
||||
QByteArray inBuffer;
|
||||
QStringList extraParameters;
|
||||
// Build parameter list.
|
||||
m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
|
||||
// If the parameter list contains sizeof-expressions, execute them separately
|
||||
// and replace them by the resulting numbers
|
||||
const QString sizeOfExpr = QLatin1String("sizeof");
|
||||
const QStringList::iterator eend = extraParameters.end();
|
||||
for (QStringList::iterator it = extraParameters.begin() ; it != eend; ++it) {
|
||||
// Strip 'sizeof(X)' to 'X' and query size
|
||||
QString &ep = *it;
|
||||
if (ep.startsWith(sizeOfExpr)) {
|
||||
int size;
|
||||
ep.truncate(ep.lastIndexOf(QLatin1Char(')')));
|
||||
ep.remove(0, ep.indexOf(QLatin1Char('(')) + 1);
|
||||
if (!getTypeSize(ep, &size, errorMessage))
|
||||
return DumpExecuteSizeFailed;
|
||||
if (loadDebug)
|
||||
qDebug() << "Size" << size << ep;
|
||||
ep = QString::number(size);
|
||||
}
|
||||
}
|
||||
// Execute call
|
||||
QString callCmd;
|
||||
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol
|
||||
<< "(2,0," << wd.addr << ','
|
||||
<< (dumpChildren ? 1 : 0) << ',' << extraParameters.join(QString(QLatin1Char(','))) << ')';
|
||||
if (loadDebug)
|
||||
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd;
|
||||
const char *outputData;
|
||||
if (!callDumper(callCmd, inBuffer, &outputData, errorMessage))
|
||||
return DumpExecuteCallFailed;
|
||||
QtDumperResult dumpResult;
|
||||
if (!QtDumperHelper::parseValue(outputData, &dumpResult)) {
|
||||
*errorMessage = QLatin1String("Parsing of value query output failed.");
|
||||
return DumpExecuteCallFailed;
|
||||
}
|
||||
*result = dumpResult.toWatchData(source);
|
||||
return DumpExecuteOk;
|
||||
}
|
||||
|
||||
// Simplify some types for sizeof expressions
|
||||
static inline void simplifySizeExpression(QString *typeName)
|
||||
{
|
||||
typeName->replace(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"),
|
||||
QLatin1String("std::string"));
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::getTypeSize(const QString &typeNameIn, int *size, QString *errorMessage)
|
||||
{
|
||||
if (loadDebug > 1)
|
||||
qDebug() << Q_FUNC_INFO << typeNameIn;
|
||||
// Look up cache
|
||||
const TypeSizeCache::const_iterator it = m_typeSizeCache.constFind(typeNameIn);
|
||||
if (it != m_typeSizeCache.constEnd()) {
|
||||
*size = it.value();
|
||||
return true;
|
||||
}
|
||||
QString typeName = typeNameIn;
|
||||
simplifySizeExpression(&typeName);
|
||||
// "std::" types sometimes only work without namespace.
|
||||
// If it fails, try again with stripped namespace
|
||||
*size = 0;
|
||||
bool success = false;
|
||||
do {
|
||||
if (runTypeSizeQuery(typeName, size, errorMessage)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
const QString stdNameSpace = QLatin1String("std::");
|
||||
if (!typeName.contains(stdNameSpace))
|
||||
break;
|
||||
typeName.remove(stdNameSpace);
|
||||
errorMessage->clear();
|
||||
if (!runTypeSizeQuery(typeName, size, errorMessage))
|
||||
break;
|
||||
success = true;
|
||||
} while (false);
|
||||
if (success)
|
||||
m_typeSizeCache.insert(typeName, *size);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage)
|
||||
{
|
||||
// Retrieve by C++ expression. If we knew the module, we could make use
|
||||
// of the TypeId query mechanism provided by the IDebugSymbolGroup.
|
||||
DEBUG_VALUE sizeValue;
|
||||
QString expression = QLatin1String("sizeof(");
|
||||
expression += typeName;
|
||||
expression += QLatin1Char(')');
|
||||
if (!CdbDebugEnginePrivate::evaluateExpression(m_cif->debugControl,
|
||||
expression, &sizeValue, errorMessage))
|
||||
return false;
|
||||
qint64 size64;
|
||||
if (!CdbSymbolGroupContext::debugValueToInteger(sizeValue, &size64)) {
|
||||
*errorMessage = QLatin1String("Expression result is not an integer");
|
||||
return false;
|
||||
}
|
||||
*size = static_cast<int>(size64);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,10 @@
|
||||
#ifndef CDBDUMPERHELPER_H
|
||||
#define CDBDUMPERHELPER_H
|
||||
|
||||
#include "watchutils.h"
|
||||
#include "cdbcom.h"
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
@@ -66,7 +69,8 @@ public:
|
||||
Failed
|
||||
};
|
||||
|
||||
explicit CdbDumperHelper(CdbComInterfaces *cif);
|
||||
explicit CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
|
||||
CdbComInterfaces *cif);
|
||||
~CdbDumperHelper();
|
||||
|
||||
State state() const { return m_state; }
|
||||
@@ -76,31 +80,50 @@ public:
|
||||
void reset(const QString &library, bool enabled);
|
||||
|
||||
// Call in a temporary breakpoint state to actually load.
|
||||
void load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access);
|
||||
void load(DebuggerManager *manager);
|
||||
|
||||
enum DumpResult { DumpNotHandled, DumpOk, DumpError };
|
||||
DumpResult dumpType(const WatchData &d, bool dumpChildren, int source,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
// bool handlesType(const QString &typeName) const;
|
||||
|
||||
inline CdbComInterfaces *comInterfaces() const { return m_cif; }
|
||||
|
||||
private:
|
||||
struct DumperInputParameters;
|
||||
|
||||
void clearBuffer();
|
||||
bool resolveSymbols(QString *errorMessage);
|
||||
bool getKnownTypes(QString *errorMessage);
|
||||
bool callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage);
|
||||
inline QString statusMessage() const;
|
||||
bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
|
||||
bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage);
|
||||
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, QString *errorMessage);
|
||||
|
||||
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed };
|
||||
DumpExecuteResult executeDump(const WatchData &wd,
|
||||
const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
|
||||
QList<WatchData> *result, QString *errorMessage);
|
||||
|
||||
static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage);
|
||||
|
||||
const QString m_messagePrefix;
|
||||
State m_state;
|
||||
IDebuggerManagerAccessForEngines *m_access;
|
||||
CdbComInterfaces *m_cif;
|
||||
|
||||
QString m_library;
|
||||
QString m_dumpObjectSymbol;
|
||||
QStringList m_knownTypes;
|
||||
QString m_qtVersion;
|
||||
QString m_qtNamespace;
|
||||
|
||||
quint64 m_inBufferAddress;
|
||||
unsigned long m_inBufferSize;
|
||||
quint64 m_outBufferAddress;
|
||||
unsigned long m_outBufferSize;
|
||||
char *m_buffer;
|
||||
|
||||
typedef QMap<QString, int> TypeSizeCache;
|
||||
TypeSizeCache m_typeSizeCache;
|
||||
QStringList m_failedTypes;
|
||||
|
||||
QtDumperHelper m_helper;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
189
src/plugins/debugger/cdb/cdbstackframecontext.cpp
Normal file
189
src/plugins/debugger/cdb/cdbstackframecontext.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Qt Software Information (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 qt-sales@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "debuggeractions.h"
|
||||
#include "watchhandler.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { OwnerNewItem, OwnerSymbolGroup, OwnerDumper };
|
||||
|
||||
typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
|
||||
typedef QList<WatchData> WatchDataList;
|
||||
|
||||
// Put a sequence of WatchData into the model
|
||||
class WatchHandlerModelInserter {
|
||||
public:
|
||||
explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
|
||||
|
||||
inline WatchHandlerModelInserter & operator*() { return *this; }
|
||||
inline WatchHandlerModelInserter &operator=(const WatchData &wd) {
|
||||
m_wh->insertData(wd);
|
||||
return *this;
|
||||
}
|
||||
inline WatchHandlerModelInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
WatchHandler *m_wh;
|
||||
};
|
||||
|
||||
// Helper to sort apart a sequence of WatchData using the Dumpers.
|
||||
// Puts the stuff for which there is no dumper in the model
|
||||
// as is and sets ownership to symbol group. The rest goes
|
||||
// to the dumpers.
|
||||
class WatchHandlerSorterInserter {
|
||||
public:
|
||||
explicit WatchHandlerSorterInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper);
|
||||
|
||||
inline WatchHandlerSorterInserter & operator*() { return *this; }
|
||||
inline WatchHandlerSorterInserter &operator=(WatchData wd);
|
||||
inline WatchHandlerSorterInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
WatchHandler *m_wh;
|
||||
const SharedPointerCdbDumperHelper m_dumper;
|
||||
QList<WatchData> m_dumperResult;
|
||||
QStringList m_dumperINames;
|
||||
};
|
||||
|
||||
WatchHandlerSorterInserter::WatchHandlerSorterInserter(WatchHandler *wh,
|
||||
const SharedPointerCdbDumperHelper &dumper) :
|
||||
m_wh(wh),
|
||||
m_dumper(dumper)
|
||||
{
|
||||
}
|
||||
|
||||
WatchHandlerSorterInserter &WatchHandlerSorterInserter::operator=(WatchData wd)
|
||||
{
|
||||
// Is this a child belonging to some item handled by dumpers,
|
||||
// such as d-elements of QStrings -> just ignore it.
|
||||
if (const int dumperINamesCount = m_dumperINames.size()) {
|
||||
for (int i = 0; i < dumperINamesCount; i++)
|
||||
if (wd.iname.startsWith(m_dumperINames.at(i)))
|
||||
return *this;
|
||||
}
|
||||
QString errorMessage;
|
||||
switch (m_dumper->dumpType(wd, true, OwnerDumper, &m_dumperResult, &errorMessage)) {
|
||||
case CdbDumperHelper::DumpOk:
|
||||
// Discard the original item and insert the dumper results
|
||||
m_dumperINames.push_back(wd.iname + QLatin1Char('.'));
|
||||
foreach(const WatchData &dwd, m_dumperResult)
|
||||
m_wh->insertData(dwd);
|
||||
break;
|
||||
case CdbDumperHelper::DumpNotHandled:
|
||||
case CdbDumperHelper::DumpError:
|
||||
wd.source = OwnerSymbolGroup;
|
||||
m_wh->insertData(wd);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// -----------CdbStackFrameContext
|
||||
CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
CdbSymbolGroupContext *symbolContext) :
|
||||
m_useDumpers(dumper->state() == CdbDumperHelper::Loaded
|
||||
&& theDebuggerBoolSetting(UseDebuggingHelpers)),
|
||||
m_dumper(dumper),
|
||||
m_symbolContext(symbolContext)
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage)
|
||||
{
|
||||
return m_symbolContext->assignValue(iname, value, newValue, errorMessage);
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << "populateModelInitially";
|
||||
const bool rc = m_useDumpers ?
|
||||
CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
|
||||
WatchHandlerSorterInserter(wh, m_dumper),
|
||||
errorMessage) :
|
||||
CdbSymbolGroupContext::populateModelInitially(m_symbolContext,
|
||||
WatchHandlerModelInserter(wh),
|
||||
errorMessage);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbStackFrameContext::completeModel(const QList<WatchData> &incompleteLocals,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debug) {
|
||||
QDebug nsp = qDebug().nospace();
|
||||
nsp << ">completeModel ";
|
||||
foreach (const WatchData &wd, incompleteLocals)
|
||||
nsp << wd.iname << ' ';
|
||||
nsp << '\n';
|
||||
}
|
||||
|
||||
if (!m_useDumpers) {
|
||||
return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
|
||||
WatchHandlerModelInserter(wh),
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
// Expand dumper items
|
||||
int handledDumpers = 0;
|
||||
foreach (const WatchData &cwd, incompleteLocals) {
|
||||
if (cwd.source == OwnerDumper) { // You already know that.
|
||||
WatchData wd = cwd;
|
||||
wd.setAllUnneeded();
|
||||
wh->insertData(wd);
|
||||
handledDumpers++;
|
||||
}
|
||||
}
|
||||
if (handledDumpers == incompleteLocals.size())
|
||||
return true;
|
||||
// Expand symbol group items
|
||||
return CdbSymbolGroupContext::completeModel(m_symbolContext, incompleteLocals,
|
||||
WatchHandlerSorterInserter(wh, m_dumper),
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
CdbStackFrameContext::~CdbStackFrameContext()
|
||||
{
|
||||
delete m_symbolContext;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
74
src/plugins/debugger/cdb/cdbstackframecontext.h
Normal file
74
src/plugins/debugger/cdb/cdbstackframecontext.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Qt Software Information (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 qt-sales@nokia.com.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSTACKFRAMECONTEXT_H
|
||||
#define CDBSTACKFRAMECONTEXT_H
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
class WatchHandler;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbDumperHelper;
|
||||
|
||||
/* CdbStackFrameContext manages a symbol group context and
|
||||
* a dumper context. It dispatches calls between the local items
|
||||
* that are handled by the symbol group and those that are handled by the dumpers. */
|
||||
|
||||
class CdbStackFrameContext
|
||||
{
|
||||
Q_DISABLE_COPY(CdbStackFrameContext)
|
||||
public:
|
||||
explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
CdbSymbolGroupContext *symbolContext);
|
||||
~CdbStackFrameContext();
|
||||
|
||||
bool assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
|
||||
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
|
||||
|
||||
bool completeModel(const QList<WatchData> &incompleteLocals,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage);
|
||||
|
||||
private:
|
||||
const bool m_useDumpers;
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
CdbSymbolGroupContext *m_symbolContext;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSTACKFRAMECONTEXT_H
|
@@ -28,9 +28,11 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstacktracecontext.h"
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbbreakpoint.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QTextStream>
|
||||
@@ -38,18 +40,20 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
CdbStackTraceContext::CdbStackTraceContext(CdbComInterfaces *cif) :
|
||||
m_cif(cif),
|
||||
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
|
||||
m_dumper(dumper),
|
||||
m_cif(dumper->comInterfaces()),
|
||||
m_instructionOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
||||
CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
unsigned long threadId,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << threadId;
|
||||
CdbComInterfaces *cif = dumper->comInterfaces();
|
||||
HRESULT hr = cif->debugSystemObjects->SetCurrentThreadId(threadId);
|
||||
if (FAILED(hr)) {
|
||||
*errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
|
||||
@@ -60,7 +64,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
||||
}
|
||||
// fill the DEBUG_STACK_FRAME array
|
||||
ULONG frameCount;
|
||||
CdbStackTraceContext *ctx = new CdbStackTraceContext(cif);
|
||||
CdbStackTraceContext *ctx = new CdbStackTraceContext(dumper);
|
||||
hr = cif->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
|
||||
if (FAILED(hr)) {
|
||||
delete ctx;
|
||||
@@ -77,7 +81,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
|
||||
|
||||
CdbStackTraceContext::~CdbStackTraceContext()
|
||||
{
|
||||
qDeleteAll(m_symbolContexts);
|
||||
qDeleteAll(m_frameContexts);
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
|
||||
@@ -85,8 +89,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << frameCount;
|
||||
|
||||
m_symbolContexts.resize(frameCount);
|
||||
qFill(m_symbolContexts, static_cast<CdbSymbolGroupContext*>(0));
|
||||
m_frameContexts.resize(frameCount);
|
||||
qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
|
||||
|
||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
@@ -114,28 +118,28 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
||||
return true;
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupContextAt(int index, QString *errorMessage)
|
||||
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
|
||||
{
|
||||
// Create a symbol group on demand
|
||||
// Create a frame on demand
|
||||
if (debugCDB)
|
||||
qDebug() << Q_FUNC_INFO << index;
|
||||
|
||||
if (index < 0 || index >= m_symbolContexts.size()) {
|
||||
if (index < 0 || index >= m_frameContexts.size()) {
|
||||
*errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_symbolContexts.size());
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_frameContexts.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_symbolContexts.at(index))
|
||||
return m_symbolContexts.at(index);
|
||||
if (m_frameContexts.at(index))
|
||||
return m_frameContexts.at(index);
|
||||
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
|
||||
if (!sg)
|
||||
return 0;
|
||||
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage);
|
||||
if (!sc)
|
||||
return 0; \
|
||||
m_symbolContexts[index] = sc;
|
||||
return sc;
|
||||
return 0;
|
||||
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
|
||||
m_frameContexts[index] = fr;
|
||||
return fr;
|
||||
}
|
||||
|
||||
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
|
||||
|
@@ -36,6 +36,7 @@
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextStream;
|
||||
@@ -46,21 +47,23 @@ namespace Internal {
|
||||
|
||||
struct CdbComInterfaces;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbStackFrameContext;
|
||||
class CdbDumperHelper;
|
||||
|
||||
/* Context representing a break point stack consisting of several frames.
|
||||
* Maintains an on-demand constructed list of CdbSymbolGroupContext
|
||||
* Maintains an on-demand constructed list of CdbStackFrameContext
|
||||
* containining the local variables of the stack. */
|
||||
|
||||
class CdbStackTraceContext
|
||||
{
|
||||
Q_DISABLE_COPY(CdbStackTraceContext)
|
||||
|
||||
explicit CdbStackTraceContext(CdbComInterfaces *cif);
|
||||
explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
|
||||
public:
|
||||
enum { maxFrames = 100 };
|
||||
|
||||
~CdbStackTraceContext();
|
||||
static CdbStackTraceContext *create(CdbComInterfaces *cif,
|
||||
static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
unsigned long threadid,
|
||||
QString *errorMessage);
|
||||
|
||||
@@ -70,7 +73,7 @@ public:
|
||||
// Top-Level instruction offset for disassembler
|
||||
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
||||
|
||||
CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage);
|
||||
CdbStackFrameContext*frameContextAt(int index, QString *errorMessage);
|
||||
|
||||
// Format for logging
|
||||
void format(QTextStream &str) const;
|
||||
@@ -80,10 +83,11 @@ private:
|
||||
bool init(unsigned long frameCount, QString *errorMessage);
|
||||
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
|
||||
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
CdbComInterfaces *m_cif;
|
||||
|
||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||
QVector <CdbSymbolGroupContext*> m_symbolContexts;
|
||||
QVector <CdbStackFrameContext*> m_frameContexts;
|
||||
QList<StackFrame> m_frames;
|
||||
ULONG64 m_instructionOffset;
|
||||
};
|
||||
|
@@ -200,16 +200,6 @@ QString CdbSymbolGroupContext::toString(bool verbose) const
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p)
|
||||
{
|
||||
if (p.Flags & (DEBUG_SYMBOL_IS_LOCAL|DEBUG_SYMBOL_IS_ARGUMENT))
|
||||
return true;
|
||||
// Do not display static members.
|
||||
if (p.Flags & DEBUG_SYMBOL_READ_ONLY)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
CdbSymbolGroupContext::SymbolState CdbSymbolGroupContext::symbolState(unsigned long index) const
|
||||
{
|
||||
return getSymbolState(m_symbolParameters.at(index));
|
||||
@@ -499,144 +489,26 @@ QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, CIDebug
|
||||
return formatArrayHelper(dv.RawBytes, 24, integerBase);
|
||||
}
|
||||
|
||||
// - Watch model functions
|
||||
class WatchDataBackInserter {
|
||||
public:
|
||||
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
|
||||
|
||||
WatchDataBackInserter & operator*() { return *this; }
|
||||
WatchDataBackInserter &operator=(const WatchData &wd) {
|
||||
m_wh.push_back(wd);
|
||||
return *this;
|
||||
}
|
||||
WatchDataBackInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
QList<WatchData> &m_wh;
|
||||
};
|
||||
|
||||
static bool insertChildrenRecursion(const QString &iname,
|
||||
CdbSymbolGroupContext *sg,
|
||||
WatchHandler *watchHandler,
|
||||
int maxRecursionLevel,
|
||||
int level,
|
||||
QString *errorMessage,
|
||||
int *childCount = 0);
|
||||
|
||||
// Insert a symbol (and its first level children depending on forceRecursion)
|
||||
static bool insertSymbolRecursion(WatchData wd,
|
||||
CdbSymbolGroupContext *sg,
|
||||
WatchHandler *watchHandler,
|
||||
int maxRecursionLevel,
|
||||
int level,
|
||||
QString *errorMessage)
|
||||
{
|
||||
// Find out whether to recurse (has children or at least knows it has children)
|
||||
// Open next level if specified by recursion depth or child is already expanded
|
||||
// (Sometimes, some root children are already expanded after creating the context).
|
||||
const bool hasChildren = wd.childCount > 0 || wd.isChildrenNeeded();
|
||||
const bool recurse = hasChildren && (level < maxRecursionLevel || sg->isExpanded(wd.iname));
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << wd.iname << "level=" << level << "recurse=" << recurse;
|
||||
bool rc = true;
|
||||
if (recurse) { // Determine number of children and indicate in model
|
||||
int childCount;
|
||||
rc = insertChildrenRecursion(wd.iname, sg, watchHandler, maxRecursionLevel, level, errorMessage, &childCount);
|
||||
if (rc) {
|
||||
wd.setChildCount(childCount);
|
||||
wd.setChildrenUnneeded();
|
||||
}
|
||||
} else {
|
||||
// No further recursion at this level, pretend entry is complete
|
||||
if (wd.isChildrenNeeded()) {
|
||||
wd.setChildCount(1);
|
||||
wd.setChildrenUnneeded();
|
||||
}
|
||||
}
|
||||
if (debug)
|
||||
qDebug() << " INSERTING: at " << level << wd.toString();
|
||||
watchHandler->insertData(wd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Insert the children of prefix.
|
||||
static bool insertChildrenRecursion(const QString &iname,
|
||||
CdbSymbolGroupContext *sg,
|
||||
WatchHandler *watchHandler,
|
||||
int maxRecursionLevel,
|
||||
int level,
|
||||
QString *errorMessage,
|
||||
int *childCountPtr)
|
||||
bool CdbSymbolGroupContext::debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value)
|
||||
{
|
||||
if (debug > 1)
|
||||
qDebug() << Q_FUNC_INFO << '\n' << iname << level;
|
||||
|
||||
QList<WatchData> watchList;
|
||||
// This implicitly enforces expansion
|
||||
if (!sg->getChildSymbols(iname, WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
|
||||
const int childCount = watchList.size();
|
||||
if (childCountPtr)
|
||||
*childCountPtr = childCount;
|
||||
int succeededChildCount = 0;
|
||||
for (int c = 0; c < childCount; c++) {
|
||||
const WatchData &wd = watchList.at(c);
|
||||
if (wd.isValid()) { // We sometimes get empty names for deeply nested data
|
||||
if (!insertSymbolRecursion(wd, sg, watchHandler, maxRecursionLevel, level + 1, errorMessage))
|
||||
return false;
|
||||
succeededChildCount++;
|
||||
} else {
|
||||
const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'.").
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(wd.type, iname);
|
||||
qWarning("%s\n", qPrintable(msg));
|
||||
}
|
||||
*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;
|
||||
}
|
||||
if (childCountPtr)
|
||||
*childCountPtr = succeededChildCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg,
|
||||
WatchHandler *watchHandler,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug() << "###" << Q_FUNC_INFO;
|
||||
|
||||
// Insert root items
|
||||
QList<WatchData> watchList;
|
||||
if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
|
||||
foreach(const WatchData &wd, watchList)
|
||||
if (!insertSymbolRecursion(wd, sg, watchHandler, 0, 0, errorMessage))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbSymbolGroupContext::completeModel(CdbSymbolGroupContext *sg,
|
||||
const QList<WatchData> &incompleteLocals,
|
||||
WatchHandler *watchHandler,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDB)
|
||||
qDebug().nospace() << "###>" << Q_FUNC_INFO << ' ' << incompleteLocals.size() << '\n';
|
||||
// The view reinserts any node being expanded with flag 'ChildrenNeeded'.
|
||||
// Recurse down one level in context unless this is already the case.
|
||||
foreach(WatchData wd, incompleteLocals) {
|
||||
const bool contextExpanded = sg->isExpanded(wd.iname);
|
||||
if (debug)
|
||||
qDebug() << " " << wd.iname << "CE=" << contextExpanded;
|
||||
if (contextExpanded) { // You know that already.
|
||||
wd.setChildrenUnneeded();
|
||||
watchHandler->insertData(wd);
|
||||
} else {
|
||||
if (!insertSymbolRecursion(wd, sg, watchHandler, 1, 0, errorMessage))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#define CDBSYMBOLGROUPCONTEXT_H
|
||||
|
||||
#include "cdbcom.h"
|
||||
#include "watchhandler.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
@@ -72,11 +73,13 @@ public:
|
||||
bool assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
|
||||
static bool populateModelInitially(CdbSymbolGroupContext *sg, WatchHandler *wh, QString *errorMessage);
|
||||
template <class OutputIterator>
|
||||
static bool populateModelInitially(CdbSymbolGroupContext *sg, OutputIterator it, QString *errorMessage);
|
||||
|
||||
template <class OutputIterator>
|
||||
static bool completeModel(CdbSymbolGroupContext *sg,
|
||||
const QList<WatchData> &incompleteLocals,
|
||||
WatchHandler *wh,
|
||||
OutputIterator it,
|
||||
QString *errorMessage);
|
||||
|
||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||
@@ -92,6 +95,7 @@ public:
|
||||
|
||||
// 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);
|
||||
@@ -125,26 +129,25 @@ private:
|
||||
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
|
||||
};
|
||||
|
||||
template <class OutputIterator>
|
||||
bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long parentId;
|
||||
if (!getChildSymbolsPosition(prefix, &start, &parentId, errorMessage))
|
||||
return false;
|
||||
// Skip over expanded children
|
||||
const unsigned long end = m_symbolParameters.size();
|
||||
for (unsigned long s = start; s < end; ++s) {
|
||||
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(s);
|
||||
if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
|
||||
*it = symbolAt(s);
|
||||
++it;
|
||||
}
|
||||
// Helper to a sequence of WatchData into a list.
|
||||
class WatchDataBackInserter {
|
||||
public:
|
||||
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
|
||||
|
||||
inline WatchDataBackInserter & operator*() { return *this; }
|
||||
inline WatchDataBackInserter &operator=(const WatchData &wd) {
|
||||
m_wh.push_back(wd);
|
||||
return *this;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inline WatchDataBackInserter &operator++() { return *this; }
|
||||
|
||||
private:
|
||||
QList<WatchData> &m_wh;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#include "cdbsymbolgroupcontext_tpl.h"
|
||||
|
||||
#endif // CDBSYMBOLGROUPCONTEXT_H
|
||||
|
@@ -146,6 +146,7 @@ private:
|
||||
friend class ScriptEngine;
|
||||
friend struct CdbDebugEnginePrivate;
|
||||
friend class CdbDumperHelper;
|
||||
friend class CdbExceptionLoggerEventCallback;
|
||||
|
||||
// called from the engines after successful startup
|
||||
virtual void notifyInferiorStopRequested() = 0;
|
||||
|
@@ -55,6 +55,7 @@
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QApplication>
|
||||
@@ -2913,44 +2914,6 @@ void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
|
||||
|
||||
static const QString strNotInScope = QLatin1String("<not in scope>");
|
||||
|
||||
static QString quoteUnprintableLatin1(const QByteArray &ba)
|
||||
{
|
||||
QString res;
|
||||
char buf[10];
|
||||
for (int i = 0, n = ba.size(); i != n; ++i) {
|
||||
unsigned char c = ba.at(i);
|
||||
if (isprint(c)) {
|
||||
res += c;
|
||||
} else {
|
||||
qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
|
||||
res += buf;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static QString decodeData(QByteArray ba, int encoding)
|
||||
{
|
||||
switch (encoding) {
|
||||
case 0: // unencoded 8 bit data
|
||||
return quoteUnprintableLatin1(ba);
|
||||
case 1: // base64 encoded 8 bit data, used for QByteArray
|
||||
ba = QByteArray::fromBase64(ba);
|
||||
return '"' + quoteUnprintableLatin1(ba) + '"';
|
||||
case 2: // base64 encoded 16 bit data, used for QString
|
||||
ba = QByteArray::fromBase64(ba);
|
||||
return '"' + QString::fromUtf16((ushort *)ba.data(), ba.size() / 2) + '"';
|
||||
case 3: // base64 encoded 32 bit data
|
||||
ba = QByteArray::fromBase64(ba);
|
||||
return '"' + QString::fromUcs4((uint *)ba.data(), ba.size() / 4) + '"';
|
||||
break;
|
||||
case 4: // base64 encoded 16 bit data, without quotes (see 2)
|
||||
ba = QByteArray::fromBase64(ba);
|
||||
return QString::fromUtf16((ushort *)ba.data(), ba.size() / 2);
|
||||
}
|
||||
return "<Encoding error>";
|
||||
}
|
||||
|
||||
static void setWatchDataValue(WatchData &data, const GdbMi &mi,
|
||||
int encoding = 0)
|
||||
{
|
||||
@@ -3041,15 +3004,7 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
|
||||
return false;
|
||||
|
||||
// simple types
|
||||
if (m_availableSimpleDebuggingHelpers.contains(type))
|
||||
return true;
|
||||
|
||||
// templates
|
||||
QString tmplate;
|
||||
QString inner;
|
||||
if (!extractTemplate(type, &tmplate, &inner))
|
||||
return false;
|
||||
return m_availableSimpleDebuggingHelpers.contains(tmplate);
|
||||
return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
|
||||
}
|
||||
|
||||
void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
|
||||
@@ -3080,145 +3035,33 @@ void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
|
||||
}
|
||||
WatchData data = data0;
|
||||
QTC_ASSERT(!data.exp.isEmpty(), return);
|
||||
QString tmplate;
|
||||
QString inner;
|
||||
bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
|
||||
QStringList inners = inner.split('@');
|
||||
if (inners.at(0).isEmpty())
|
||||
inners.clear();
|
||||
for (int i = 0; i != inners.size(); ++i)
|
||||
inners[i] = inners[i].simplified();
|
||||
|
||||
QString outertype = isTemplate ? tmplate : data.type;
|
||||
// adjust the data extract
|
||||
if (outertype == m_namespace + "QWidget")
|
||||
outertype = m_namespace + "QObject";
|
||||
|
||||
QString extraArgs[4];
|
||||
extraArgs[0] = "0";
|
||||
extraArgs[1] = "0";
|
||||
extraArgs[2] = "0";
|
||||
extraArgs[3] = "0";
|
||||
|
||||
int extraArgCount = 0;
|
||||
|
||||
// "generic" template dumpers: passing sizeof(argument)
|
||||
// gives already most information the dumpers need
|
||||
foreach (const QString &arg, inners)
|
||||
extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
|
||||
|
||||
// in rare cases we need more or less:
|
||||
if (outertype == m_namespace + "QObject") {
|
||||
extraArgs[0] = "(char*)&((('"
|
||||
+ m_namespace + "QObjectPrivate'*)&"
|
||||
+ data.exp + ")->children)-(char*)&" + data.exp;
|
||||
} else if (outertype == m_namespace + "QVector") {
|
||||
extraArgs[1] = "(char*)&(("
|
||||
+ data.exp + ").d->array)-(char*)" + data.exp + ".d";
|
||||
} else if (outertype == m_namespace + "QObjectSlot"
|
||||
|| outertype == m_namespace + "QObjectSignal") {
|
||||
// we need the number out of something like
|
||||
// iname="local.ob.slots.2" // ".deleteLater()"?
|
||||
int pos = data.iname.lastIndexOf('.');
|
||||
QString slotNumber = data.iname.mid(pos + 1);
|
||||
QTC_ASSERT(slotNumber.toInt() != -1, /**/);
|
||||
extraArgs[0] = slotNumber;
|
||||
} else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
|
||||
QString nodetype;
|
||||
if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
|
||||
nodetype = m_namespace + "QMapNode";
|
||||
nodetype += data.type.mid(outertype.size());
|
||||
} else {
|
||||
// FIXME: doesn't work for QMultiMap
|
||||
nodetype = data.type + "::Node";
|
||||
}
|
||||
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
|
||||
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
|
||||
extraArgs[2] = sizeofTypeExpression(nodetype);
|
||||
extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
|
||||
} else if (outertype == m_namespace + "QMapNode") {
|
||||
extraArgs[2] = sizeofTypeExpression(data.type);
|
||||
extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
|
||||
} else if (outertype == "std::vector" || outertype == "vector") {
|
||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
||||
if (inners.at(0) == "bool") {
|
||||
outertype = "std::vector::bool";
|
||||
} else {
|
||||
//extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
|
||||
//extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
|
||||
}
|
||||
} else if (outertype == "std::deque" || outertype == "deque") {
|
||||
// remove 'std::allocator<...>':
|
||||
extraArgs[1] = "0";
|
||||
} else if (outertype == "std::stack" || outertype == "stack") {
|
||||
// remove 'std::allocator<...>':
|
||||
extraArgs[1] = "0";
|
||||
} else if (outertype == "std::set" || outertype == "set") {
|
||||
// remove 'std::less<...>':
|
||||
extraArgs[1] = "0";
|
||||
// remove 'std::allocator<...>':
|
||||
extraArgs[2] = "0";
|
||||
} else if (outertype == "std::map" || outertype == "map") {
|
||||
// We don't want the comparator and the allocator confuse gdb.
|
||||
// But we need the offset of the second item in the value pair.
|
||||
// We read the type of the pair from the allocator argument because
|
||||
// that gets the constness "right" (in the sense that gdb can
|
||||
// read it back;
|
||||
QString pairType = inners.at(3);
|
||||
// remove 'std::allocator<...>':
|
||||
pairType = pairType.mid(15, pairType.size() - 15 - 2);
|
||||
extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
|
||||
extraArgs[3] = "0";
|
||||
} else if (outertype == "std::basic_string" || outertype == "basic_string") {
|
||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
||||
if (inners.at(0) == "char") {
|
||||
outertype = "std::string";
|
||||
} else if (inners.at(0) == "wchar_t") {
|
||||
outertype = "std::wstring";
|
||||
}
|
||||
extraArgs[0] = "0";
|
||||
extraArgs[1] = "0";
|
||||
extraArgs[2] = "0";
|
||||
extraArgs[3] = "0";
|
||||
}
|
||||
QByteArray params;
|
||||
QStringList extraArgs;
|
||||
const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
|
||||
m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, ¶ms, &extraArgs);
|
||||
|
||||
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
||||
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
||||
int protocol = 2;
|
||||
const int protocol = 2;
|
||||
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
||||
|
||||
QString addr;
|
||||
if (data.addr.startsWith("0x"))
|
||||
addr = "(void*)" + data.addr;
|
||||
else
|
||||
addr = "&(" + data.exp + ")";
|
||||
|
||||
QByteArray params;
|
||||
params.append(outertype.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.iname.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.exp.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(inner.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.iname.toUtf8());
|
||||
params.append('\0');
|
||||
if (data.addr.startsWith(QLatin1String("0x"))) {
|
||||
addr = QLatin1String("(void*)") + data.addr;
|
||||
} else {
|
||||
addr = QLatin1String("&(") + data.exp + QLatin1Char(')');
|
||||
}
|
||||
|
||||
sendWatchParameters(params);
|
||||
|
||||
QString cmd ="call "
|
||||
+ QString("(void*)qDumpObjectData440(")
|
||||
+ QString::number(protocol)
|
||||
+ ',' + "%1+1" // placeholder for token
|
||||
+ ',' + addr
|
||||
+ ',' + (dumpChildren ? "1" : "0")
|
||||
+ ',' + extraArgs[0]
|
||||
+ ',' + extraArgs[1]
|
||||
+ ',' + extraArgs[2]
|
||||
+ ',' + extraArgs[3] + ')';
|
||||
QString cmd;
|
||||
QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
|
||||
protocol << ',' << "%1+1" // placeholder for token
|
||||
<<',' << addr << ',' << (dumpChildren ? "1" : "0")
|
||||
<< ',' << extraArgs.join(QString(QLatin1Char(','))) << ')';
|
||||
|
||||
//qDebug() << "CMD: " << cmd;
|
||||
qDebug() << "CMD: " << cmd;
|
||||
|
||||
QVariant var;
|
||||
var.setValue(data);
|
||||
@@ -3451,6 +3294,7 @@ void GdbEngine::updateWatchModel2()
|
||||
|
||||
void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
||||
{
|
||||
m_dumperHelper.clear();
|
||||
//qDebug() << "DATA DUMPER TRIAL:" << record.toString();
|
||||
GdbMi output = record.data.findChild("consolestreamoutput");
|
||||
QByteArray out = output.data();
|
||||
@@ -3464,23 +3308,27 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
||||
GdbMi contents;
|
||||
contents.fromString(out);
|
||||
GdbMi simple = contents.findChild("dumpers");
|
||||
m_namespace = contents.findChild("namespace").data();
|
||||
|
||||
m_dumperHelper.setQtNamespace(contents.findChild("namespace").data());
|
||||
GdbMi qtversion = contents.findChild("qtversion");
|
||||
int qtv = 0;
|
||||
if (qtversion.children().size() == 3) {
|
||||
m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
|
||||
qtv = (qtversion.childAt(0).data().toInt() << 16)
|
||||
+ (qtversion.childAt(1).data().toInt() << 8)
|
||||
+ qtversion.childAt(2).data().toInt();
|
||||
//qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
|
||||
} else {
|
||||
m_qtVersion = 0;
|
||||
}
|
||||
m_dumperHelper.setQtVersion(qtv);
|
||||
|
||||
//qDebug() << "CONTENTS: " << contents.toString();
|
||||
//qDebug() << "SIMPLE DUMPERS: " << simple.toString();
|
||||
m_availableSimpleDebuggingHelpers.clear();
|
||||
|
||||
QStringList availableSimpleDebuggingHelpers;
|
||||
foreach (const GdbMi &item, simple.children())
|
||||
m_availableSimpleDebuggingHelpers.append(item.data());
|
||||
if (m_availableSimpleDebuggingHelpers.isEmpty()) {
|
||||
availableSimpleDebuggingHelpers.append(item.data());
|
||||
m_dumperHelper.parseQueryTypes(availableSimpleDebuggingHelpers, QtDumperHelper::GdbDebugger);
|
||||
|
||||
if (availableSimpleDebuggingHelpers.isEmpty()) {
|
||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||
q->showStatusMessage(tr("Debugging helpers not found."));
|
||||
//QMessageBox::warning(q->mainWindow(),
|
||||
@@ -3494,8 +3342,9 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
|
||||
} else {
|
||||
m_debuggingHelperState = DebuggingHelperAvailable;
|
||||
q->showStatusMessage(tr("%1 custom dumpers found.")
|
||||
.arg(m_availableSimpleDebuggingHelpers.size()));
|
||||
.arg(m_dumperHelper.typeCount()));
|
||||
}
|
||||
qDebug() << m_dumperHelper.toString(true);
|
||||
//qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDebuggingHelpers;
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include "idebuggerengine.h"
|
||||
#include "gdbmi.h"
|
||||
#include "outputcollector.h"
|
||||
#include "watchutils.h"
|
||||
|
||||
#include <consoleprocess.h>
|
||||
|
||||
@@ -342,9 +343,7 @@ private:
|
||||
QString m_editedData;
|
||||
int m_pendingRequests;
|
||||
|
||||
QStringList m_availableSimpleDebuggingHelpers;
|
||||
QString m_namespace; // namespace used in "namespaced Qt";
|
||||
int m_qtVersion; // Qt version used in the debugged program
|
||||
QtDumperHelper m_dumperHelper;
|
||||
|
||||
DebuggingHelperState m_debuggingHelperState;
|
||||
QList<GdbMi> m_currentFunctionArgs;
|
||||
|
@@ -78,6 +78,7 @@ static int watcherCounter = 0;
|
||||
WatchData::WatchData() :
|
||||
childCount(-1),
|
||||
valuedisabled(false),
|
||||
source(0),
|
||||
state(InitialState),
|
||||
parentIndex(-1),
|
||||
row(-1),
|
||||
|
@@ -124,6 +124,7 @@ public:
|
||||
private:
|
||||
|
||||
public:
|
||||
int source; // Used by some debuggers (CDB) to tell where it originates from (dumper or symbol evaluation)
|
||||
int state;
|
||||
|
||||
// Model
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -31,15 +31,17 @@
|
||||
#define WATCHUTILS_H
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
|
||||
QString dotEscape(QString str);
|
||||
QString currentTime();
|
||||
bool isSkippableFunction(const QString &funcName, const QString &fileName);
|
||||
@@ -64,8 +66,131 @@ QString extractTypeFromPTypeOutput(const QString &str);
|
||||
bool isIntOrFloatType(const QString &type);
|
||||
QString sizeofTypeExpression(const QString &type);
|
||||
|
||||
// Parse 'query' (1) protocol response of the custom dumpers
|
||||
bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace);
|
||||
// Decode string data as returned by the dumper helpers.
|
||||
QString decodeData(const QByteArray &baIn, int encoding);
|
||||
|
||||
// Result of a dumper call.
|
||||
struct QtDumperResult
|
||||
{
|
||||
struct Child {
|
||||
Child();
|
||||
|
||||
int valueEncoded;
|
||||
QString name;
|
||||
QString address;
|
||||
QByteArray value;
|
||||
};
|
||||
|
||||
QtDumperResult();
|
||||
void clear();
|
||||
QList<WatchData> toWatchData(int source = 0) const;
|
||||
|
||||
QString iname;
|
||||
QString address;
|
||||
QString type;
|
||||
QByteArray value;
|
||||
int valueEncoded;
|
||||
bool valuedisabled;
|
||||
int childCount;
|
||||
bool internal;
|
||||
QString childType;
|
||||
QList <Child> children;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug in, const QtDumperResult &d);
|
||||
|
||||
/* Attempt to put common code of the dumper handling into a helper
|
||||
* class.
|
||||
* "Custom dumper" is a library compiled against the current
|
||||
* Qt containing functions to evaluate values of Qt classes
|
||||
* (such as QString, taking pointers to their addresses).
|
||||
* The library must be loaded into the debuggee.
|
||||
* It provides a function that takes input from an input buffer
|
||||
* and some parameters and writes output into an output buffer.
|
||||
* Parameter 1 is the protocol:
|
||||
* 1) Query. Fills output buffer with known types, Qt version and namespace.
|
||||
* This information is parsed and stored by this class (special type
|
||||
* enumeration).
|
||||
* 2) Evaluate symbol, taking address and some additional parameters
|
||||
* depending on type. */
|
||||
|
||||
class QtDumperHelper {
|
||||
public:
|
||||
enum Debugger {
|
||||
GdbDebugger, // Can evalulate expressions in function calls
|
||||
CdbDebugger // Can only handle scalar, simple types in function calls
|
||||
};
|
||||
|
||||
enum Type {
|
||||
UnknownType,
|
||||
SupportedType, // A type that requires no special handling by the dumper
|
||||
// Below types require special handling
|
||||
QObjectType, QWidgetType, QObjectSlotType, QObjectSignalType,
|
||||
QVectorType, QMapType, QMultiMapType, QMapNodeType,
|
||||
StdVectorType, StdDequeType, StdSetType,StdMapType, StdStackType,
|
||||
StdStringType
|
||||
};
|
||||
|
||||
// Type/Parameter struct required for building a value query
|
||||
struct TypeData {
|
||||
TypeData();
|
||||
void clear();
|
||||
|
||||
Type type;
|
||||
bool isTemplate;
|
||||
QString tmplate;
|
||||
QString inner;
|
||||
};
|
||||
|
||||
QtDumperHelper();
|
||||
void clear();
|
||||
|
||||
int typeCount() const;
|
||||
// Look up a simple, non-template type
|
||||
Type simpleType(const QString &simpleType) const;
|
||||
// Look up a (potentially) template type and fill parameter struct
|
||||
TypeData typeData(const QString &typeName) const;
|
||||
Type type(const QString &typeName) const;
|
||||
|
||||
int qtVersion() const;
|
||||
QString qtVersionString() const;
|
||||
void setQtVersion(int v);
|
||||
void setQtVersion(const QString &v);
|
||||
|
||||
QString qtNamespace() const;
|
||||
void setQtNamespace(const QString &qtNamespace);
|
||||
|
||||
// Complete parse of "query" (protocol 1) response from debuggee buffer.
|
||||
// 'data' excludes the leading indicator character.
|
||||
bool parseQuery(const char *data, Debugger debugger);
|
||||
// Set up from pre-parsed type list
|
||||
void parseQueryTypes(const QStringList &l, Debugger debugger);
|
||||
|
||||
// Determine the parameters required for an "evaluate" (protocol 2) call
|
||||
void evaluationParameters(const WatchData &data,
|
||||
const TypeData &td,
|
||||
Debugger debugger,
|
||||
QByteArray *inBuffer,
|
||||
QStringList *extraParameters) const;
|
||||
|
||||
// Parse the value response (protocol 2) from debuggee buffer.
|
||||
// 'data' excludes the leading indicator character.
|
||||
static bool parseValue(const char *data, QtDumperResult *r);
|
||||
|
||||
static bool needsExpressionSyntax(Type t);
|
||||
|
||||
QString toString(bool debug = false) const;
|
||||
|
||||
private:
|
||||
typedef QMap<QString, Type> NameTypeMap;
|
||||
|
||||
// Look up a simple (namespace) type
|
||||
static Type specialType(QString s);
|
||||
|
||||
NameTypeMap m_nameTypeMap;
|
||||
int m_qtVersion;
|
||||
QString m_qtNamespace;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
Reference in New Issue
Block a user