Commit 72a18377 authored by Friedemann Kleint's avatar Friedemann Kleint

Initial work on dumpers for CDB.

Add dumper calls, parser etc. Catch crashes
in the dumper functions.
parent 3cb783f7
......@@ -60,6 +60,10 @@ int qtGhVersion = QT_VERSION;
# include <QtGui/QImage>
#endif
#ifdef Q_OS_WIN
# include <windows.h>
#endif
#include <list>
#include <map>
#include <string>
......@@ -231,11 +235,17 @@ static QByteArray stripPointerType(QByteArray type)
}
// This is used to abort evaluation of custom data dumpers in a "coordinated"
// way. Abortion will happen anyway when we try to access a non-initialized
// way. Abortion will happen at the latest when we try to access a non-initialized
// non-trivial object, so there is no way to prevent this from occuring at all
// conceptionally. Gdb will catch SIGSEGV and return to the calling frame.
// This is just fine provided we only _read_ memory in the custom handlers
// below.
// conceptionally. Ideally, if there is API to check memory access, it should
// be used to terminate nicely, especially with CDB.
// 1) Gdb will catch SIGSEGV and return to the calling frame.
// This is just fine provided we only _read_ memory in the custom handlers
// below.
// 2) For MSVC/CDB, exceptions must be handled in the dumper, which is
// achieved using __try/__except. The exception will be reported in the
// debugger, which will then execute a 'gN' command, passing handling back
// to the __except clause.
volatile int qProvokeSegFaultHelper;
......@@ -269,11 +279,16 @@ static bool startsWith(const char *s, const char *t)
return qstrncmp(s, t, qstrlen(t)) == 0;
}
// provoke segfault when address is not readable
#define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
#define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
// provoke segfault unconditionally
#define qCheck(b) do { if (!(b)) qProvokeSegFaultHelper = *(char*)0; } while (0)
// Check memory for read access and provoke segfault if nothing else helps.
// On Windows, try to be less crash-prone by checking memory using WinAPI
#ifdef Q_OS_WIN
# define qCheckAccess(d) if (IsBadReadPtr(d, 1)) return false; do { qProvokeSegFaultHelper = *(char*)d; } while (0)
# define qCheckPointer(d) if (d && IsBadReadPtr(d, 1)) return false; do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
#else
# define qCheckAccess(d) do { qProvokeSegFaultHelper = *(char*)d; } while (0)
# define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0)
#endif
const char *stripNamespace(const char *type)
{
......@@ -692,11 +707,14 @@ void QDumper::putEllipsis()
#define TT(type, value) \
"<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>"
static void qDumpUnknown(QDumper &d)
#define DUMPUNKNOWN_MESSAGE "<internal error>"
static void qDumpUnknown(QDumper &d, const char *why = 0)
{
P(d, "iname", d.iname);
P(d, "addr", d.data);
P(d, "value", "<internal error>");
if (!why)
why = DUMPUNKNOWN_MESSAGE;
P(d, "value", why);
P(d, "type", d.outertype);
P(d, "numchild", "0");
d.disarm();
......@@ -815,7 +833,7 @@ static void qDumpInnerValueOrPointer(QDumper &d,
//////////////////////////////////////////////////////////////////////////////
static void qDumpQByteArray(QDumper &d)
static bool qDumpQByteArray(QDumper &d)
{
const QByteArray &ba = *reinterpret_cast<const QByteArray *>(d.data);
......@@ -848,6 +866,7 @@ static void qDumpQByteArray(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQDateTime(QDumper &d)
......@@ -1063,7 +1082,7 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val
}
static void qDumpQHash(QDumper &d)
static bool qDumpQHash(QDumper &d)
{
QHashData *h = *reinterpret_cast<QHashData *const*>(d.data);
const char *keyType = d.templateParameters[0];
......@@ -1078,7 +1097,7 @@ static void qDumpQHash(QDumper &d)
int n = h->size;
if (n < 0)
qCheck(false);
return false;
if (n > 0) {
qCheckPointer(h->fakeNext);
qCheckPointer(*h->buckets);
......@@ -1127,6 +1146,7 @@ static void qDumpQHash(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQHashNode(QDumper &d)
......@@ -1177,23 +1197,25 @@ static void qDumpQImage(QDumper &d)
#endif
}
static void qDumpQList(QDumper &d)
static bool qDumpQList(QDumper &d)
{
// This uses the knowledge that QList<T> has only a single member
// of type union { QListData p; QListData::Data *d; };
const QListData &ldata = *reinterpret_cast<const QListData*>(d.data);
const QListData::Data *pdata =
*reinterpret_cast<const QListData::Data* const*>(d.data);
qCheckAccess(pdata);
int nn = ldata.size();
if (nn < 0)
qCheck(false);
return false;
if (nn > 0) {
qCheckAccess(ldata.d->array);
//qCheckAccess(ldata.d->array[0]);
//qCheckAccess(ldata.d->array[nn - 1]);
#if QT_VERSION >= 0x040400
if (ldata.d->ref._q_value <= 0)
qCheck(false);
return false;
#endif
}
......@@ -1252,9 +1274,10 @@ static void qDumpQList(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQLinkedList(QDumper &d)
static bool qDumpQLinkedList(QDumper &d)
{
// This uses the knowledge that QLinkedList<T> has only a single member
// of type union { QLinkedListData *d; QLinkedListNode<T> *e; };
......@@ -1262,7 +1285,7 @@ static void qDumpQLinkedList(QDumper &d)
reinterpret_cast<const QLinkedListData*>(deref(d.data));
int nn = ldata->size;
if (nn < 0)
qCheck(false);
return false;
int n = nn;
P(d, "value", "<" << n << " items>");
......@@ -1294,6 +1317,7 @@ static void qDumpQLinkedList(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQLocale(QDumper &d)
......@@ -1341,7 +1365,7 @@ static void qDumpQLocale(QDumper &d)
d.disarm();
}
static void qDumpQMapNode(QDumper &d)
static bool qDumpQMapNode(QDumper &d)
{
const QMapData *h = reinterpret_cast<const QMapData *>(d.data);
const char *keyType = d.templateParameters[0];
......@@ -1375,9 +1399,10 @@ static void qDumpQMapNode(QDumper &d)
}
d.disarm();
return true;
}
static void qDumpQMap(QDumper &d)
static bool qDumpQMap(QDumper &d)
{
QMapData *h = *reinterpret_cast<QMapData *const*>(d.data);
const char *keyType = d.templateParameters[0];
......@@ -1386,7 +1411,7 @@ static void qDumpQMap(QDumper &d)
int n = h->size;
if (n < 0)
qCheck(false);
return false;
if (n > 0) {
qCheckAccess(h->backward);
qCheckAccess(h->forward[0]);
......@@ -1455,11 +1480,12 @@ static void qDumpQMap(QDumper &d)
}
d.disarm();
return true;
}
static void qDumpQMultiMap(QDumper &d)
static bool qDumpQMultiMap(QDumper &d)
{
qDumpQMap(d);
return qDumpQMap(d);
}
static void qDumpQModelIndex(QDumper &d)
......@@ -1890,7 +1916,7 @@ static void qDumpQPixmap(QDumper &d)
#endif
}
static void qDumpQSet(QDumper &d)
static bool qDumpQSet(QDumper &d)
{
// This uses the knowledge that QHash<T> has only a single member
// of union { QHashData *d; QHashNode<Key, T> *e; };
......@@ -1899,7 +1925,7 @@ static void qDumpQSet(QDumper &d)
int n = hd->size;
if (n < 0)
qCheck(false);
return false;
if (n > 0) {
qCheckAccess(node);
qCheckPointer(node->next);
......@@ -1933,6 +1959,7 @@ static void qDumpQSet(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQSharedPointer(QDumper &d)
......@@ -1959,7 +1986,7 @@ static void qDumpQSharedPointer(QDumper &d)
d.disarm();
}
static void qDumpQString(QDumper &d)
static bool qDumpQString(QDumper &d)
{
const QString &str = *reinterpret_cast<const QString *>(d.data);
......@@ -1975,14 +2002,15 @@ static void qDumpQString(QDumper &d)
P(d, "numchild", "0");
d.disarm();
return true;
}
static void qDumpQStringList(QDumper &d)
static bool qDumpQStringList(QDumper &d)
{
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
int n = list.size();
if (n < 0)
qCheck(false);
return false;
if (n > 0) {
qCheckAccess(&list.front());
qCheckAccess(&list.back());
......@@ -2009,6 +2037,7 @@ static void qDumpQStringList(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpQTextCodec(QDumper &d)
......@@ -2109,7 +2138,7 @@ static void qDumpQVariant(QDumper &d)
d.disarm();
}
static void qDumpQVector(QDumper &d)
static bool qDumpQVector(QDumper &d)
{
QVectorData *v = *reinterpret_cast<QVectorData *const*>(d.data);
......@@ -2117,7 +2146,7 @@ static void qDumpQVector(QDumper &d)
// from asking for unavailable child details
int nn = v->size;
if (nn < 0)
qCheck(false);
return false;
if (nn > 0) {
//qCheckAccess(&vec.front());
//qCheckAccess(&vec.back());
......@@ -2149,9 +2178,10 @@ static void qDumpQVector(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpStdList(QDumper &d)
static bool qDumpStdList(QDumper &d)
{
const std::list<int> &list = *reinterpret_cast<const std::list<int> *>(d.data);
const void *p = d.data;
......@@ -2196,9 +2226,10 @@ static void qDumpStdList(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpStdMap(QDumper &d)
static bool qDumpStdMap(QDumper &d)
{
typedef std::map<int, int> DummyType;
const DummyType &map = *reinterpret_cast<const DummyType*>(d.data);
......@@ -2209,7 +2240,8 @@ static void qDumpStdMap(QDumper &d)
p = deref(p);
int nn = map.size();
qCheck(nn >= 0);
if (nn < 0)
return false;
DummyType::const_iterator it = map.begin();
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
qCheckAccess(it.operator->());
......@@ -2263,9 +2295,10 @@ static void qDumpStdMap(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpStdSet(QDumper &d)
static bool qDumpStdSet(QDumper &d)
{
typedef std::set<int> DummyType;
const DummyType &set = *reinterpret_cast<const DummyType*>(d.data);
......@@ -2274,7 +2307,8 @@ static void qDumpStdSet(QDumper &d)
p = deref(p);
int nn = set.size();
qCheck(nn >= 0);
if (nn < 0)
return false;
DummyType::const_iterator it = set.begin();
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
qCheckAccess(it.operator->());
......@@ -2306,9 +2340,10 @@ static void qDumpStdSet(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpStdString(QDumper &d)
static bool qDumpStdString(QDumper &d)
{
const std::string &str = *reinterpret_cast<const std::string *>(d.data);
......@@ -2325,9 +2360,10 @@ static void qDumpStdString(QDumper &d)
P(d, "numchild", "0");
d.disarm();
return true;
}
static void qDumpStdWString(QDumper &d)
static bool qDumpStdWString(QDumper &d)
{
const std::wstring &str = *reinterpret_cast<const std::wstring *>(d.data);
......@@ -2344,9 +2380,10 @@ static void qDumpStdWString(QDumper &d)
P(d, "numchild", "0");
d.disarm();
return true;
}
static void qDumpStdVector(QDumper &d)
static bool qDumpStdVector(QDumper &d)
{
// Correct type would be something like:
// std::_Vector_base<int,std::allocator<int, std::allocator<int> >>::_Vector_impl
......@@ -2361,7 +2398,7 @@ static void qDumpStdVector(QDumper &d)
// from asking for unavailable child details
int nn = (v->finish - v->start) / d.extraInt[0];
if (nn < 0)
qCheck(false);
return false;
if (nn > 0) {
qCheckAccess(v->start);
qCheckAccess(v->finish);
......@@ -2392,9 +2429,10 @@ static void qDumpStdVector(QDumper &d)
d << "]";
}
d.disarm();
return true;
}
static void qDumpStdVectorBool(QDumper &d)
static bool qDumpStdVectorBool(QDumper &d)
{
// FIXME
return qDumpStdVector(d);
......@@ -2402,10 +2440,14 @@ static void qDumpStdVectorBool(QDumper &d)
static void handleProtocolVersion2and3(QDumper & d)
{
if (!d.outertype[0]) {
qDumpUnknown(d);
return;
}
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
__try {
#endif
d.setupTemplateParameters();
P(d, "iname", d.iname);
......@@ -2551,6 +2593,12 @@ static void handleProtocolVersion2and3(QDumper & d)
if (!d.success)
qDumpUnknown(d);
#ifdef Q_CC_MSVC // Catch exceptions with MSVC/CDB
} __except(EXCEPTION_EXECUTE_HANDLER) {
qDumpUnknown(d, DUMPUNKNOWN_MESSAGE" <exception>");
}
#endif
}
} // anonymous namespace
......
......@@ -34,6 +34,7 @@ HEADERS += \
$$PWD/cdbdebugoutput.h \
$$PWD/cdbsymbolgroupcontext.h \
$$PWD/cdbstacktracecontext.h \
$$PWD/cdbstackframecontext.h \
$$PWD/cdbbreakpoint.h \
$$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \
......@@ -46,6 +47,7 @@ SOURCES += \
$$PWD/cdbdebugeventcallback.cpp \
$$PWD/cdbdebugoutput.cpp \
$$PWD/cdbsymbolgroupcontext.cpp \
$$PWD/cdbstackframecontext.cpp \
$$PWD/cdbstacktracecontext.cpp \
$$PWD/cdbbreakpoint.cpp \
$$PWD/cdbmodules.cpp \
......
......@@ -29,8 +29,9 @@
#include "cdbdebugengine.h"
#include "cdbdebugengine_p.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbstacktracecontext.h"
#include "cdbstackframecontext.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbbreakpoint.h"
#include "cdbmodules.h"
#include "cdbassembler.h"
......@@ -274,7 +275,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
m_hDebuggeeProcess(0),
m_hDebuggeeThread(0),
m_breakEventMode(BreakEventHandle),
m_dumper(&m_cif),
m_dumper(new CdbDumperHelper(parent, &m_cif)),
m_watchTimer(-1),
m_debugEventCallBack(engine),
m_engine(engine),
......@@ -464,7 +465,7 @@ bool CdbDebugEngine::startDebugger()
dumperEnabled = false;
}
}
m_d->m_dumper.reset(dumperLibName, dumperEnabled);
m_d->m_dumper->reset(dumperLibName, dumperEnabled);
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
......@@ -590,19 +591,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
} else {
m_currentThreadId = 0;
}
// Set initial breakpoints
// Clear any saved breakpoints and set initial breakpoints
m_engine->executeDebuggerCommand(QLatin1String("bc"));
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization();
// At any event, we want a temporary breakpoint at main() to load
// the dumpers.
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
if (m_dumper->state() == CdbDumperHelper::NotLoaded) {
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
QString errorMessage;
CDBBreakPoint mainBP;
// Do not resolve at this point in the rare event someone
// has main in a module
mainBP.funcName = QLatin1String("main");
mainBP.oneShot = true;
QString errorMessage;
if (!mainBP.add(m_cif.debugControl, &errorMessage))
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
}
......@@ -675,13 +677,13 @@ void CdbDebugEngine::exitDebugger()
killWatchTimer();
}
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
{
if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC);
return 0;
}
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage))
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
return sg;
return 0;
}
......@@ -718,8 +720,8 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
}
bool success = false;
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage))
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage);
if (CdbStackFrameContext *sgc = getStackFrameContext(frameIndex, errorMessage))
success = sgc->populateModelInitially(wh, errorMessage);
wh->rebuildModel();
return success;
......@@ -800,8 +802,8 @@ void CdbDebugEngine::updateWatchModel()
filterEvaluateWatchers(&incomplete, watchHandler);
// Do locals. We might get called while running when someone enters watchers
if (!incomplete.empty()) {
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage);
if (!sg || !CdbSymbolGroupContext::completeModel(sg, incomplete, watchHandler, &errorMessage))
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
if (!sg || !sg->completeModel(incomplete, watchHandler, &errorMessage))
break;
}
watchHandler->rebuildModel();
......@@ -1016,7 +1018,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
bool success = false;
do {
QString newValue;
CdbSymbolGroupContext *sg = m_d->getStackFrameSymbolGroupContext(frameIndex, &errorMessage);
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
if (!sg)
break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
......@@ -1061,17 +1063,30 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
QString *value,
QString *type,
QString *errorMessage)
{
DEBUG_VALUE debugValue;
if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage))
return false;
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl,
const QString &expression,
DEBUG_VALUE *debugValue,
QString *errorMessage)
{
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << expression;
DEBUG_VALUE debugValue;
memset(&debugValue, 0, sizeof(DEBUG_VALUE));
memset(debugValue, 0, sizeof(DEBUG_VALUE));
// Original syntax must be restored, else setting breakpoints will fail.
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS);
ULONG errorPosition = 0;
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, &debugValue,
&errorPosition); if (FAILED(hr)) {
const HRESULT hr = ctrl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, debugValue,
&errorPosition);
if (FAILED(hr)) {
if (HRESULT_CODE(hr) == 517) {
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
arg(expression);
......@@ -1081,7 +1096,6 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
}
return false;
}
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
......@@ -1355,14 +1369,14 @@ void CdbDebugEnginePrivate::handleDebugEvent()
case BreakEventHandle:
case BreakEventMain:
if (mode == BreakEventMain)
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_dumper->load(m_debuggerManager);
m_debuggerManagerAccess->notifyInferiorStopped();
updateThreadList();
updateStackTrace();
break;
case BreakEventMainLoadDumpers:
// Temp stop to load dumpers
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_dumper->load(m_debuggerManager);
m_engine->startWatchTimer();
continueInferiorProcess();
break;
......@@ -1436,7 +1450,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
QString errorMessage;
m_engine->reloadRegisters();
m_currentStackTrace =
CdbStackTraceContext::create(&m_cif, m_currentThreadId, &errorMessage);
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
if (!m_currentStackTrace) {
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
return;
......
......@@ -46,7 +46,7 @@ namespace Internal {
class DebuggerManager;
class IDebuggerManagerAccessForEngines;
class WatchHandler;
class CdbSymbolGroupContext;
class CdbStackFrameContext;