Commit cbd85651 authored by mae's avatar mae
Browse files

Merge branch 'master' of git@scm.dev.nokia.troll.no:creator/mainline

parents ffe9fe58 df2c805c
......@@ -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; do { qProvokeSegFaultHelper = *(char*)d; } while (0)
# define qCheckPointer(d) if (d && IsBadReadPtr(d, 1)) return; 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();
......@@ -1078,7 +1096,7 @@ static void qDumpQHash(QDumper &d)
int n = h->size;
if (n < 0)
qCheck(false);
return;
if (n > 0) {
qCheckPointer(h->fakeNext);
qCheckPointer(*h->buckets);
......@@ -1181,19 +1199,21 @@ static void 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;
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;
#endif
}
......@@ -1262,7 +1282,7 @@ static void qDumpQLinkedList(QDumper &d)
reinterpret_cast<const QLinkedListData*>(deref(d.data));
int nn = ldata->size;
if (nn < 0)
qCheck(false);
return;
int n = nn;
P(d, "value", "<" << n << " items>");
......@@ -1386,7 +1406,7 @@ static void qDumpQMap(QDumper &d)
int n = h->size;
if (n < 0)
qCheck(false);
return;
if (n > 0) {
qCheckAccess(h->backward);
qCheckAccess(h->forward[0]);
......@@ -1899,7 +1919,7 @@ static void qDumpQSet(QDumper &d)
int n = hd->size;
if (n < 0)
qCheck(false);
return;
if (n > 0) {
qCheckAccess(node);
qCheckPointer(node->next);
......@@ -1952,8 +1972,23 @@ static void qDumpQSharedPointer(QDumper &d)
P(d, "name", "data");
qDumpInnerValue(d, d.innertype, ptr.data());
d.endHash();
I(d, "strongref", 44);
I(d, "weakref", 45);
const int v = sizeof(void *);
d.beginHash();
const void *weak = addOffset(deref(addOffset(d.data, v)), v);
P(d, "name", "weakref");
P(d, "value", *static_cast<const int *>(weak));
P(d, "type", "int");
P(d, "addr", weak);
P(d, "numchild", "0");
d.endHash();
d.beginHash();
const void *strong = addOffset(weak, sizeof(int));
P(d, "name", "strongref");
P(d, "value", *static_cast<const int *>(strong));
P(d, "type", "int");
P(d, "addr", strong);
P(d, "numchild", "0");
d.endHash();
d << "]";
}
d.disarm();
......@@ -1982,7 +2017,7 @@ static void qDumpQStringList(QDumper &d)
const QStringList &list = *reinterpret_cast<const QStringList *>(d.data);
int n = list.size();
if (n < 0)
qCheck(false);
return;
if (n > 0) {
qCheckAccess(&list.front());
qCheckAccess(&list.back());
......@@ -2117,7 +2152,7 @@ static void qDumpQVector(QDumper &d)
// from asking for unavailable child details
int nn = v->size;
if (nn < 0)
qCheck(false);
return;
if (nn > 0) {
//qCheckAccess(&vec.front());
//qCheckAccess(&vec.back());
......@@ -2209,7 +2244,8 @@ static void qDumpStdMap(QDumper &d)
p = deref(p);
int nn = map.size();
qCheck(nn >= 0);
if (nn < 0)
return;
DummyType::const_iterator it = map.begin();
for (int i = 0; i < nn && i < 10 && it != map.end(); ++i, ++it)
qCheckAccess(it.operator->());
......@@ -2274,7 +2310,8 @@ static void qDumpStdSet(QDumper &d)
p = deref(p);
int nn = set.size();
qCheck(nn >= 0);
if (nn < 0)
return;
DummyType::const_iterator it = set.begin();
for (int i = 0; i < nn && i < 10 && it != set.end(); ++i, ++it)
qCheckAccess(it.operator->());
......@@ -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;
if (nn > 0) {
qCheckAccess(v->start);
qCheckAccess(v->finish);
......@@ -2402,10 +2439,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 +2592,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
......
......@@ -384,7 +384,7 @@ void OutputPaneManager::ensurePageVisible(int idx)
}
}
// Slot connected to showPage signal of each page
void OutputPaneManager::showPage(bool focus)
{
int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender()));
......
......@@ -34,6 +34,7 @@ HEADERS += \
$$PWD/cdbdebugoutput.h \
$$PWD/cdbsymbolgroupcontext.h \
$$PWD/cdbstacktracecontext.h \
$$PWD/cdbstackframecontext.h \
$$PWD/cdbbreakpoint.h \
$$PWD/cdbmodules.h \
$$PWD/cdbassembler.h \
......@@ -46,6 +47,7 @@ SOURCES += \
$$PWD/cdbdebugeventcallback.cpp \
$$PWD/cdbdebugoutput.cpp \
$$PWD/cdbsymbolgroupcontext.cpp \
$$PWD/cdbstackframecontext.cpp \
$$PWD/cdbstacktracecontext.cpp \
$$PWD/cdbbreakpoint.cpp \
$$PWD/cdbmodules.cpp \
......
......@@ -29,8 +29,9 @@
#include "cdbdebugengine.h"
#include "cdbdebugengine_p.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbstacktracecontext.h"
#include "cdbstackframecontext.h"
#include "cdbsymbolgroupcontext.h"
#include "cdbbreakpoint.h"
#include "cdbmodules.h"
#include "cdbassembler.h"
......@@ -274,7 +275,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
m_hDebuggeeProcess(0),
m_hDebuggeeThread(0),
m_breakEventMode(BreakEventHandle),
m_dumper(&m_cif),
m_dumper(new CdbDumperHelper(parent, &m_cif)),
m_watchTimer(-1),
m_debugEventCallBack(engine),
m_engine(engine),
......@@ -464,7 +465,7 @@ bool CdbDebugEngine::startDebugger()
dumperEnabled = false;
}
}
m_d->m_dumper.reset(dumperLibName, dumperEnabled);
m_d->m_dumper->reset(dumperLibName, dumperEnabled);
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
......@@ -590,19 +591,20 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
} else {
m_currentThreadId = 0;
}
// Set initial breakpoints
// Clear any saved breakpoints and set initial breakpoints
m_engine->executeDebuggerCommand(QLatin1String("bc"));
if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
m_engine->attemptBreakpointSynchronization();
// At any event, we want a temporary breakpoint at main() to load
// the dumpers.
if (m_dumper.state() == CdbDumperHelper::NotLoaded) {
if (m_dumper->state() == CdbDumperHelper::NotLoaded) {
if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
QString errorMessage;
CDBBreakPoint mainBP;
// Do not resolve at this point in the rare event someone
// has main in a module
mainBP.funcName = QLatin1String("main");
mainBP.oneShot = true;
QString errorMessage;
if (!mainBP.add(m_cif.debugControl, &errorMessage))
m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage);
}
......@@ -675,13 +677,13 @@ void CdbDebugEngine::exitDebugger()
killWatchTimer();
}
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
{
if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC);
return 0;
}
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage))
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
return sg;
return 0;
}
......@@ -718,8 +720,8 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
}
bool success = false;
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage))
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage);
if (CdbStackFrameContext *sgc = getStackFrameContext(frameIndex, errorMessage))
success = sgc->populateModelInitially(wh, errorMessage);
wh->rebuildModel();
return success;
......@@ -800,8 +802,8 @@ void CdbDebugEngine::updateWatchModel()
filterEvaluateWatchers(&incomplete, watchHandler);
// Do locals. We might get called while running when someone enters watchers
if (!incomplete.empty()) {
CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage);
if (!sg || !CdbSymbolGroupContext::completeModel(sg, incomplete, watchHandler, &errorMessage))
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
if (!sg || !sg->completeModel(incomplete, watchHandler, &errorMessage))
break;
}
watchHandler->rebuildModel();
......@@ -1016,7 +1018,7 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
bool success = false;
do {
QString newValue;
CdbSymbolGroupContext *sg = m_d->getStackFrameSymbolGroupContext(frameIndex, &errorMessage);
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
if (!sg)
break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
......@@ -1061,17 +1063,30 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
QString *value,
QString *type,
QString *errorMessage)
{
DEBUG_VALUE debugValue;
if (!m_d->evaluateExpression(m_d->m_cif.debugControl, expression, &debugValue, errorMessage))
return false;
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
bool CdbDebugEnginePrivate::evaluateExpression(CIDebugControl *ctrl,
const QString &expression,
DEBUG_VALUE *debugValue,
QString *errorMessage)
{
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO << expression;
DEBUG_VALUE debugValue;
memset(&debugValue, 0, sizeof(DEBUG_VALUE));
memset(debugValue, 0, sizeof(DEBUG_VALUE));
// Original syntax must be restored, else setting breakpoints will fail.
SyntaxSetter syntaxSetter(m_d->m_cif.debugControl, DEBUG_EXPR_CPLUSPLUS);
SyntaxSetter syntaxSetter(ctrl, DEBUG_EXPR_CPLUSPLUS);
ULONG errorPosition = 0;
const HRESULT hr = m_d->m_cif.debugControl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, &debugValue,
&errorPosition); if (FAILED(hr)) {
const HRESULT hr = ctrl->EvaluateWide(expression.utf16(),
DEBUG_VALUE_INVALID, debugValue,
&errorPosition);
if (FAILED(hr)) {
if (HRESULT_CODE(hr) == 517) {
*errorMessage = QString::fromLatin1("Unable to evaluate '%1': Expression out of scope.").
arg(expression);
......@@ -1081,7 +1096,6 @@ bool CdbDebugEngine::evaluateExpression(const QString &expression,
}
return false;
}
*value = CdbSymbolGroupContext::debugValueToString(debugValue, m_d->m_cif.debugControl, type);
return true;
}
......@@ -1355,14 +1369,14 @@ void CdbDebugEnginePrivate::handleDebugEvent()
case BreakEventHandle:
case BreakEventMain:
if (mode == BreakEventMain)
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_dumper->load(m_debuggerManager);
m_debuggerManagerAccess->notifyInferiorStopped();
updateThreadList();
updateStackTrace();
break;
case BreakEventMainLoadDumpers:
// Temp stop to load dumpers
m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
m_dumper->load(m_debuggerManager);
m_engine->startWatchTimer();
continueInferiorProcess();
break;
......@@ -1436,7 +1450,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
QString errorMessage;
m_engine->reloadRegisters();
m_currentStackTrace =
CdbStackTraceContext::create(&m_cif, m_currentThreadId, &errorMessage);
CdbStackTraceContext::create(m_dumper, m_currentThreadId, &errorMessage);
if (!m_currentStackTrace) {
qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
return;
......
......@@ -46,7 +46,7 @@ namespace Internal {
class DebuggerManager;
class IDebuggerManagerAccessForEngines;
class WatchHandler;
class CdbSymbolGroupContext;
class CdbStackFrameContext;
class CdbStackTraceContext;
// Thin wrapper around the 'DBEng' debugger engine shared library
......@@ -125,7 +125,7 @@ struct CdbDebugEnginePrivate
void cleanStackTrace();
void clearForRun();
void handleModuleLoad(const QString &);
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const;
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
void clearDisplay();
bool interruptInterferiorProcess(QString *errorMessage);
......@@ -136,6 +136,7 @@ struct CdbDebugEnginePrivate
bool attemptBreakpointSynchronization(QString *errorMessage);
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
const QSharedPointer<CdbOptions> m_options;
HANDLE m_hDebuggeeProcess;
......@@ -147,7 +148,7 @@ struct CdbDebugEnginePrivate
CdbComInterfaces m_cif;
CdbDebugEventCallback m_debugEventCallBack;
CdbDebugOutput m_debugOutputCallBack;
CdbDumperHelper m_dumper;
QSharedPointer<CdbDumperHelper> m_dumper;
CdbDebugEngine* m_engine;
DebuggerManager *m_debuggerManager;
......
......@@ -318,14 +318,16 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
}
// Format exception with stacktrace in case of C++ exception
void formatException(const EXCEPTION_RECORD64 *e, CdbComInterfaces &cif, QTextStream &str)
void formatException(const EXCEPTION_RECORD64 *e,
const QSharedPointer<CdbDumperHelper> &dumper,
QTextStream &str)
{
formatException(e, str);
if (e->ExceptionCode == cppExceptionCode) {
QString errorMessage;
ULONG currentThreadId = 0;
cif.debugSystemObjects->GetCurrentThreadId(&currentThreadId);
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(&cif, currentThreadId, &errorMessage)) {
dumper->comInterfaces()->debugSystemObjects->GetCurrentThreadId(&currentThreadId);
if (CdbStackTraceContext *stc = CdbStackTraceContext::create(dumper, currentThreadId, &errorMessage)) {
str << "at:\n";
stc->format(str);
str <<'\n';
......@@ -343,7 +345,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
QString msg;
{
QTextStream str(&msg);
formatException(Exception, m_pEngine->m_d->m_cif, str);
formatException(Exception, m_pEngine->m_d->m_dumper, str);
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << msg;
......@@ -469,6 +471,37 @@ STDMETHODIMP CdbDebugEventCallback::SystemError(
return S_OK;
}
// -----------ExceptionLoggerEventCallback
CdbExceptionLoggerEventCallback::CdbExceptionLoggerEventCallback(const QString &logPrefix,
IDebuggerManagerAccessForEngines *access) :
m_logPrefix(logPrefix),
m_access(access)
{
}
STDMETHODIMP CdbExceptionLoggerEventCallback::GetInterestMask(THIS_ __out PULONG mask)
{
*mask = DEBUG_EVENT_EXCEPTION;
return S_OK;
}
STDMETHODIMP CdbExceptionLoggerEventCallback::Exception(
THIS_
__in PEXCEPTION_RECORD64 Exception,
__in ULONG /* FirstChance */
)
{
m_exceptionMessages.push_back(QString());
{
QTextStream str(&m_exceptionMessages.back());
formatException(Exception, str);
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << m_exceptionMessages.back();
m_access->showDebuggerOutput(m_logPrefix, m_exceptionMessages.back());
return S_OK;
}
// -----------IgnoreDebugEventCallback
IgnoreDebugEventCallback::IgnoreDebugEventCallback()
{
......
......@@ -32,12 +32,13 @@
#include "cdbcom.h"
#include <QtCore/QtGlobal>
#include <QtCore/QStringList>
namespace Debugger {
namespace Internal {
class CdbDebugEngine;
class IDebuggerManagerAccessForEngines;
// Base class for event callbacks that takes care
// Active X magic. Provides base implementations with
......@@ -235,6 +236,34 @@ private:
CdbDebugEngine *m_pEngine;
};
// Event handler logs exceptions to the debugger window
// and ignores the rest. To be used for running dumper calls.
class CdbExceptionLoggerEventCallback : public CdbDebugEventCallbackBase
{
public:
explicit CdbExceptionLoggerEventCallback(const QString &logPrefix,
IDebuggerManagerAccessForEngines *access);
STDMETHOD(GetInterestMask)(
THIS_
__out PULONG mask
);
STDMETHOD(Exception)(
THIS_
__in PEXCEPTION_RECORD64 Exception,
__in ULONG FirstChance
);
int exceptionCount() const { return m_exceptionMessages.size(); }
QStringList exceptionMessages() const { return m_exceptionMessages; }
private:
const QString m_logPrefix;
IDebuggerManagerAccessForEngines *m_access;
QStringList m_exceptionMessages;
};
// Event handler that ignores everything
class IgnoreDebugEventCallback : public CdbDebugEventCallbackBase
{
......
......@@ -80,7 +80,8 @@ STDMETHODIMP CdbDebugOutputBase::Output(
IN PCWSTR text
)
{
output(mask, QString::fromUtf16(text));
const QString msg = QString::fromUtf16(text);
output(mask, msg.trimmed());
return S_OK;
}
......
......@@ -30,7 +30,10 @@
#ifndef CDBDUMPERHELPER_H
#define CDBDUMPERHELPER_H
#include "watchutils.h"
#include "cdbcom.h"
#include <QtCore/QStringList>
#include <QtCore/QMap>
namespace Debugger {
namespace Internal {
......@@ -66,7 +69,8 @@ public:
Failed
};
explicit CdbDumperHelper(CdbComInterfaces *cif);
explicit CdbDumperHelper(IDebuggerManagerAccessForEngines *access,
CdbComInterfaces *cif);
~CdbDumperHelper();
State state() const { return m_state; }
......@@ -76,31 +80,50 @@ public:
void reset(const QString &library, bool enabled);