Initial work on dumpers for CDB.

Add dumper calls, parser etc. Catch crashes
in the dumper functions.
This commit is contained in:
Friedemann Kleint
2009-04-29 14:15:09 +02:00
parent 3cb783f7fc
commit 72a183777e
22 changed files with 1920 additions and 628 deletions

View File

@@ -60,6 +60,10 @@ int qtGhVersion = QT_VERSION;
# include <QtGui/QImage> # include <QtGui/QImage>
#endif #endif
#ifdef Q_OS_WIN
# include <windows.h>
#endif
#include <list> #include <list>
#include <map> #include <map>
#include <string> #include <string>
@@ -231,11 +235,17 @@ static QByteArray stripPointerType(QByteArray type)
} }
// This is used to abort evaluation of custom data dumpers in a "coordinated" // 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 // 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. // 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 // This is just fine provided we only _read_ memory in the custom handlers
// below. // 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; volatile int qProvokeSegFaultHelper;
@@ -269,11 +279,16 @@ static bool startsWith(const char *s, const char *t)
return qstrncmp(s, t, qstrlen(t)) == 0; return qstrncmp(s, t, qstrlen(t)) == 0;
} }
// provoke segfault when address is not readable // 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 qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
# define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0) # define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
// provoke segfault unconditionally #endif
#define qCheck(b) do { if (!(b)) qProvokeSegFaultHelper = *(char*)0; } while (0)
const char *stripNamespace(const char *type) const char *stripNamespace(const char *type)
{ {
@@ -692,11 +707,14 @@ void QDumper::putEllipsis()
#define TT(type, value) \ #define TT(type, value) \
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>" "<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, "iname", d.iname);
P(d, "addr", d.data); 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, "type", d.outertype);
P(d, "numchild", "0"); P(d, "numchild", "0");
d.disarm(); 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); const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
@@ -848,6 +866,7 @@ static void qDumpQByteArray(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQDateTime(QDumper &d) 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); QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
const char *keyType = d.templateParameters[0]; const char *keyType = d.templateParameters[0];
@@ -1078,7 +1097,7 @@ static void qDumpQHash(QDumper &d)
int n = h->size; int n = h->size;
if (n < 0) if (n < 0)
qCheck(false); return false;
if (n > 0) { if (n > 0) {
qCheckPointer(h->fakeNext); qCheckPointer(h->fakeNext);
qCheckPointer(*h->buckets); qCheckPointer(*h->buckets);
@@ -1127,6 +1146,7 @@ static void qDumpQHash(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQHashNode(QDumper &d) static void qDumpQHashNode(QDumper &d)
@@ -1177,23 +1197,25 @@ static void qDumpQImage(QDumper &d)
#endif #endif
} }
static void qDumpQList(QDumper &d) static bool qDumpQList(QDumper &d)
{ {
// This uses the knowledge that QList<T> has only a single member // This uses the knowledge that QList<T> has only a single member
// of type union { QListData p; QListData::Data *d; }; // of type union { QListData p; QListData::Data *d; };
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data); const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
const QListData::Data *pdata = const QListData::Data *pdata =
*reinterpret_cast<const QListData::Data* const*>(d.data); *reinterpret_cast<const QListData::Data* const*>(d.data);
qCheckAccess(pdata);
int nn = ldata.size(); int nn = ldata.size();
if (nn < 0) if (nn < 0)
qCheck(false); return false;
if (nn > 0) { if (nn > 0) {
qCheckAccess(ldata.d->array); qCheckAccess(ldata.d->array);
//qCheckAccess(ldata.d->array[0]); //qCheckAccess(ldata.d->array[0]);
//qCheckAccess(ldata.d->array[nn - 1]); //qCheckAccess(ldata.d->array[nn - 1]);
#if QT_VERSION >= 0x040400 #if QT_VERSION >= 0x040400
if (ldata.d->ref._q_value <= 0) if (ldata.d->ref._q_value <= 0)
qCheck(false); return false;
#endif #endif
} }
@@ -1252,9 +1274,10 @@ static void qDumpQList(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); 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 // This uses the knowledge that QLinkedList<T> has only a single member
// of type union { QLinkedListData *d; QLinkedListNode<T> *e; }; // of type union { QLinkedListData *d; QLinkedListNode<T> *e; };
@@ -1262,7 +1285,7 @@ static void qDumpQLinkedList(QDumper &d)
reinterpret_cast<const QLinkedListData*>(deref(d.data)); reinterpret_cast<const QLinkedListData*>(deref(d.data));
int nn = ldata->size; int nn = ldata->size;
if (nn < 0) if (nn < 0)
qCheck(false); return false;
int n = nn; int n = nn;
P(d, "value", "<" << n << " items>"); P(d, "value", "<" << n << " items>");
@@ -1294,6 +1317,7 @@ static void qDumpQLinkedList(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQLocale(QDumper &d) static void qDumpQLocale(QDumper &d)
@@ -1341,7 +1365,7 @@ static void qDumpQLocale(QDumper &d)
d.disarm(); d.disarm();
} }
static void qDumpQMapNode(QDumper &d) static bool qDumpQMapNode(QDumper &d)
{ {
const QMapData *h = reinterpret_cast<const QMapData *>(d.data); const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
const char *keyType = d.templateParameters[0]; const char *keyType = d.templateParameters[0];
@@ -1375,9 +1399,10 @@ static void qDumpQMapNode(QDumper &d)
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQMap(QDumper &d) static bool qDumpQMap(QDumper &d)
{ {
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data); QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
const char *keyType = d.templateParameters[0]; const char *keyType = d.templateParameters[0];
@@ -1386,7 +1411,7 @@ static void qDumpQMap(QDumper &d)
int n = h->size; int n = h->size;
if (n < 0) if (n < 0)
qCheck(false); return false;
if (n > 0) { if (n > 0) {
qCheckAccess(h->backward); qCheckAccess(h->backward);
qCheckAccess(h->forward[0]); qCheckAccess(h->forward[0]);
@@ -1455,11 +1480,12 @@ static void qDumpQMap(QDumper &d)
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQMultiMap(QDumper &d) static bool qDumpQMultiMap(QDumper &d)
{ {
qDumpQMap(d); return qDumpQMap(d);
} }
static void qDumpQModelIndex(QDumper &d) static void qDumpQModelIndex(QDumper &d)
@@ -1890,7 +1916,7 @@ static void qDumpQPixmap(QDumper &d)
#endif #endif
} }
static void qDumpQSet(QDumper &d) static bool qDumpQSet(QDumper &d)
{ {
// This uses the knowledge that QHash<T> has only a single member // This uses the knowledge that QHash<T> has only a single member
// of union { QHashData *d; QHashNode<Key, T> *e; }; // of union { QHashData *d; QHashNode<Key, T> *e; };
@@ -1899,7 +1925,7 @@ static void qDumpQSet(QDumper &d)
int n = hd->size; int n = hd->size;
if (n < 0) if (n < 0)
qCheck(false); return false;
if (n > 0) { if (n > 0) {
qCheckAccess(node); qCheckAccess(node);
qCheckPointer(node->next); qCheckPointer(node->next);
@@ -1933,6 +1959,7 @@ static void qDumpQSet(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQSharedPointer(QDumper &d) static void qDumpQSharedPointer(QDumper &d)
@@ -1959,7 +1986,7 @@ static void qDumpQSharedPointer(QDumper &d)
d.disarm(); d.disarm();
} }
static void qDumpQString(QDumper &d) static bool qDumpQString(QDumper &d)
{ {
const QString &str = *reinterpret_cast<const QString *>(d.data); const QString &str = *reinterpret_cast<const QString *>(d.data);
@@ -1975,14 +2002,15 @@ static void qDumpQString(QDumper &d)
P(d, "numchild", "0"); P(d, "numchild", "0");
d.disarm(); d.disarm();
return true;
} }
static void qDumpQStringList(QDumper &d) static bool qDumpQStringList(QDumper &d)
{ {
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data); const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
int n = list.size(); int n = list.size();
if (n < 0) if (n < 0)
qCheck(false); return false;
if (n > 0) { if (n > 0) {
qCheckAccess(&list.front()); qCheckAccess(&list.front());
qCheckAccess(&list.back()); qCheckAccess(&list.back());
@@ -2009,6 +2037,7 @@ static void qDumpQStringList(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpQTextCodec(QDumper &d) static void qDumpQTextCodec(QDumper &d)
@@ -2109,7 +2138,7 @@ static void qDumpQVariant(QDumper &d)
d.disarm(); d.disarm();
} }
static void qDumpQVector(QDumper &d) static bool qDumpQVector(QDumper &d)
{ {
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data); QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
@@ -2117,7 +2146,7 @@ static void qDumpQVector(QDumper &d)
// from asking for unavailable child details // from asking for unavailable child details
int nn = v->size; int nn = v->size;
if (nn < 0) if (nn < 0)
qCheck(false); return false;
if (nn > 0) { if (nn > 0) {
//qCheckAccess(&vec.front()); //qCheckAccess(&vec.front());
//qCheckAccess(&vec.back()); //qCheckAccess(&vec.back());
@@ -2149,9 +2178,10 @@ static void qDumpQVector(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); 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 std::list<int> &list = *reinterpret_cast<const std::list<int> *>(d.data);
const void *p = d.data; const void *p = d.data;
@@ -2196,9 +2226,10 @@ static void qDumpStdList(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpStdMap(QDumper &d) static bool qDumpStdMap(QDumper &d)
{ {
typedef std::map<int, int> DummyType; typedef std::map<int, int> DummyType;
const DummyType &map = *reinterpret_cast<const DummyType*>(d.data); const DummyType &map = *reinterpret_cast<const DummyType*>(d.data);
@@ -2209,7 +2240,8 @@ static void qDumpStdMap(QDumper &d)
p = deref(p); p = deref(p);
int nn = map.size(); int nn = map.size();
qCheck(nn >= 0); if (nn < 0)
return false;
DummyType::const_iterator it = map.begin(); DummyType::const_iterator it = map.begin();
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it) for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
qCheckAccess(it.operator->()); qCheckAccess(it.operator->());
@@ -2263,9 +2295,10 @@ static void qDumpStdMap(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpStdSet(QDumper &d) static bool qDumpStdSet(QDumper &d)
{ {
typedef std::set<int> DummyType; typedef std::set<int> DummyType;
const DummyType &set = *reinterpret_cast<const DummyType*>(d.data); const DummyType &set = *reinterpret_cast<const DummyType*>(d.data);
@@ -2274,7 +2307,8 @@ static void qDumpStdSet(QDumper &d)
p = deref(p); p = deref(p);
int nn = set.size(); int nn = set.size();
qCheck(nn >= 0); if (nn < 0)
return false;
DummyType::const_iterator it = set.begin(); DummyType::const_iterator it = set.begin();
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it) for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
qCheckAccess(it.operator->()); qCheckAccess(it.operator->());
@@ -2306,9 +2340,10 @@ static void qDumpStdSet(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); 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); const std::string &str = *reinterpret_cast<const std::string *>(d.data);
@@ -2325,9 +2360,10 @@ static void qDumpStdString(QDumper &d)
P(d, "numchild", "0"); P(d, "numchild", "0");
d.disarm(); 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); const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
@@ -2344,9 +2380,10 @@ static void qDumpStdWString(QDumper &d)
P(d, "numchild", "0"); P(d, "numchild", "0");
d.disarm(); d.disarm();
return true;
} }
static void qDumpStdVector(QDumper &d) static bool qDumpStdVector(QDumper &d)
{ {
// Correct type would be something like: // Correct type would be something like:
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl // 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 // from asking for unavailable child details
int nn = (v->finish - v->start) / d.extraInt[0]; int nn = (v->finish - v->start) / d.extraInt[0];
if (nn < 0) if (nn < 0)
qCheck(false); return false;
if (nn > 0) { if (nn > 0) {
qCheckAccess(v->start); qCheckAccess(v->start);
qCheckAccess(v->finish); qCheckAccess(v->finish);
@@ -2392,9 +2429,10 @@ static void qDumpStdVector(QDumper &d)
d << "]"; d << "]";
} }
d.disarm(); d.disarm();
return true;
} }
static void qDumpStdVectorBool(QDumper &d) static bool qDumpStdVectorBool(QDumper &d)
{ {
// FIXME // FIXME
return qDumpStdVector(d); return qDumpStdVector(d);
@@ -2402,10 +2440,14 @@ static void qDumpStdVectorBool(QDumper &d)
static void handleProtocolVersion2and3(QDumper & d) static void handleProtocolVersion2and3(QDumper & d)
{ {
if (!d.outertype[0]) { if (!d.outertype[0]) {
qDumpUnknown(d); qDumpUnknown(d);
return; return;
} }
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
__try {
#endif
d.setupTemplateParameters(); d.setupTemplateParameters();
P(d, "iname", d.iname); P(d, "iname", d.iname);
@@ -2551,6 +2593,12 @@ static void handleProtocolVersion2and3(QDumper & d)
if (!d.success) if (!d.success)
qDumpUnknown(d); qDumpUnknown(d);
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
} __except(EXCEPTION_EXECUTE_HANDLER) {
qDumpUnknown(d, DUMPUNKNOWN_MESSAGE" <exception>");
}
#endif
} }
} // anonymous namespace } // anonymous namespace

View File

@@ -34,6 +34,7 @@ HEADERS += \
$$PWD/cdbdebugoutput.h \ $$PWD/cdbdebugoutput.h \
$$PWD/cdbsymbolgroupcontext.h \ $$PWD/cdbsymbolgroupcontext.h \
$$PWD/cdbstacktracecontext.h \ $$PWD/cdbstacktracecontext.h \
$$PWD/cdbstackframecontext.h \
$$PWD/cdbbreakpoint.h \ $$PWD/cdbbreakpoint.h \
$$PWD/cdbmodules.h \ $$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \ $$PWD/cdbassembler.h \
@@ -46,6 +47,7 @@ SOURCES += \
$$PWD/cdbdebugeventcallback.cpp \ $$PWD/cdbdebugeventcallback.cpp \
$$PWD/cdbdebugoutput.cpp \ $$PWD/cdbdebugoutput.cpp \
$$PWD/cdbsymbolgroupcontext.cpp \ $$PWD/cdbsymbolgroupcontext.cpp \
$$PWD/cdbstackframecontext.cpp \
$$PWD/cdbstacktracecontext.cpp \ $$PWD/cdbstacktracecontext.cpp \
$$PWD/cdbbreakpoint.cpp \ $$PWD/cdbbreakpoint.cpp \
$$PWD/cdbmodules.cpp \ $$PWD/cdbmodules.cpp \

View File

@@ -29,8 +29,9 @@
#include "cdbdebugengine.h" #include "cdbdebugengine.h"
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbstacktracecontext.h" #include "cdbstacktracecontext.h"
#include "cdbstackframecontext.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbbreakpoint.h" #include "cdbbreakpoint.h"
#include "cdbmodules.h" #include "cdbmodules.h"
#include "cdbassembler.h" #include "cdbassembler.h"
@@ -274,7 +275,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
m_hDebuggeeProcess(0), m_hDebuggeeProcess(0),
m_hDebuggeeThread(0), m_hDebuggeeThread(0),
m_breakEventMode(BreakEventHandle), m_breakEventMode(BreakEventHandle),
m_dumper(&m_cif), m_dumper(new CdbDumperHelper(parent, &m_cif)),
m_watchTimer(-1), m_watchTimer(-1),
m_debugEventCallBack(engine), m_debugEventCallBack(engine),
m_engine(engine), m_engine(engine),
@@ -464,7 +465,7 @@ bool CdbDebugEngine::startDebugger()
dumperEnabled = false; dumperEnabled = false;
} }
} }
m_d->m_dumper.reset(dumperLibName, dumperEnabled); m_d->m_dumper->reset(dumperLibName, dumperEnabled);
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage; QString errorMessage;
bool rc = false; bool rc = false;
@@ -590,19 +591,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
} else { } else {
m_currentThreadId = 0; m_currentThreadId = 0;
} }
// Set initial breakpoints // Clear any saved breakpoints and set initial breakpoints
m_engine->executeDebuggerCommand(QLatin1String("bc"));
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints()) if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization(); m_engine->attemptBreakpointSynchronization();
// At any event, we want a temporary breakpoint at main() to load // At any event, we want a temporary breakpoint at main() to load
// the dumpers. // the dumpers.
if (m_dumper.state() == CdbDumperHelper::NotLoaded) { if (m_dumper->state() == CdbDumperHelper::NotLoaded) {
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) { if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
QString errorMessage;
CDBBreakPoint mainBP; CDBBreakPoint mainBP;
// Do not resolve at this point in the rare event someone // Do not resolve at this point in the rare event someone
// has main in a module // has main in a module
mainBP.funcName = QLatin1String("main"); mainBP.funcName = QLatin1String("main");
mainBP.oneShot = true; mainBP.oneShot = true;
QString errorMessage;
if (!mainBP.add(m_cif.debugControl, &errorMessage)) if (!mainBP.add(m_cif.debugControl, &errorMessage))
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage); m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
} }
@@ -675,13 +677,13 @@ void CdbDebugEngine::exitDebugger()
killWatchTimer(); killWatchTimer();
} }
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
{ {
if (!m_currentStackTrace) { if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC); *errorMessage = QLatin1String(msgNoStackTraceC);
return 0; return 0;
} }
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage)) if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
return sg; return sg;
return 0; return 0;
} }
@@ -718,8 +720,8 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
} }
bool success = false; bool success = false;
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage)) if (CdbStackFrameContext *sgc = getStackFrameContext(frameIndex, errorMessage))
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage); success = sgc->populateModelInitially(wh, errorMessage);
wh->rebuildModel(); wh->rebuildModel();
return success; return success;
@@ -800,8 +802,8 @@ void CdbDebugEngine::updateWatchModel()
filterEvaluateWatchers(&incomplete, watchHandler); filterEvaluateWatchers(&incomplete, watchHandler);
// Do locals. We might get called while running when someone enters watchers // Do locals. We might get called while running when someone enters watchers
if (!incomplete.empty()) { if (!incomplete.empty()) {
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage); CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
if (!sg || !CdbSymbolGroupContext::completeModel(sg, incomplete, watchHandler, &errorMessage)) if (!sg || !sg->completeModel(incomplete, watchHandler, &errorMessage))
break; break;
} }
watchHandler->rebuildModel(); watchHandler->rebuildModel();
@@ -1016,7 +1018,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
bool success = false; bool success = false;
do { do {
QString newValue; QString newValue;
CdbSymbolGroupContext *sg = m_d->getStackFrameSymbolGroupContext(frameIndex, &errorMessage); CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
if (!sg) if (!sg)
break; break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage)) if (!sg->assignValue(expr, value, &newValue, &errorMessage))
@@ -1061,17 +1063,30 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
QString *value, QString *value,
QString *type, QString *type,
QString *errorMessage) 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) if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << expression; 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. // 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; ULONG errorPosition = 0;
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(), const HRESULT hr = ctrl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, &debugValue, DEBUG_VALUE_INVALID, debugValue,
&errorPosition); if (FAILED(hr)) { &errorPosition);
if (FAILED(hr)) {
if (HRESULT_CODE(hr) == 517) { if (HRESULT_CODE(hr) == 517) {
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope."). *errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
arg(expression); arg(expression);
@@ -1081,7 +1096,6 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
} }
return false; return false;
} }
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true; return true;
} }
@@ -1355,14 +1369,14 @@ void CdbDebugEnginePrivate::handleDebugEvent()
case BreakEventHandle: case BreakEventHandle:
case BreakEventMain: case BreakEventMain:
if (mode == BreakEventMain) if (mode == BreakEventMain)
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess); m_dumper->load(m_debuggerManager);
m_debuggerManagerAccess->notifyInferiorStopped(); m_debuggerManagerAccess->notifyInferiorStopped();
updateThreadList(); updateThreadList();
updateStackTrace(); updateStackTrace();
break; break;
case BreakEventMainLoadDumpers: case BreakEventMainLoadDumpers:
// Temp stop to load dumpers // Temp stop to load dumpers
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess); m_dumper->load(m_debuggerManager);
m_engine->startWatchTimer(); m_engine->startWatchTimer();
continueInferiorProcess(); continueInferiorProcess();
break; break;
@@ -1436,7 +1450,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
QString errorMessage; QString errorMessage;
m_engine->reloadRegisters(); m_engine->reloadRegisters();
m_currentStackTrace = m_currentStackTrace =
CdbStackTraceContext::create(&m_cif, m_currentThreadId, &errorMessage); CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
if (!m_currentStackTrace) { if (!m_currentStackTrace) {
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage)); qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
return; return;

View File

@@ -46,7 +46,7 @@ namespace Internal {
class DebuggerManager; class DebuggerManager;
class IDebuggerManagerAccessForEngines; class IDebuggerManagerAccessForEngines;
class WatchHandler; class WatchHandler;
class CdbSymbolGroupContext; class CdbStackFrameContext;
class CdbStackTraceContext; class CdbStackTraceContext;
// Thin wrapper around the 'DBEng' debugger engine shared library // Thin wrapper around the 'DBEng' debugger engine shared library
@@ -125,7 +125,7 @@ struct CdbDebugEnginePrivate
void cleanStackTrace(); void cleanStackTrace();
void clearForRun(); void clearForRun();
void handleModuleLoad(const QString &); void handleModuleLoad(const QString &);
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const; CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
void clearDisplay(); void clearDisplay();
bool interruptInterferiorProcess(QString *errorMessage); bool interruptInterferiorProcess(QString *errorMessage);
@@ -136,6 +136,7 @@ struct CdbDebugEnginePrivate
bool attemptBreakpointSynchronization(QString *errorMessage); bool attemptBreakpointSynchronization(QString *errorMessage);
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, 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; const QSharedPointer<CdbOptions> m_options;
HANDLE m_hDebuggeeProcess; HANDLE m_hDebuggeeProcess;
@@ -147,7 +148,7 @@ struct CdbDebugEnginePrivate
CdbComInterfaces m_cif; CdbComInterfaces m_cif;
CdbDebugEventCallback m_debugEventCallBack; CdbDebugEventCallback m_debugEventCallBack;
CdbDebugOutput m_debugOutputCallBack; CdbDebugOutput m_debugOutputCallBack;
CdbDumperHelper m_dumper; QSharedPointer<CdbDumperHelper> m_dumper;
CdbDebugEngine* m_engine; CdbDebugEngine* m_engine;
DebuggerManager *m_debuggerManager; DebuggerManager *m_debuggerManager;

View File

@@ -318,14 +318,16 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
} }
// Format exception with stacktrace in case of C++ exception // 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); formatException(e, str);
if (e->ExceptionCode == cppExceptionCode) { if (e->ExceptionCode == cppExceptionCode) {
QString errorMessage; QString errorMessage;
ULONG currentThreadId = 0; ULONG currentThreadId = 0;
cif.debugSystemObjects->GetCurrentThreadId(&currentThreadId); dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(&currentThreadId);
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(&cif, currentThreadId, &errorMessage)) { if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
str << "at:\n"; str << "at:\n";
stc->format(str); stc->format(str);
str <<'\n'; str <<'\n';
@@ -343,7 +345,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString msg; QString msg;
{ {
QTextStream str(&msg); QTextStream str(&msg);
formatException(Exception, m_pEngine->m_d->m_cif, str); formatException(Exception, m_pEngine->m_d->m_dumper, str);
} }
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << msg; qDebug() << Q_FUNC_INFO << '\n' << msg;
@@ -469,6 +471,37 @@ STDMETHODIMP CdbDebugEventCallback::SystemError(
return S_OK; 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::IgnoreDebugEventCallback() IgnoreDebugEventCallback::IgnoreDebugEventCallback()
{ {

View File

@@ -32,12 +32,13 @@
#include "cdbcom.h" #include "cdbcom.h"
#include <QtCore/QtGlobal> #include <QtCore/QStringList>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class CdbDebugEngine; class CdbDebugEngine;
class IDebuggerManagerAccessForEngines;
// Base class for event callbacks that takes care // Base class for event callbacks that takes care
// Active X magic. Provides base implementations with // Active X magic. Provides base implementations with
@@ -235,6 +236,34 @@ private:
CdbDebugEngine *m_pEngine; 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 // Event handler that ignores everything
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
{ {

View File

@@ -80,7 +80,8 @@ STDMETHODIMP CdbDebugOutputBase::Output(
IN PCWSTR text IN PCWSTR text
) )
{ {
output(mask, QString::fromUtf16(text)); const QString msg = QString::fromUtf16(text);
output(mask, msg.trimmed());
return S_OK; return S_OK;
} }

View File

@@ -32,7 +32,8 @@
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include "cdbdebugoutput.h" #include "cdbdebugoutput.h"
#include "cdbdebugeventcallback.h" #include "cdbdebugeventcallback.h"
#include "watchutils.h" #include "cdbsymbolgroupcontext.h"
#include "watchhandler.h"
#include <QtCore/QRegExp> #include <QtCore/QRegExp>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
@@ -43,21 +44,20 @@ enum { loadDebug = 0 };
static const char *dumperModuleNameC = "gdbmacros"; static const char *dumperModuleNameC = "gdbmacros";
static const char *qtCoreModuleNameC = "QtCore"; static const char *qtCoreModuleNameC = "QtCore";
static const ULONG waitTimeOutMS = 30000; static const ULONG waitTimeOutMS = 30000;
static const char *dumperPrefixC = "dumper:";
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
// Alloc memory in debuggee using the ".dvalloc" command as // Alloc memory in debuggee using the ".dvalloc" command as
// there seems to be no API for it. // there seems to be no API for it.
static bool allocDebuggeeMemory(CIDebugControl *ctl, static bool allocDebuggeeMemory(CdbComInterfaces *cif,
CIDebugClient *client,
int size, ULONG64 *address, QString *errorMessage) int size, ULONG64 *address, QString *errorMessage)
{ {
*address = 0; *address = 0;
const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size); const QString allocCmd = QLatin1String(".dvalloc ") + QString::number(size);
StringOutputHandler stringHandler; StringOutputHandler stringHandler;
OutputRedirector redir(client, &stringHandler); OutputRedirector redir(cif->debugClient, &stringHandler);
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, allocCmd, errorMessage))
return false; return false;
// "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
bool ok = false; bool ok = false;
@@ -68,7 +68,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
if (ok) if (ok)
*address = addri; *address = addri;
} }
if (loadDebug) if (loadDebug > 1)
qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok; qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
if (!ok) { if (!ok) {
*errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output); *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 // Alloc an AscII string in debuggee
static bool createDebuggeeAscIIString(CIDebugControl *ctl, static bool createDebuggeeAscIIString(CdbComInterfaces *cif,
CIDebugClient *client,
CIDebugDataSpaces *data,
const QString &s, const QString &s,
ULONG64 *address, ULONG64 *address,
QString *errorMessage) QString *errorMessage)
{ {
QByteArray sAsciiData = s.toLocal8Bit(); QByteArray sAsciiData = s.toLocal8Bit();
sAsciiData += '\0'; sAsciiData += '\0';
if (!allocDebuggeeMemory(ctl, client, sAsciiData.size(), address, errorMessage)) if (!allocDebuggeeMemory(cif, sAsciiData.size(), address, errorMessage))
return false; 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)) { if (FAILED(hr)) {
*errorMessage= msgComFailed("WriteVirtual", hr); *errorMessage= msgComFailed("WriteVirtual", hr);
return false; return false;
@@ -101,21 +99,20 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
// the QtCored4.pdb file to be present as we need "qstrdup" // the QtCored4.pdb file to be present as we need "qstrdup"
// as dummy symbol. This is ok ATM since dumpers only // as dummy symbol. This is ok ATM since dumpers only
// make sense for Qt apps. // make sense for Qt apps.
static bool debuggeeLoadLibrary(CIDebugControl *ctl, static bool debuggeeLoadLibrary(IDebuggerManagerAccessForEngines *access,
CIDebugClient *client, CdbComInterfaces *cif,
CIDebugSymbols *syms, const QString &moduleName,
CIDebugDataSpaces *data, QString *errorMessage)
const QString &moduleName, QString *errorMessage)
{ {
if (loadDebug) if (loadDebug > 1)
qDebug() << Q_FUNC_INFO << moduleName; qDebug() << Q_FUNC_INFO << moduleName;
// Try to ignore the breakpoints // Try to ignore the breakpoints
IgnoreDebugEventCallback devNull; CdbExceptionLoggerEventCallback exLogger(QLatin1String(dumperPrefixC), access);
EventCallbackRedirector eventRedir(client, &devNull); EventCallbackRedirector eventRedir(cif->debugClient, &exLogger);
// Make a call to LoadLibraryA. First, reserve memory in debugger // Make a call to LoadLibraryA. First, reserve memory in debugger
// and copy name over. // and copy name over.
ULONG64 nameAddress; ULONG64 nameAddress;
if (!createDebuggeeAscIIString(ctl, client, data, moduleName, &nameAddress, errorMessage)) if (!createDebuggeeAscIIString(cif, moduleName, &nameAddress, errorMessage))
return false; return false;
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)" // We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
// (void* LoadLibraryA(char*)). However, despite providing a symbol // (void* LoadLibraryA(char*)). However, despite providing a symbol
@@ -125,21 +122,21 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some // Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information. // reason, the symbol is present in QtGui as well without type information.
QString dummyFunc = QLatin1String("*qstrdup"); QString dummyFunc = QLatin1String("*qstrdup");
if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk) if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
return false; return false;
QString callCmd = QLatin1String(".call "); QString callCmd = QLatin1String(".call ");
callCmd += dummyFunc; callCmd += dummyFunc;
callCmd += QLatin1String("(0x"); callCmd += QLatin1String("(0x");
callCmd += QString::number(nameAddress, 16); callCmd += QString::number(nameAddress, 16);
callCmd += QLatin1Char(')'); callCmd += QLatin1Char(')');
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, callCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
return false; 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; return false;
// This will hit a breakpoint. // This will hit a breakpoint.
if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QString(QLatin1Char('g')), errorMessage))
return false; return false;
const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS); const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr); *errorMessage = msgComFailed("WaitForEvent", hr);
return false; return false;
@@ -147,66 +144,13 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
return true; 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::CdbDumperHelper(CdbComInterfaces *cif) : CdbDumperHelper::CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
CdbComInterfaces *cif) :
m_messagePrefix(QLatin1String(dumperPrefixC)),
m_state(NotLoaded), m_state(NotLoaded),
m_access(access),
m_cif(cif), m_cif(cif),
m_inBufferAddress(0), m_inBufferAddress(0),
m_inBufferSize(0), m_inBufferSize(0),
@@ -234,24 +178,23 @@ void CdbDumperHelper::reset(const QString &library, bool enabled)
m_library = library; m_library = library;
m_state = enabled ? NotLoaded : Disabled; m_state = enabled ? NotLoaded : Disabled;
m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
m_knownTypes.clear(); m_helper.clear();
m_qtVersion.clear();
m_qtNamespace.clear();
m_inBufferAddress = m_outBufferAddress = 0; m_inBufferAddress = m_outBufferAddress = 0;
m_inBufferSize = m_outBufferSize = 0; m_inBufferSize = m_outBufferSize = 0;
m_typeSizeCache.clear();
m_failedTypes.clear();
clearBuffer(); clearBuffer();
} }
// Attempt to load and initialize dumpers, give feedback // Attempt to load and initialize dumpers, give feedback
// to user. // to user.
void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access) void CdbDumperHelper::load(DebuggerManager *manager)
{ {
enum Result { Failed, Succeeded, NoQtApp }; enum Result { Failed, Succeeded, NoQtApp };
if (m_state != NotLoaded) if (m_state != NotLoaded)
return; return;
manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000); manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
const QString messagePrefix = QLatin1String("dumper:");
QString message; QString message;
Result result = Failed; Result result = Failed;
do { do {
@@ -266,12 +209,11 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
// Make sure the dumper lib is loaded. // Make sure the dumper lib is loaded.
if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) { if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
// Try to load // Try to load
if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces, if (!debuggeeLoadLibrary(m_access, m_cif, m_library, &message)) {
m_library, &message)) {
break; break;
} }
} else { } 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 // Resolve symbols and do call to get types
if (!resolveSymbols(&message)) if (!resolveSymbols(&message))
@@ -285,17 +227,17 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
switch (result) { switch (result) {
case Failed: case Failed:
message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message); message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
access->showDebuggerOutput(messagePrefix, message); m_access->showDebuggerOutput(m_messagePrefix, message);
access->showQtDumperLibraryWarning(message); m_access->showQtDumperLibraryWarning(message);
m_state = Disabled; m_state = Disabled;
break; break;
case Succeeded: case Succeeded:
access->showDebuggerOutput(messagePrefix, message); m_access->showDebuggerOutput(m_messagePrefix, message);
access->showDebuggerOutput(messagePrefix, statusMessage()); m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString());
m_state = Loaded; m_state = Loaded;
break; break;
case NoQtApp: case NoQtApp:
access->showDebuggerOutput(messagePrefix, message); m_access->showDebuggerOutput(m_messagePrefix, message);
m_state = Disabled; m_state = Disabled;
break; break;
} }
@@ -303,15 +245,6 @@ void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEn
qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message; 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. // Retrieve address and optionally size of a symbol.
static inline bool getSymbolAddress(CIDebugSymbols *sg, static inline bool getSymbolAddress(CIDebugSymbols *sg,
const QString &name, const QString &name,
@@ -373,35 +306,76 @@ bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
bool CdbDumperHelper::getKnownTypes(QString *errorMessage) bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
{ {
QByteArray output; 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; 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)); *errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
} }
if (loadDebug) if (loadDebug)
qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion << m_qtNamespace; qDebug() << Q_FUNC_INFO << m_helper.toString(true);
return 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; char *ptr = const_cast<char*>(buffer.data());
EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull); ULONG bytesToWrite = buffer.size();
const QString callCmd = p.command(m_dumpObjectSymbol); while (bytesToWrite > 0) {
// Set up call and a temporary breakpoint after it. 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)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
return false; return false;
// Go and filter away break event // Set up call and a temporary breakpoint after it.
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage)) // 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; return false;
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS); HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr); *errorMessage = msgComFailed("WaitForEvent", hr);
return false; 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 // 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)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("ReadVirtual", hr); *errorMessage = msgComFailed("ReadVirtual", hr);
return false; return false;
@@ -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)); *errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
return false; 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; return true;
} }

View File

@@ -30,7 +30,10 @@
#ifndef CDBDUMPERHELPER_H #ifndef CDBDUMPERHELPER_H
#define CDBDUMPERHELPER_H #define CDBDUMPERHELPER_H
#include "watchutils.h"
#include "cdbcom.h"
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QMap>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -66,7 +69,8 @@ public:
Failed Failed
}; };
explicit CdbDumperHelper(CdbComInterfaces *cif); explicit CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
CdbComInterfaces *cif);
~CdbDumperHelper(); ~CdbDumperHelper();
State state() const { return m_state; } State state() const { return m_state; }
@@ -76,31 +80,50 @@ public:
void reset(const QString &library, bool enabled); void reset(const QString &library, bool enabled);
// Call in a temporary breakpoint state to actually load. // 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: private:
struct DumperInputParameters;
void clearBuffer(); void clearBuffer();
bool resolveSymbols(QString *errorMessage); bool resolveSymbols(QString *errorMessage);
bool getKnownTypes(QString *errorMessage); bool getKnownTypes(QString *errorMessage);
bool callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage); bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
inline QString statusMessage() const; 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; State m_state;
IDebuggerManagerAccessForEngines *m_access;
CdbComInterfaces *m_cif; CdbComInterfaces *m_cif;
QString m_library; QString m_library;
QString m_dumpObjectSymbol; QString m_dumpObjectSymbol;
QStringList m_knownTypes;
QString m_qtVersion;
QString m_qtNamespace;
quint64 m_inBufferAddress; quint64 m_inBufferAddress;
unsigned long m_inBufferSize; unsigned long m_inBufferSize;
quint64 m_outBufferAddress; quint64 m_outBufferAddress;
unsigned long m_outBufferSize; unsigned long m_outBufferSize;
char *m_buffer; char *m_buffer;
typedef QMap<QString, int> TypeSizeCache;
TypeSizeCache m_typeSizeCache;
QStringList m_failedTypes;
QtDumperHelper m_helper;
}; };
} // namespace Internal } // namespace Internal

View 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

View 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

View File

@@ -28,9 +28,11 @@
**************************************************************************/ **************************************************************************/
#include "cdbstacktracecontext.h" #include "cdbstacktracecontext.h"
#include "cdbstackframecontext.h"
#include "cdbbreakpoint.h" #include "cdbbreakpoint.h"
#include "cdbsymbolgroupcontext.h" #include "cdbsymbolgroupcontext.h"
#include "cdbdebugengine_p.h" #include "cdbdebugengine_p.h"
#include "cdbdumperhelper.h"
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QTextStream> #include <QtCore/QTextStream>
@@ -38,18 +40,20 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
CdbStackTraceContext::CdbStackTraceContext(CdbComInterfaces *cif) : CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
m_cif(cif), m_dumper(dumper),
m_cif(dumper->comInterfaces()),
m_instructionOffset(0) m_instructionOffset(0)
{ {
} }
CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif, CdbStackTraceContext *CdbStackTraceContext::create(const QSharedPointer<CdbDumperHelper> &dumper,
unsigned long threadId, unsigned long threadId,
QString *errorMessage) QString *errorMessage)
{ {
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << threadId; qDebug() << Q_FUNC_INFO << threadId;
CdbComInterfaces *cif = dumper->comInterfaces();
HRESULT hr = cif->debugSystemObjects->SetCurrentThreadId(threadId); HRESULT hr = cif->debugSystemObjects->SetCurrentThreadId(threadId);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3"). *errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
@@ -60,7 +64,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
} }
// fill the DEBUG_STACK_FRAME array // fill the DEBUG_STACK_FRAME array
ULONG frameCount; 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); hr = cif->debugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
if (FAILED(hr)) { if (FAILED(hr)) {
delete ctx; delete ctx;
@@ -77,7 +81,7 @@ CdbStackTraceContext *CdbStackTraceContext::create(CdbComInterfaces *cif,
CdbStackTraceContext::~CdbStackTraceContext() CdbStackTraceContext::~CdbStackTraceContext()
{ {
qDeleteAll(m_symbolContexts); qDeleteAll(m_frameContexts);
} }
bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/) bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
@@ -85,8 +89,8 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << frameCount; qDebug() << Q_FUNC_INFO << frameCount;
m_symbolContexts.resize(frameCount); m_frameContexts.resize(frameCount);
qFill(m_symbolContexts, static_cast<CdbSymbolGroupContext*>(0)); qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames // Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
WCHAR wszBuf[MAX_PATH]; WCHAR wszBuf[MAX_PATH];
@@ -114,28 +118,28 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
return true; 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) if (debugCDB)
qDebug() << Q_FUNC_INFO << index; 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."). *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; return 0;
} }
if (m_frameContexts.at(index))
if (m_symbolContexts.at(index)) return m_frameContexts.at(index);
return m_symbolContexts.at(index);
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage); CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
if (!sg) if (!sg)
return 0; return 0;
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage); CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage);
if (!sc) if (!sc)
return 0; \ return 0;
m_symbolContexts[index] = sc; CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
return sc; m_frameContexts[index] = fr;
return fr;
} }
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage) CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)

View File

@@ -36,6 +36,7 @@
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QVector> #include <QtCore/QVector>
#include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QTextStream; class QTextStream;
@@ -46,21 +47,23 @@ namespace Internal {
struct CdbComInterfaces; struct CdbComInterfaces;
class CdbSymbolGroupContext; class CdbSymbolGroupContext;
class CdbStackFrameContext;
class CdbDumperHelper;
/* Context representing a break point stack consisting of several frames. /* 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. */ * containining the local variables of the stack. */
class CdbStackTraceContext class CdbStackTraceContext
{ {
Q_DISABLE_COPY(CdbStackTraceContext) Q_DISABLE_COPY(CdbStackTraceContext)
explicit CdbStackTraceContext(CdbComInterfaces *cif); explicit CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper);
public: public:
enum { maxFrames = 100 }; enum { maxFrames = 100 };
~CdbStackTraceContext(); ~CdbStackTraceContext();
static CdbStackTraceContext *create(CdbComInterfaces *cif, static CdbStackTraceContext *create(const QSharedPointer<CdbDumperHelper> &dumper,
unsigned long threadid, unsigned long threadid,
QString *errorMessage); QString *errorMessage);
@@ -70,7 +73,7 @@ public:
// Top-Level instruction offset for disassembler // Top-Level instruction offset for disassembler
ULONG64 instructionOffset() const { return m_instructionOffset; } ULONG64 instructionOffset() const { return m_instructionOffset; }
CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage); CdbStackFrameContext*frameContextAt(int index, QString *errorMessage);
// Format for logging // Format for logging
void format(QTextStream &str) const; void format(QTextStream &str) const;
@@ -80,10 +83,11 @@ private:
bool init(unsigned long frameCount, QString *errorMessage); bool init(unsigned long frameCount, QString *errorMessage);
CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage); CIDebugSymbolGroup *createSymbolGroup(int index, QString *errorMessage);
const QSharedPointer<CdbDumperHelper> m_dumper;
CdbComInterfaces *m_cif; CdbComInterfaces *m_cif;
DEBUG_STACK_FRAME m_cdbFrames[maxFrames]; DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
QVector <CdbSymbolGroupContext*> m_symbolContexts; QVector <CdbStackFrameContext*> m_frameContexts;
QList<StackFrame> m_frames; QList<StackFrame> m_frames;
ULONG64 m_instructionOffset; ULONG64 m_instructionOffset;
}; };

View File

@@ -200,16 +200,6 @@ QString CdbSymbolGroupContext::toString(bool verbose) const
return rc; 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 CdbSymbolGroupContext::SymbolState CdbSymbolGroupContext::symbolState(unsigned long index) const
{ {
return getSymbolState(m_symbolParameters.at(index)); return getSymbolState(m_symbolParameters.at(index));
@@ -499,144 +489,26 @@ QString CdbSymbolGroupContext::debugValueToString(const DEBUG_VALUE &dv, CIDebug
return formatArrayHelper(dv.RawBytes, 24, integerBase); return formatArrayHelper(dv.RawBytes, 24, integerBase);
} }
// - Watch model functions bool CdbSymbolGroupContext::debugValueToInteger(const DEBUG_VALUE &dv, qint64 *value)
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) *value = 0;
// Open next level if specified by recursion depth or child is already expanded switch (dv.Type) {
// (Sometimes, some root children are already expanded after creating the context). case DEBUG_VALUE_INT8:
const bool hasChildren = wd.childCount > 0 || wd.isChildrenNeeded(); *value = dv.I8;
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)
{
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));
}
}
if (childCountPtr)
*childCountPtr = succeededChildCount;
return true; return true;
} case DEBUG_VALUE_INT16:
*value = static_cast<short>(dv.I16);
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; return true;
} case DEBUG_VALUE_INT32:
*value = static_cast<long>(dv.I32);
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 true;
case DEBUG_VALUE_INT64:
*value = static_cast<long long>(dv.I64);
return true;
default:
break;
}
return false;
} }
} // namespace Internal } // namespace Internal

View File

@@ -31,6 +31,7 @@
#define CDBSYMBOLGROUPCONTEXT_H #define CDBSYMBOLGROUPCONTEXT_H
#include "cdbcom.h" #include "cdbcom.h"
#include "watchhandler.h"
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QVector> #include <QtCore/QVector>
@@ -72,11 +73,13 @@ public:
bool assignValue(const QString &iname, const QString &value, bool assignValue(const QString &iname, const QString &value,
QString *newValue /* = 0 */, QString *errorMessage); 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, static bool completeModel(CdbSymbolGroupContext *sg,
const QList<WatchData> &incompleteLocals, const QList<WatchData> &incompleteLocals,
WatchHandler *wh, OutputIterator it,
QString *errorMessage); QString *errorMessage);
// Retrieve child symbols of prefix as a sequence of WatchData. // 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 // 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 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, ..." // format an array of unsigned longs as "0x323, 0x2322, ..."
static QString hexFormatArray(const unsigned short *array, int size); static QString hexFormatArray(const unsigned short *array, int size);
@@ -125,26 +129,25 @@ private:
QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters; QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
}; };
template <class OutputIterator> // Helper to a sequence of WatchData into a list.
bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage) class WatchDataBackInserter {
{ public:
unsigned long start; explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
unsigned long parentId;
if (!getChildSymbolsPosition(prefix, &start, &parentId, errorMessage)) inline WatchDataBackInserter & operator*() { return *this; }
return false; inline WatchDataBackInserter &operator=(const WatchData &wd) {
// Skip over expanded children m_wh.push_back(wd);
const unsigned long end = m_symbolParameters.size(); return *this;
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;
}
}
return true;
} }
inline WatchDataBackInserter &operator++() { return *this; }
private:
QList<WatchData> &m_wh;
};
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger
#include "cdbsymbolgroupcontext_tpl.h"
#endif // CDBSYMBOLGROUPCONTEXT_H #endif // CDBSYMBOLGROUPCONTEXT_H

View File

@@ -146,6 +146,7 @@ private:
friend class ScriptEngine; friend class ScriptEngine;
friend struct CdbDebugEnginePrivate; friend struct CdbDebugEnginePrivate;
friend class CdbDumperHelper; friend class CdbDumperHelper;
friend class CdbExceptionLoggerEventCallback;
// called from the engines after successful startup // called from the engines after successful startup
virtual void notifyInferiorStopRequested() = 0; virtual void notifyInferiorStopRequested() = 0;

View File

@@ -55,6 +55,7 @@
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QtCore/QTime> #include <QtCore/QTime>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QTextStream>
#include <QtGui/QAction> #include <QtGui/QAction>
#include <QtGui/QApplication> #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 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, static void setWatchDataValue(WatchData &data, const GdbMi &mi,
int encoding = 0) int encoding = 0)
{ {
@@ -3041,15 +3004,7 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
return false; return false;
// simple types // simple types
if (m_availableSimpleDebuggingHelpers.contains(type)) return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
return true;
// templates
QString tmplate;
QString inner;
if (!extractTemplate(type, &tmplate, &inner))
return false;
return m_availableSimpleDebuggingHelpers.contains(tmplate);
} }
void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren) void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
@@ -3080,145 +3035,33 @@ void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
} }
WatchData data = data0; WatchData data = data0;
QTC_ASSERT(!data.exp.isEmpty(), return); 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; QByteArray params;
// adjust the data extract QStringList extraArgs;
if (outertype == m_namespace + "QWidget") const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
outertype = m_namespace + "QObject"; m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, &params, &extraArgs);
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";
}
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2; //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
//int protocol = data.iname.startsWith("watch") ? 3 : 2; //int protocol = data.iname.startsWith("watch") ? 3 : 2;
int protocol = 2; const int protocol = 2;
//int protocol = isDisplayedIName(data.iname) ? 3 : 2; //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
QString addr; QString addr;
if (data.addr.startsWith("0x")) if (data.addr.startsWith(QLatin1String("0x"))) {
addr = "(void*)" + data.addr; addr = QLatin1String("(void*)") + data.addr;
else } else {
addr = "&(" + data.exp + ")"; addr = QLatin1String("&(") + data.exp + QLatin1Char(')');
}
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');
sendWatchParameters(params); sendWatchParameters(params);
QString cmd ="call " QString cmd;
+ QString("(void*)qDumpObjectData440(") QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
+ QString::number(protocol) protocol << ',' << "%1+1" // placeholder for token
+ ',' + "%1+1" // placeholder for token <<',' << addr << ',' << (dumpChildren ? "1" : "0")
+ ',' + addr << ',' << extraArgs.join(QString(QLatin1Char(','))) << ')';
+ ',' + (dumpChildren ? "1" : "0")
+ ',' + extraArgs[0]
+ ',' + extraArgs[1]
+ ',' + extraArgs[2]
+ ',' + extraArgs[3] + ')';
//qDebug() << "CMD: " << cmd; qDebug() << "CMD: " << cmd;
QVariant var; QVariant var;
var.setValue(data); var.setValue(data);
@@ -3451,6 +3294,7 @@ void GdbEngine::updateWatchModel2()
void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record) void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
{ {
m_dumperHelper.clear();
//qDebug() << "DATA DUMPER TRIAL:" << record.toString(); //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
GdbMi output = record.data.findChild("consolestreamoutput"); GdbMi output = record.data.findChild("consolestreamoutput");
QByteArray out = output.data(); QByteArray out = output.data();
@@ -3464,23 +3308,27 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
GdbMi contents; GdbMi contents;
contents.fromString(out); contents.fromString(out);
GdbMi simple = contents.findChild("dumpers"); GdbMi simple = contents.findChild("dumpers");
m_namespace = contents.findChild("namespace").data();
m_dumperHelper.setQtNamespace(contents.findChild("namespace").data());
GdbMi qtversion = contents.findChild("qtversion"); GdbMi qtversion = contents.findChild("qtversion");
int qtv = 0;
if (qtversion.children().size() == 3) { 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(1).data().toInt() << 8)
+ qtversion.childAt(2).data().toInt(); + qtversion.childAt(2).data().toInt();
//qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion; //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
} else {
m_qtVersion = 0;
} }
m_dumperHelper.setQtVersion(qtv);
//qDebug() << "CONTENTS: " << contents.toString(); //qDebug() << "CONTENTS: " << contents.toString();
//qDebug() << "SIMPLE DUMPERS: " << simple.toString(); //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
m_availableSimpleDebuggingHelpers.clear();
QStringList availableSimpleDebuggingHelpers;
foreach (const GdbMi &item, simple.children()) foreach (const GdbMi &item, simple.children())
m_availableSimpleDebuggingHelpers.append(item.data()); availableSimpleDebuggingHelpers.append(item.data());
if (m_availableSimpleDebuggingHelpers.isEmpty()) { m_dumperHelper.parseQueryTypes(availableSimpleDebuggingHelpers, QtDumperHelper::GdbDebugger);
if (availableSimpleDebuggingHelpers.isEmpty()) {
m_debuggingHelperState = DebuggingHelperUnavailable; m_debuggingHelperState = DebuggingHelperUnavailable;
q->showStatusMessage(tr("Debugging helpers not found.")); q->showStatusMessage(tr("Debugging helpers not found."));
//QMessageBox::warning(q->mainWindow(), //QMessageBox::warning(q->mainWindow(),
@@ -3494,8 +3342,9 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record)
} else { } else {
m_debuggingHelperState = DebuggingHelperAvailable; m_debuggingHelperState = DebuggingHelperAvailable;
q->showStatusMessage(tr("%1 custom dumpers found.") 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; //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDebuggingHelpers;
} }

View File

@@ -33,6 +33,7 @@
#include "idebuggerengine.h" #include "idebuggerengine.h"
#include "gdbmi.h" #include "gdbmi.h"
#include "outputcollector.h" #include "outputcollector.h"
#include "watchutils.h"
#include <consoleprocess.h> #include <consoleprocess.h>
@@ -342,9 +343,7 @@ private:
QString m_editedData; QString m_editedData;
int m_pendingRequests; int m_pendingRequests;
QStringList m_availableSimpleDebuggingHelpers; QtDumperHelper m_dumperHelper;
QString m_namespace; // namespace used in "namespaced Qt";
int m_qtVersion; // Qt version used in the debugged program
DebuggingHelperState m_debuggingHelperState; DebuggingHelperState m_debuggingHelperState;
QList<GdbMi> m_currentFunctionArgs; QList<GdbMi> m_currentFunctionArgs;

View File

@@ -78,6 +78,7 @@ static int watcherCounter = 0;
WatchData::WatchData() : WatchData::WatchData() :
childCount(-1), childCount(-1),
valuedisabled(false), valuedisabled(false),
source(0),
state(InitialState), state(InitialState),
parentIndex(-1), parentIndex(-1),
row(-1), row(-1),

View File

@@ -124,6 +124,7 @@ public:
private: private:
public: public:
int source; // Used by some debuggers (CDB) to tell where it originates from (dumper or symbol evaluation)
int state; int state;
// Model // Model

File diff suppressed because it is too large Load Diff

View File

@@ -31,15 +31,17 @@
#define WATCHUTILS_H #define WATCHUTILS_H
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QMap>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QString; class QDebug;
class QByteArray;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class WatchData;
QString dotEscape(QString str); QString dotEscape(QString str);
QString currentTime(); QString currentTime();
bool isSkippableFunction(const QString &funcName, const QString &fileName); bool isSkippableFunction(const QString &funcName, const QString &fileName);
@@ -64,8 +66,131 @@ QString extractTypeFromPTypeOutput(const QString &str);
bool isIntOrFloatType(const QString &type); bool isIntOrFloatType(const QString &type);
QString sizeofTypeExpression(const QString &type); QString sizeofTypeExpression(const QString &type);
// Parse 'query' (1) protocol response of the custom dumpers // Decode string data as returned by the dumper helpers.
bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace); 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 Internal
} // namespace Debugger } // namespace Debugger