From 52915776cdb4810dce51043d8078768e1b3b47ac Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Wed, 22 Apr 2009 17:28:26 +0200
Subject: [PATCH] Make CDB load custom dumpers.

Load in a 'well-defined' (temporary) breakpoint
at main().
---
 share/qtcreator/gdbmacros/gdbmacros.cpp       |   4 +
 src/plugins/debugger/cdb/cdbbreakpoint.cpp    |  24 +-
 src/plugins/debugger/cdb/cdbbreakpoint.h      |   1 +
 src/plugins/debugger/cdb/cdbdebugengine.cpp   | 104 ++++--
 src/plugins/debugger/cdb/cdbdebugengine_p.h   |   7 +-
 .../debugger/cdb/cdbdebugeventcallback.cpp    |  20 +-
 src/plugins/debugger/cdb/cdbdumperhelper.cpp  | 342 ++++++++++++++----
 src/plugins/debugger/cdb/cdbdumperhelper.h    |  60 ++-
 src/plugins/debugger/cdb/cdbmodules.cpp       | 101 +++++-
 src/plugins/debugger/cdb/cdbmodules.h         |  10 +-
 src/plugins/debugger/debuggermanager.h        |  14 +-
 src/plugins/debugger/gdbengine.cpp            |   6 +-
 src/plugins/debugger/watchutils.cpp           |  53 +++
 src/plugins/debugger/watchutils.h             |   3 +
 14 files changed, 593 insertions(+), 156 deletions(-)

diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp
index 06c1e1ae1cf..639a0d6500b 100644
--- a/share/qtcreator/gdbmacros/gdbmacros.cpp
+++ b/share/qtcreator/gdbmacros/gdbmacros.cpp
@@ -2529,7 +2529,11 @@ void *qDumpObjectData440(
     int protocolVersion,
     int token,
     void *data,
+#ifdef Q_CC_MSVC // CDB cannot handle boolean parameters
+    int dumpChildren,
+#else
     bool dumpChildren,
+#endif
     int extraInt0,
     int extraInt1,
     int extraInt2,
diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp
index 6cb8ebdd910..2824a57e5c4 100644
--- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp
+++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp
@@ -48,7 +48,8 @@ static const char sourceFileQuoteC = '`';
 
 CDBBreakPoint::CDBBreakPoint() :
     ignoreCount(0),
-    lineNumber(-1)
+    lineNumber(-1),
+    oneShot(false)
 {
 }
 
@@ -57,7 +58,8 @@ CDBBreakPoint::CDBBreakPoint(const BreakpointData &bpd) :
      condition(bpd.condition),
      ignoreCount(0),
      funcName(bpd.funcName),
-     lineNumber(-1)
+     lineNumber(-1),
+     oneShot(false)
 {
     if (!bpd.ignoreCount.isEmpty())
         ignoreCount = bpd.ignoreCount.toInt();
@@ -75,6 +77,10 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
         return 1;
     if (lineNumber < rhs.lineNumber)
         return -1;
+    if (oneShot && !rhs.oneShot)
+        return 1;
+    if (!oneShot && rhs.oneShot)
+        return -1;
     if (const int fileCmp = fileName.compare(rhs.fileName))
         return fileCmp;
     if (const int  funcCmp = funcName.compare(rhs.funcName))
@@ -87,7 +93,8 @@ int CDBBreakPoint::compare(const CDBBreakPoint& rhs) const
 void CDBBreakPoint::clear()
 {
      ignoreCount = 0;
-     clearExpressionData();
+     oneShot = false;
+     clearExpressionData();     
 }
 
 void CDBBreakPoint::clearExpressionData()
@@ -110,6 +117,8 @@ QDebug operator<<(QDebug dbg, const CDBBreakPoint &bp)
         nsp << " condition='" << bp.condition << '\'';
     if (bp.ignoreCount)
         nsp << " ignoreCount=" << bp.ignoreCount;
+    if (bp.oneShot)
+        nsp << " oneShot";
     return dbg;
 }
 
@@ -144,7 +153,10 @@ bool CDBBreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
     }
     // Pass Count is ignoreCount + 1
     ibp->SetPassCount(ignoreCount + 1u);
-    ibp->AddFlags(DEBUG_BREAKPOINT_ENABLED);
+    ULONG flags = DEBUG_BREAKPOINT_ENABLED;
+    if (oneShot)
+        flags |= DEBUG_BREAKPOINT_ONE_SHOT;
+    ibp->AddFlags(flags);
     return true;
 }
 
@@ -190,6 +202,10 @@ bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
     ibp->GetPassCount(&ignoreCount);
     if (ignoreCount)
         ignoreCount--;
+    ULONG flags = 0;
+    ibp->GetFlags(&flags);
+    if (flags & DEBUG_BREAKPOINT_ONE_SHOT)
+        oneShot = true;
     const QString expr = QString::fromUtf16(wszBuf);
     if (!parseExpression(expr)) {
         *errorMessage = QString::fromLatin1("Parsing of '%1' failed.").arg(expr);
diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h
index f1a775fe6e8..9ee7ef8d860 100644
--- a/src/plugins/debugger/cdb/cdbbreakpoint.h
+++ b/src/plugins/debugger/cdb/cdbbreakpoint.h
@@ -83,6 +83,7 @@ struct CDBBreakPoint
     unsigned long ignoreCount;    // ignore count associated with breakpoint
     int lineNumber;     // line in source file
     QString funcName;       // name of containing function
+    bool oneShot;
 };
 
 QDebug operator<<(QDebug, const CDBBreakPoint &bp);
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp
index 2aec4e327c3..a60057071b6 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine.cpp
+++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp
@@ -53,6 +53,7 @@
 #include <utils/consoleprocess.h>
 
 #include <QtCore/QDebug>
+#include <QtCore/QTimer>
 #include <QtCore/QTimerEvent>
 #include <QtCore/QFileInfo>
 #include <QtCore/QDir>
@@ -96,13 +97,13 @@ QString msgDebugEngineComResult(HRESULT hr)
         case E_UNEXPECTED:
         return QLatin1String("E_UNEXPECTED");
         case E_NOTIMPL:
-        return QLatin1String("E_NOTIMPL");
+        return QLatin1String("E_NOTIMPL");        
     }
     if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
         return QLatin1String("ERROR_ACCESS_DENIED");;
     if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
         return QLatin1String("STATUS_CONTROL_C_EXIT");
-    return Core::Utils::winErrorMessage(HRESULT_CODE(hr));
+    return QLatin1String("E_FAIL ") + Core::Utils::winErrorMessage(HRESULT_CODE(hr));
 }
 
 static QString msgStackIndexOutOfRange(int idx, int size)
@@ -450,14 +451,16 @@ void CdbDebugEnginePrivate::clearDisplay()
 bool CdbDebugEngine::startDebugger()
 {
     m_d->clearDisplay();
+    const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
     // Figure out dumper. @TODO: same in gdb...
-    bool dumperEnabled = false && m_d->m_debuggerManager->qtDumperLibraryEnabled();
-    const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManager->qtDumperLibraryName());
+    const QString dumperLibName = QDir::toNativeSeparators(m_d->m_debuggerManagerAccess->qtDumperLibraryName());
+    bool dumperEnabled = mode != AttachCore && !dumperLibName.isEmpty()
+                         && m_d->m_debuggerManagerAccess->qtDumperLibraryEnabled();
     if (dumperEnabled) {
         const QFileInfo fi(dumperLibName);
         if (!fi.isFile()) {
             const QString msg = tr("The dumper library '%1' does not exist.").arg(dumperLibName);
-            m_d->m_debuggerManager->showQtDumperLibraryWarning(msg);
+            m_d->m_debuggerManagerAccess->showQtDumperLibraryWarning(msg);
             dumperEnabled = false;
         }
     }
@@ -466,7 +469,6 @@ bool CdbDebugEngine::startDebugger()
     QString errorMessage;
     bool rc = false;
     m_d->clearForRun();
-    const DebuggerStartMode mode = m_d->m_debuggerManager->startMode();
     switch (mode) {
     case AttachExternal:
         rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
@@ -561,6 +563,52 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *
     return true;
 }
 
+// check for a breakpoint at 'main()'
+static inline bool hasBreakPointAtMain(const BreakHandler *bp)
+{
+    if (const int count = bp->size()) {
+        // check all variations, resolved or not
+        const QString main = QLatin1String("main");
+        const QString qMain = QLatin1String("qMain");
+        const QString moduleMainPattern = QLatin1String("!main");
+        for (int i = 0; i < count ; i++) {
+            const QString &function = bp->at(i)->funcName;
+            if (function == main || function == qMain || function.endsWith(moduleMainPattern))
+                return true;
+        }
+    }
+    return false;
+}
+
+void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
+{
+    setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
+    m_debuggerManagerAccess->notifyInferiorRunning();
+    ULONG currentThreadId;
+    if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, &currentThreadId))) {
+        m_currentThreadId = currentThreadId;
+    } else {
+        m_currentThreadId = 0;
+    }
+    // Set initial breakpoints
+    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 (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) {
+            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);
+        }
+    }
+}
+
 void CdbDebugEngine::processTerminated(unsigned long exitCode)
 {
     if (debugCDB)
@@ -859,13 +907,18 @@ void CdbDebugEngine::continueInferior()
 }
 
 // Continue process without notifications
-bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessage)
+bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
 {
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
     const HRESULT hr = m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
     if (FAILED(hr)) {
-        *errorMessage = msgComFailed("SetExecutionStatus", hr);
+        const QString errorMessage = msgComFailed("SetExecutionStatus", hr);
+        if (errorMessagePtr) {
+            *errorMessagePtr = errorMessage;
+        } else {
+            qWarning("continueInferiorProcess: %s\n", qPrintable(errorMessage));
+        }
         return false;
     }
     return  true;
@@ -992,10 +1045,10 @@ void CdbDebugEngine::executeDebuggerCommand(const QString &command)
 
 bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage)
 {
-    if (debugCDB)
-        qDebug() << Q_FUNC_INFO << command;
     // output to all clients, else we do not see anything
     const HRESULT hr = ctrl->ExecuteWide(DEBUG_OUTCTL_ALL_CLIENTS, command.utf16(), 0);
+    if (debugCDB)
+        qDebug() << "executeDebuggerCommand" << command << SUCCEEDED(hr);
     if (FAILED(hr)) {
         *errorMessage = QString::fromLatin1("Unable to execute '%1': %2").
                         arg(command, msgDebugEngineComResult(hr));
@@ -1300,10 +1353,19 @@ void CdbDebugEnginePrivate::handleDebugEvent()
 
     switch (mode) {
     case BreakEventHandle:
+    case BreakEventMain:
+        if (mode == BreakEventMain)
+            m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
         m_debuggerManagerAccess->notifyInferiorStopped();
         updateThreadList();
         updateStackTrace();
         break;
+    case BreakEventMainLoadDumpers:
+        // Temp stop to load dumpers    
+        m_dumper.load(m_debuggerManager, m_debuggerManagerAccess);
+        m_engine->startWatchTimer();
+        continueInferiorProcess();
+        break;
     case BreakEventIgnoreOnce:
         m_engine->startWatchTimer();
         break;
@@ -1417,17 +1479,6 @@ void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
     if (debugCDB>2)
         qDebug() << Q_FUNC_INFO << "\n    " << name;
     updateModules();
-    // Call the dumper helper hook and notify about progress.
-    bool ignoreNextBreakPoint;
-    if (m_dumper.moduleLoadHook(name, &ignoreNextBreakPoint)) {
-        if (m_dumper.state() == CdbDumperHelper::Loaded)
-            m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Dumpers loaded: %1").arg(m_dumper.library()));
-    } else {
-        m_debuggerManager->showQtDumperLibraryWarning(m_dumper.errorMessage());
-        m_debuggerManagerAccess->showDebuggerOutput(QLatin1String(dumperPrefixC), QString::fromLatin1("Unable to load dumpers: %1").arg(m_dumper.errorMessage()));
-    }
-    if (ignoreNextBreakPoint)
-        m_breakEventMode = BreakEventIgnoreOnce;
 }
 
 void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
@@ -1435,6 +1486,17 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
     Q_UNUSED(pBP)
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
+    // Did we hit main() and did the user want that or is that just
+    // our internal BP to load the dumpers?
+    QString errorMessage;
+    CDBBreakPoint bp;
+    if (bp.retrieve(pBP, &errorMessage) && !bp.funcName.isEmpty()) {
+        if (bp.funcName == QLatin1String("main") || bp.funcName.endsWith(QLatin1String("!main"))) {
+            m_breakEventMode = bp.oneShot ? BreakEventMainLoadDumpers : BreakEventMain;
+        }
+        if (debugCDB)
+            qDebug() << bp << " b-mode=" << m_breakEventMode;
+    }
 }
 
 void CdbDebugEngine::reloadSourceFiles()
diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h
index 95cdc759f9a..86ea197f0a2 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine_p.h
+++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h
@@ -98,6 +98,10 @@ struct CdbDebugEnginePrivate
     enum HandleBreakEventMode { // Special modes for break event handler.
         BreakEventHandle,
         BreakEventIgnoreOnce,
+        // We hit main (and the user intended it)
+        BreakEventMain,
+        // We hit main (and the user did not intend it, just load dumpers)
+        BreakEventMainLoadDumpers,
         BreakEventSyncBreakPoints,
     };
 
@@ -107,6 +111,7 @@ struct CdbDebugEnginePrivate
     bool init(QString *errorMessage);
     ~CdbDebugEnginePrivate();
 
+    void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle);
     void setDebuggeeHandles(HANDLE hDebuggeeProcess,  HANDLE hDebuggeeThread);
 
     bool isDebuggeeRunning() const { return m_watchTimer != -1; }
@@ -125,7 +130,7 @@ struct CdbDebugEnginePrivate
 
     bool interruptInterferiorProcess(QString *errorMessage);
 
-    bool continueInferiorProcess(QString *errorMessage);
+    bool continueInferiorProcess(QString *errorMessage = 0);
     bool continueInferior(QString *errorMessage);
 
     bool attemptBreakpointSynchronization(QString *errorMessage);
diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp
index 05bbb897bfe..372cda234c7 100644
--- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp
+++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp
@@ -247,7 +247,8 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
         break;
     case EXCEPTION_ACCESS_VIOLATION: {
             const bool writeOperation = e->ExceptionInformation[0];
-            str << (writeOperation ? "write access violation" : "read access violation");
+            str << (writeOperation ? "write" : "read")
+                << " access violation at: 0x" << e->ExceptionInformation[1];
     }
         break;
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
@@ -342,8 +343,10 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
     QString msg;
     {
         QTextStream str(&msg);
-        formatException(Exception, m_pEngine->m_d->m_cif, str);
+        formatException(Exception, m_pEngine->m_d->m_cif, str);        
     }
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << '\n' << msg;
     m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg);
     return S_OK;
 }
@@ -402,18 +405,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
     Q_UNUSED(StartOffset)
     if (debugCDB)
         qDebug() << Q_FUNC_INFO << ModuleName;
-
-    m_pEngine->m_d->setDebuggeeHandles(reinterpret_cast<HANDLE>(Handle), reinterpret_cast<HANDLE>(InitialThreadHandle));
-    m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning();
-
-    ULONG currentThreadId;
-    if (SUCCEEDED(m_pEngine->m_d->m_cif.debugSystemObjects->GetThreadIdByHandle(InitialThreadHandle, &currentThreadId)))
-        m_pEngine->m_d->m_currentThreadId = currentThreadId;
-    else
-        m_pEngine->m_d->m_currentThreadId = 0;
-    // Set initial breakpoints
-    if (m_pEngine->m_d->m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints())
-        m_pEngine->attemptBreakpointSynchronization();
+    m_pEngine->m_d->processCreatedAttached(Handle, InitialThreadHandle);
     return S_OK;
 }
 
diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp
index 16c734164e0..a0aa5142d15 100644
--- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp
+++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp
@@ -32,11 +32,18 @@
 #include "cdbdebugengine_p.h"
 #include "cdbdebugoutput.h"
 #include "cdbdebugeventcallback.h"
+#include "watchutils.h"
 
 #include <QtCore/QRegExp>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTextStream>
 
 enum { loadDebug = 0 };
 
+static const char *dumperModuleNameC = "gdbmacros";
+static const char *qtCoreModuleNameC = "QtCore";
+static const ULONG waitTimeOutMS = 30000;
+
 namespace Debugger {
 namespace Internal {
 
@@ -52,7 +59,7 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
     OutputRedirector redir(client, &stringHandler);
     if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, allocCmd, errorMessage))
         return false;
-   // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized
+   // "Allocated 1000 bytes starting at 003a0000" .. hopefully never localized    
     bool ok = false;
     const QString output = stringHandler.result();
     const int lastBlank = output.lastIndexOf(QLatin1Char(' '));
@@ -61,6 +68,8 @@ static bool allocDebuggeeMemory(CIDebugControl *ctl,
         if (ok)
             *address = addri;
     }
+     if (loadDebug)
+        qDebug() << Q_FUNC_INFO << '\n' << output << *address << ok;
     if (!ok) {
         *errorMessage = QString::fromLatin1("Failed to parse output '%1'").arg(output);
         return false;
@@ -88,25 +97,6 @@ static bool createDebuggeeAscIIString(CIDebugControl *ctl,
     return true;
 }
 
-// Locate 'qstrdup' in the (potentially namespaced) corelib. For some
-// reason, the symbol is present in QtGui as well without type information.
-static inline QString resolveStrdup(CIDebugSymbols *syms, QString *errorMessage)
-{
-    QStringList matches;
-    const QString pattern = QLatin1String("*qstrdup");
-    const QRegExp corelibPattern(QLatin1String("QtCore[d]*4!"));
-    Q_ASSERT(corelibPattern.isValid());
-    if (!searchSymbols(syms, pattern, &matches, errorMessage))
-        return QString();
-    QStringList corelibStrdup = matches.filter(corelibPattern);
-    if (corelibStrdup.isEmpty()) {
-        *errorMessage = QString::fromLatin1("Unable to locate '%1' in '%2' (%3)").
-                        arg(pattern, corelibPattern.pattern(), matches.join(QString(QLatin1Char(','))));
-        return QString();
-    }
-    return corelibStrdup.front();
-}
-
 // Load a library into the debuggee. Currently requires
 // the QtCored4.pdb file to be present as we need "qstrdup"
 // as dummy symbol. This is ok ATM since dumpers only
@@ -132,9 +122,10 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
     // server, the debugger refuses to recognize it as a function.
     // Set up the call stack with a function of same signature (qstrdup)
     // and change the call register to LoadLibraryA() before executing "g".
-    // Prepare call.
-    const QString dummyFunc = resolveStrdup(syms, errorMessage);
-    if (dummyFunc.isEmpty())
+    // Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
+    // reason, the symbol is present in QtGui as well without type information.
+    QString dummyFunc = QLatin1String("*qstrdup");
+    if (resolveSymbol(syms, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
         return false;
     QString callCmd = QLatin1String(".call ");
     callCmd += dummyFunc;
@@ -145,80 +136,293 @@ static bool debuggeeLoadLibrary(CIDebugControl *ctl,
         return false;
     if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage))
         return false;
-    // This will hit a breakpoint
-    if (loadDebug)
-        qDebug() << " executing 'g'";
+    // This will hit a breakpoint.
     if (!CdbDebugEnginePrivate::executeDebuggerCommand(ctl, QString(QLatin1Char('g')), errorMessage))
+        return false;    
+    const HRESULT hr = ctl->WaitForEvent(0, waitTimeOutMS);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("WaitForEvent", hr);
         return false;
-    // @Todo: We cannot evaluate output here as it is asynchronous
+    }
     return true;
 }
 
-CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :
+// ------------------- CdbDumperHelper::DumperInputParameters
+struct CdbDumperHelper::DumperInputParameters {
+    DumperInputParameters(int protocolVersion_ = 1,
+                          int token_ = 1,
+                          quint64 Address_ = 0,
+                          bool dumpChildren_ = false,
+                          int extraInt0_ = 0,
+                          int extraInt1_ = 0,
+                          int extraInt2_ = 0,
+                          int extraInt3_ = 0);
+
+    QString command(const QString &dumpFunction) const;
+
+    int protocolVersion;
+    int token;
+    quint64 address;
+    bool dumpChildren;
+    int extraInt0;
+    int extraInt1;
+    int extraInt2;
+    int extraInt3;
+};
+
+CdbDumperHelper::DumperInputParameters::DumperInputParameters(int protocolVersion_,
+                                                              int token_,
+                                                              quint64 address_,
+                                                              bool dumpChildren_,
+                                                              int extraInt0_,
+                                                              int extraInt1_,
+                                                              int extraInt2_,
+                                                              int extraInt3_) :
+    protocolVersion(protocolVersion_),
+    token(token_),
+    address(address_),
+    dumpChildren(dumpChildren_),
+    extraInt0(extraInt0_),
+    extraInt1(extraInt1_),
+    extraInt2(extraInt2_),
+    extraInt3(extraInt3_)
+{
+}
+
+QString CdbDumperHelper::DumperInputParameters::command(const QString &dumpFunction) const
+{
+    QString rc;
+    QTextStream str(&rc);
+    str.setIntegerBase(16);
+    str << ".call " << dumpFunction << '(' << protocolVersion << ',' << token << ',';
+    str.setIntegerBase(16);
+    str << "0x" << address;
+    str.setIntegerBase(10);
+    str << ',' << (dumpChildren ? 1 : 0) << ',' << extraInt0 << ',' << extraInt1
+            << ',' << extraInt2 << ',' << extraInt3 << ')';
+    return rc;
+}
+
+// ------------------- CdbDumperHelper
+
+CdbDumperHelper::CdbDumperHelper(CdbComInterfaces *cif) :    
     m_state(NotLoaded),
-    m_cif(cif)
+    m_cif(cif),
+    m_inBufferAddress(0),
+    m_inBufferSize(0),
+    m_outBufferAddress(0),
+    m_outBufferSize(0),
+    m_buffer(0)
 {
 }
 
+CdbDumperHelper::~CdbDumperHelper()
+{
+    clearBuffer();
+}
+
+void CdbDumperHelper::clearBuffer()
+{
+    if (m_buffer) {
+        delete [] m_buffer;
+        m_buffer = 0;
+    }
+}
+
 void CdbDumperHelper::reset(const QString &library, bool enabled)
 {
     m_library = library;
     m_state = enabled ? NotLoaded : Disabled;
     m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
-    m_errorMessage.clear();
+    m_knownTypes.clear();
+    m_qtVersion.clear();
+    m_qtNamespace.clear();
+    m_inBufferAddress = m_outBufferAddress = 0;
+    m_inBufferSize = m_outBufferSize = 0;
+    clearBuffer();
 }
 
-bool CdbDumperHelper::moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint)
+// Attempt to load and initialize dumpers, give feedback
+// to user.
+void CdbDumperHelper::load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access)
 {
-    *ignoreNextBreakPoint = false;
-    bool ok = true; // report failure only once
-    switch (m_state) {
-    case Disabled:
-        break;
-    case NotLoaded:
-        // Load once QtCore is there.
-        if (name.contains(QLatin1String("QtCore"))) {
-            if (loadDebug)
-                qDebug() << Q_FUNC_INFO << '\n' << name << m_state << executionStatusString(m_cif->debugControl);
-            ok = debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
-                                     m_library, &m_errorMessage);
-            if (ok) {
-                m_state = Loading;
-                *ignoreNextBreakPoint = true;
-            } else {
-                m_state = Failed;
-            }
-            if (loadDebug)
-                qDebug() << m_state << executionStatusString(m_cif->debugControl);
+    enum Result { Failed, Succeeded, NoQtApp };
+
+    if (m_state != NotLoaded)
+        return;
+    manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000);
+    const QString messagePrefix = QLatin1String("dumper:");
+    QString message;
+    Result result = Failed;
+    do {
+        // Do we have Qt and are we already loaded by accident?
+        QStringList modules;
+        if (!getModuleNameList(m_cif->debugSymbols, &modules, &message))
+            break;
+        if (modules.filter(QLatin1String(qtCoreModuleNameC)).isEmpty()) {
+            message = QCoreApplication::translate("CdbDumperHelper", "The debugger does not appear to be Qt application.");
+            result = NoQtApp;
         }
-        break;
-    case Loading:
-        // Hurray, loaded. Now resolve the symbols we need
-        if (name.contains(QLatin1String("gdbmacros"))) {
-            ok = resolveSymbols(&m_errorMessage);
-            if (ok) {
-                m_state = Loaded;
-            } else {
-                m_state = Failed;
+        // Make sure the dumper lib is loaded.
+        if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) {
+            // Try to load
+            if (!debuggeeLoadLibrary(m_cif->debugControl, m_cif->debugClient, m_cif->debugSymbols, m_cif->debugDataSpaces,
+                                     m_library, &message)) {
+                break;
             }
+        } else {
+            access->showDebuggerOutput(messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded."));
         }
+        // Resolve symbols and do call to get types
+        if (!resolveSymbols(&message))
+            break;
+        if (!getKnownTypes(&message))
+            break;
+        message = QCoreApplication::translate("CdbDumperHelper", "Dumper library '%1' loaded.").arg(m_library);
+        result = Succeeded;
+    } while (false);
+    // eval state and notify user
+    switch (result) {
+    case Failed:
+        message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message);
+        access->showDebuggerOutput(messagePrefix, message);
+        access->showQtDumperLibraryWarning(message);
+        m_state = Disabled;
         break;
-    case Loaded:
+    case Succeeded:
+        access->showDebuggerOutput(messagePrefix, message);
+        access->showDebuggerOutput(messagePrefix, statusMessage());
+        m_state = Loaded;
         break;
-    case Failed:
+    case NoQtApp:
+        access->showDebuggerOutput(messagePrefix, message);
+        m_state = Disabled;
         break;
-    };    
-    return ok;
+    }    
+    if (loadDebug)
+        qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message;
+}
+
+QString CdbDumperHelper::statusMessage() const
+{
+    const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("CdbDumperHelper", "<none>") : m_qtNamespace;
+    return QCoreApplication::translate("CdbDumperHelper",
+                                       "%n known types, Qt version: %1, Qt namespace: %2",
+                                       0, QCoreApplication::CodecForTr,
+                                       m_knownTypes.size()).arg(m_qtVersion, nameSpace);
+}
+
+// Retrieve address and optionally size of a symbol.
+static inline bool getSymbolAddress(CIDebugSymbols *sg,
+                                    const QString &name,
+                                    quint64 *address,
+                                    ULONG *size /* = 0*/,
+                                    QString *errorMessage)
+{
+    // Get address
+    HRESULT hr = sg->GetOffsetByNameWide(name.utf16(), address);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("GetOffsetByNameWide", hr);
+        return false;
+    }
+    // Get size. Even works for arrays
+    if (size) {
+        ULONG64 moduleAddress;
+        ULONG type;
+        hr = sg->GetOffsetTypeId(*address, &type, &moduleAddress);
+        if (FAILED(hr)) {
+            *errorMessage = msgComFailed("GetOffsetTypeId", hr);
+            return false;
+        }
+        hr = sg->GetTypeSize(moduleAddress, type, size);
+        if (FAILED(hr)) {
+            *errorMessage = msgComFailed("GetTypeSize", hr);
+            return false;
+        }
+    } // size desired
+    return true;
 }
 
 bool CdbDumperHelper::resolveSymbols(QString *errorMessage)
 {    
-    // Resolve the symbols we need
-    m_dumpObjectSymbol = QLatin1String("qDumpObjectData440");
-    const bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk;
+    // Resolve the symbols we need (potentially namespaced).
+    // There is a 'qDumpInBuffer' in QtCore as well.
+    m_dumpObjectSymbol = QLatin1String("*qDumpObjectData440");
+    QString inBufferSymbol = QLatin1String("*qDumpInBuffer");
+    QString outBufferSymbol = QLatin1String("*qDumpOutBuffer");
+    const QString dumperModuleName = QLatin1String(dumperModuleNameC);
+    bool rc = resolveSymbol(m_cif->debugSymbols, &m_dumpObjectSymbol, errorMessage) == ResolveSymbolOk
+                    && resolveSymbol(m_cif->debugSymbols, dumperModuleName, &inBufferSymbol, errorMessage) == ResolveSymbolOk
+                    && resolveSymbol(m_cif->debugSymbols, dumperModuleName, &outBufferSymbol, errorMessage) == ResolveSymbolOk;
+    if (!rc)
+        return false;
+    //  Determine buffer addresses, sizes and alloc buffer
+    rc = getSymbolAddress(m_cif->debugSymbols, inBufferSymbol, &m_inBufferAddress, &m_inBufferSize, errorMessage)
+         && getSymbolAddress(m_cif->debugSymbols, outBufferSymbol, &m_outBufferAddress, &m_outBufferSize, errorMessage);
+    if (!rc)
+        return false;
+    m_buffer = new char[qMax(m_inBufferSize, m_outBufferSize)];
     if (loadDebug)
-        qDebug() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol;
-    return rc;
+        qDebug().nospace() << Q_FUNC_INFO << '\n' << rc << m_dumpObjectSymbol
+                << " buffers at 0x" << QString::number(m_inBufferAddress, 16) << ','
+                << m_inBufferSize << "; 0x"
+                << QString::number(m_outBufferAddress, 16) << ',' << m_outBufferSize << '\n';
+    return true;
+}
+
+bool CdbDumperHelper::getKnownTypes(QString *errorMessage)
+{
+    QByteArray output;
+    if (!callDumper(DumperInputParameters(1), &output, errorMessage)) {
+        return false;
+    }
+    if (!parseQueryDumperOutput(output, &m_knownTypes, &m_qtVersion, &m_qtNamespace)) {
+     *errorMessage = QString::fromLatin1("Unable to parse the dumper output: '%1'").arg(QString::fromAscii(output));
+    }
+    if (loadDebug)
+        qDebug() << Q_FUNC_INFO << m_knownTypes << m_qtVersion  << m_qtNamespace;
+    return true;
+}
+
+bool CdbDumperHelper::callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage)
+{
+    IgnoreDebugEventCallback devNull;
+    EventCallbackRedirector eventRedir(m_cif->debugClient, &devNull);
+    const QString callCmd = p.command(m_dumpObjectSymbol);
+    // Set up call and a temporary breakpoint after it.
+    if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
+        return false;    
+    // Go and filter away break event
+    if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, QString(QLatin1Char('g')), errorMessage))
+        return false;
+    HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("WaitForEvent", hr);
+        return false;
+    }
+    // Read output
+    hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("ReadVirtual", hr);
+        return false;
+    }
+    // see QDumper implementation
+    const char result = m_buffer[0];
+    switch (result) {
+    case 't':
+        break;
+    case '+':
+        *errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
+        return false;
+    case 'f':
+        *errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
+        return false;
+    default:
+        *errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
+        return false;
+    }
+    *output = QByteArray(m_buffer + 1);
+    return true;
 }
 
 } // namespace Internal
diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h
index 8947acd134c..c5be52e22f4 100644
--- a/src/plugins/debugger/cdb/cdbdumperhelper.h
+++ b/src/plugins/debugger/cdb/cdbdumperhelper.h
@@ -30,55 +30,77 @@
 #ifndef CDBDUMPERHELPER_H
 #define CDBDUMPERHELPER_H
 
-#include <QtCore/QString>
+#include <QtCore/QStringList>
 
 namespace Debugger {
 namespace Internal {
 
 struct CdbComInterfaces;
-
-// For code clarity, all the stuff related to custom dumpers
-// goes here.
-// "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.
+class IDebuggerManagerAccessForEngines;
+class DebuggerManager;
+
+/* For code clarity, all the stuff related to custom dumpers
+ * goes here.
+ * "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.
+ * Loading the dumpers requires making the debuggee call functions
+ * (LoadLibrary() and the dumper functions). This only works if the
+ * debuggee is in a 'well-defined' breakpoint state (such as at 'main()').
+ * Calling the load functions from an IDebugEvent callback causes
+ * WaitForEvent() to fail with unknown errors. Calling the load functions from an
+ * non 'well-defined' (arbitrary) breakpoint state will cause LoadLibrary
+ * to trigger an access violations.
+ * Currently, we call the load function when stopping at 'main()' for which
+ * we set a temporary break point if the user does not want to stop there. */
 
 class CdbDumperHelper
 {
+    Q_DISABLE_COPY(CdbDumperHelper)
 public:
    enum State {
         Disabled,
         NotLoaded,
-        Loading,
         Loaded,
         Failed
     };
 
     explicit CdbDumperHelper(CdbComInterfaces *cif);
+    ~CdbDumperHelper();
+
+    State state() const { return m_state; }
+    operator bool() const { return m_state == Loaded; }
 
     // Call before starting the debugger
     void reset(const QString &library, bool enabled);
 
-    // Call from the module loaded event handler.
-    // It will load the dumper library and resolve the required symbols
-    // when appropriate.
-    bool moduleLoadHook(const QString &name, bool *ignoreNextBreakPoint);
-
-    State state() const { return m_state; }
-
-    QString errorMessage() const { return m_errorMessage; }
-    QString library() const { return m_library; }
+    // Call in a temporary breakpoint state to actually load.
+    void load(DebuggerManager *manager, IDebuggerManagerAccessForEngines *access);
 
 private:
+    struct DumperInputParameters;
+
+    void clearBuffer();
     bool resolveSymbols(QString *errorMessage);
+    bool getKnownTypes(QString *errorMessage);
+    bool callDumper(const DumperInputParameters &p, QByteArray *output, QString *errorMessage);
+    inline QString statusMessage() const;
 
     State m_state;
     CdbComInterfaces *m_cif;
 
     QString m_library;
     QString m_dumpObjectSymbol;
-    QString m_errorMessage;
+    QStringList m_knownTypes;
+    QString m_qtVersion;
+    QString m_qtNamespace;
+
+    quint64 m_inBufferAddress;
+    unsigned long m_inBufferSize;
+    quint64 m_outBufferAddress;
+    unsigned long m_outBufferSize;
+    char *m_buffer;
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/cdb/cdbmodules.cpp b/src/plugins/debugger/cdb/cdbmodules.cpp
index d3e4250e5c8..8aef06ae03f 100644
--- a/src/plugins/debugger/cdb/cdbmodules.cpp
+++ b/src/plugins/debugger/cdb/cdbmodules.cpp
@@ -32,25 +32,47 @@
 #include "cdbdebugengine_p.h"
 
 #include <QtCore/QFileInfo>
+#include <QtCore/QRegExp>
 
 namespace Debugger {
 namespace Internal {
 
-bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage)
-{    
-    modules->clear();
+static inline bool getModuleCount(CIDebugSymbols *syms, ULONG *count, QString *errorMessage)
+{
+    *count = 0;
     ULONG loadedCount, unloadedCount;
-    HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
+    const HRESULT hr = syms->GetNumberModules(&loadedCount, &unloadedCount);
     if (FAILED(hr)) {
         *errorMessage= msgComFailed("GetNumberModules", hr);
         return false;
     }
-    // retrieve array of parameters
-    const ULONG count = loadedCount + unloadedCount;
+    *count = loadedCount + unloadedCount;
+    return true;
+}
+
+bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage)
+{
+    ULONG count;
+    modules->clear();
+    if (!getModuleCount(syms, &count, errorMessage))
+        return false;
+    WCHAR wszBuf[MAX_PATH];
+    for (ULONG m = 0; m < count; m++)
+        if (SUCCEEDED(syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0)))
+                modules->push_back(QString::fromUtf16(wszBuf));
+    return true;
+}
+
+bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage)
+{    
+    ULONG count;
+    modules->clear();
+    if (!getModuleCount(syms, &count, errorMessage))
+        return false;
     QVector<DEBUG_MODULE_PARAMETERS> parameters(count);
     DEBUG_MODULE_PARAMETERS *parmPtr = &(*parameters.begin());
     memset(parmPtr, 0, sizeof(DEBUG_MODULE_PARAMETERS) * count);
-    hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
+    HRESULT hr = syms->GetModuleParameters(count, 0, 0u, parmPtr);
     // E_INVALIDARG indicates 'Partial results' according to docu
     if (FAILED(hr) && hr != E_INVALIDARG) {
         *errorMessage= msgComFailed("GetModuleParameters", hr);
@@ -67,7 +89,7 @@ bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorM
                             && (p.SymbolType != DEBUG_SYMTYPE_NONE);
             module.startAddress = hexPrefix + QString::number(p.Base, 16);
             module.endAddress = hexPrefix + QString::number((p.Base + p.Size), 16);
-            hr = syms ->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
+            hr = syms->GetModuleNameStringWide(DEBUG_MODNAME_IMAGE, m, 0, wszBuf, MAX_PATH - 1, 0);
             if (FAILED(hr) && hr != E_INVALIDARG) {
                 *errorMessage= msgComFailed("GetModuleNameStringWide", hr);
                 return false;
@@ -111,10 +133,10 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
     return true;
 }
 
-// Add missing the module specifier: "main" -> "project!main"
-
-ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
-                                  QString *errorMessage)
+// Helper for the resolveSymbol overloads.
+static ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
+                                         QStringList *matches,
+                                         QString *errorMessage)
 {
     // Is it an incomplete symbol?
     if (symbol->contains(QLatin1Char('!')))
@@ -123,20 +145,63 @@ ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
     if (*symbol == QLatin1String("qMain"))
         *symbol = QLatin1String("main");
     // resolve
-    QStringList matches;
-    if (!searchSymbols(syms, *symbol, &matches, errorMessage))
+    if (!searchSymbols(syms, *symbol, matches, errorMessage))
         return ResolveSymbolError;
-    if (matches.empty())
+    if (matches->empty())
         return ResolveSymbolNotFound;
-    *symbol = matches.front();
-    if (matches.size() > 1) {
+    *symbol = matches->front();
+    if (matches->size() > 1) {
         *errorMessage = QString::fromLatin1("Ambiguous symbol '%1': %2").
-                        arg(*symbol, matches.join(QString(QLatin1Char(' '))));
+                        arg(*symbol, matches->join(QString(QLatin1Char(' '))));
         return ResolveSymbolAmbiguous;
     }
     return ResolveSymbolOk;
 }
 
+// Add missing the module specifier: "main" -> "project!main"
+ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol,
+                                  QString *errorMessage)
+{
+    QStringList matches;
+    return resolveSymbol(syms, symbol, &matches, errorMessage);
+}
+
+ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage)
+{
+    QStringList matches;
+    const ResolveSymbolResult r1 = resolveSymbol(syms, symbol, &matches, errorMessage);
+    switch (r1) {
+    case ResolveSymbolOk:
+    case ResolveSymbolNotFound:
+    case ResolveSymbolError:
+        return r1;
+    case ResolveSymbolAmbiguous:
+        break;
+    }
+    // Filter out
+    errorMessage->clear();
+    const QRegExp re(pattern);
+    if (!re.isValid()) {
+        *errorMessage = QString::fromLatin1("Internal error: Invalid pattern '%1'.").arg(pattern);
+        return ResolveSymbolError;
+    }
+    const QStringList filteredMatches = matches.filter(re);
+    if (filteredMatches.size() == 1) {
+        *symbol = filteredMatches.front();
+        return ResolveSymbolOk;
+    }
+    // something went wrong
+    const QString matchesString = matches.join(QString(QLatin1Char(',')));
+    if (filteredMatches.empty()) {
+        *errorMessage = QString::fromLatin1("None of symbols '%1' found for '%2' matches '%3'.").
+                        arg(matchesString, *symbol, pattern);
+        return ResolveSymbolNotFound;
+    }
+    *errorMessage = QString::fromLatin1("Ambiguous match of symbols '%1' found for '%2' (%3)").
+                        arg(matchesString, *symbol, pattern);
+    return ResolveSymbolAmbiguous;
+}
+
 // List symbols of a module
 bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
                       QList<Symbol> *symbols, QString *errorMessage)
diff --git a/src/plugins/debugger/cdb/cdbmodules.h b/src/plugins/debugger/cdb/cdbmodules.h
index 9df7dbf28b8..79030aea243 100644
--- a/src/plugins/debugger/cdb/cdbmodules.h
+++ b/src/plugins/debugger/cdb/cdbmodules.h
@@ -30,8 +30,7 @@
 #ifndef CDBMODULES_H
 #define CDBMODULES_H
 
-#include <QtCore/QList>
-#include <QtCore/QString>
+#include <QtCore/QStringList>
 
 #include "cdbcom.h"
 
@@ -42,7 +41,8 @@ class Module;
 class Symbol;
 
 bool getModuleList(CIDebugSymbols *syms, QList<Module> *modules, QString *errorMessage);
-// Search symbols matching a pattern
+bool getModuleNameList(CIDebugSymbols *syms, QStringList *modules, QString *errorMessage);
+// Search symbols matching a pattern. Does not filter on module names.
 bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
                    QStringList *matches, QString *errorMessage);
 
@@ -52,8 +52,12 @@ bool searchSymbols(CIDebugSymbols *syms, const QString &pattern,
 enum ResolveSymbolResult { ResolveSymbolOk, ResolveSymbolAmbiguous,
                            ResolveSymbolNotFound, ResolveSymbolError };
 
+// Resolve a symbol that is unique to all modules
 ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, QString *symbol, QString *errorMessage);
 
+// Resolve symbol overload with an additional regexp pattern to filter on modules.
+ResolveSymbolResult resolveSymbol(CIDebugSymbols *syms, const QString &pattern, QString *symbol, QString *errorMessage);
+
 // List symbols of a module
 bool getModuleSymbols(CIDebugSymbols *syms, const QString &moduleName,
                       QList<Symbol> *symbols, QString *errorMessage);
diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h
index e5117845a95..821c697fb27 100644
--- a/src/plugins/debugger/debuggermanager.h
+++ b/src/plugins/debugger/debuggermanager.h
@@ -145,6 +145,7 @@ private:
     friend class CdbDebugEventCallback;
     friend class ScriptEngine;
     friend struct CdbDebugEnginePrivate;
+    friend class CdbDumperHelper;
 
     // called from the engines after successful startup
     virtual void notifyInferiorStopRequested() = 0;
@@ -171,6 +172,11 @@ private:
     virtual void reloadModules() = 0;
     virtual void reloadSourceFiles() = 0;
     virtual void reloadRegisters() = 0;
+
+    virtual bool qtDumperLibraryEnabled() const = 0;
+    virtual QString qtDumperLibraryName() const = 0;
+    virtual void showQtDumperLibraryWarning(const QString &details = QString()) = 0;
+
 };
 
 
@@ -252,10 +258,6 @@ public slots:
 
     void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever
 
-    bool qtDumperLibraryEnabled() const;
-    QString qtDumperLibraryName() const;
-    void showQtDumperLibraryWarning(const QString &details);
-
 private slots:
     void showDebuggerOutput(const QString &prefix, const QString &msg);
     void showDebuggerInput(const QString &prefix, const QString &msg);
@@ -307,6 +309,10 @@ private:
     QList<QDockWidget*> dockWidgets() const { return m_dockWidgets; }
     void createDockWidgets();
 
+    virtual bool qtDumperLibraryEnabled() const;
+    virtual QString qtDumperLibraryName() const;
+    virtual void showQtDumperLibraryWarning(const QString &details = QString());
+
     //    
     // internal implementation
     //  
diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp
index 5b30a331c30..b9b35ce4d76 100644
--- a/src/plugins/debugger/gdbengine.cpp
+++ b/src/plugins/debugger/gdbengine.cpp
@@ -4135,16 +4135,16 @@ void GdbEngine::tryLoadDebuggingHelpers()
 
     PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
     m_debuggingHelperState = DebuggingHelperUnavailable;
-    if (!q->qtDumperLibraryEnabled())
+    if (!qq->qtDumperLibraryEnabled())
         return;
-    const QString lib = q->qtDumperLibraryName();
+    const QString lib = qq->qtDumperLibraryName();
     //qDebug() << "DUMPERLIB: " << lib;
     // @TODO: same in CDB engine...
     const QFileInfo fi(lib);
     if (!fi.exists()) {
         const QString msg = tr("The dumper library '%1' does not exist.").arg(lib);
         debugMessage(msg);
-        q->showQtDumperLibraryWarning(msg);
+        qq->showQtDumperLibraryWarning(msg);
         return;
     }
 
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index 993bd1a914a..cc1a9784870 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -299,5 +299,58 @@ QString sizeofTypeExpression(const QString &type)
     return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')');
 }
 
+/* Parse 'query' (1) protocol response of the custom dumpers:
+ * "'dumpers=["QByteArray","QDateTime",..."std::basic_string",],
+ * qtversion=["4","5","1"],namespace="""' */
+
+bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace)
+{
+    types->clear();
+    qtVersion->clear();
+    qtNamespace->clear();
+    const QChar equals = QLatin1Char('=');
+    const QChar doubleQuote = QLatin1Char('"');
+    const QChar openingBracket = QLatin1Char('[');
+    const QChar closingBracket = QLatin1Char(']');
+    const QString dumperKey = QLatin1String("dumpers");
+    const QString versionKey = QLatin1String("qtversion");
+    const QString namespaceKey = QLatin1String("namespace");
+
+    const QString s = QString::fromLatin1(a);
+    const int size = a.size();
+    for (int pos = 0; pos < size; ) {
+        // split into keyword / value pairs
+        const int equalsPos = s.indexOf(equals, pos);
+        if (equalsPos == -1)
+            break;
+        const QStringRef keyword = s.midRef(pos, equalsPos - pos);
+        // Array or flat value. Cut out value without delimiters
+        int valuePos = equalsPos + 1;
+        const QChar endChar = s.at(valuePos) == doubleQuote ? doubleQuote : closingBracket;
+        valuePos++;
+        int endValuePos = s.indexOf(endChar, valuePos);
+        if (endValuePos == -1)
+            return false;
+        QString value = s.mid(valuePos, endValuePos - valuePos);
+        pos = endValuePos;
+        // Evaluate
+        if (keyword == namespaceKey) {
+            *qtNamespace = value;
+        } else {
+            if (keyword == versionKey) {
+                *qtVersion = value.remove(doubleQuote).replace(QLatin1Char(','), QLatin1Char('.'));
+            } else {
+                if (keyword == dumperKey) {
+                    *types = value.remove(doubleQuote).split(QLatin1Char(','));
+                }
+            }
+        }
+        // find next keyword
+        while (pos < size && !s.at(pos).isLetterOrNumber())
+            pos++;
+    }
+    return true;
+}
+
 }
 }
diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h
index 6c7dbb9143a..392f0e0c3fd 100644
--- a/src/plugins/debugger/watchutils.h
+++ b/src/plugins/debugger/watchutils.h
@@ -34,6 +34,7 @@
 
 QT_BEGIN_NAMESPACE
 class QString;
+class QByteArray;
 QT_END_NAMESPACE
 
 namespace Debugger {
@@ -63,6 +64,8 @@ QString extractTypeFromPTypeOutput(const QString &str);
 bool isIntOrFloatType(const QString &type);
 QString sizeofTypeExpression(const QString &type);
 
+// Parse 'query' (1) protocol response of the custom dumpers
+bool parseQueryDumperOutput(const QByteArray &a, QStringList *types, QString *qtVersion, QString *qtNamespace);
 
 } // namespace Internal
 } // namespace Debugger
-- 
GitLab