From 536320ea1ac63c83b6e6777a0dabe7d47e9ba8cd Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Thu, 26 Mar 2009 16:49:28 +0100
Subject: [PATCH] Start stack frame context/symbol group classes for CDB

---
 src/plugins/debugger/cdb/cdb.pri              |   8 +-
 src/plugins/debugger/cdb/cdbdebugengine.cpp   | 247 ++++++------------
 src/plugins/debugger/cdb/cdbdebugengine_p.h   |  21 +-
 .../debugger/cdb/cdbstacktracecontext.cpp     | 161 ++++++++++++
 .../debugger/cdb/cdbstacktracecontext.h       |  86 ++++++
 .../debugger/cdb/cdbsymbolgroupcontext.cpp    | 177 +++++++++++++
 .../debugger/cdb/cdbsymbolgroupcontext.h      | 110 ++++++++
 src/plugins/debugger/gdbengine.cpp            |  12 +-
 src/plugins/debugger/stackhandler.cpp         |  10 +
 src/plugins/debugger/stackhandler.h           |   3 +
 10 files changed, 642 insertions(+), 193 deletions(-)
 create mode 100644 src/plugins/debugger/cdb/cdbstacktracecontext.cpp
 create mode 100644 src/plugins/debugger/cdb/cdbstacktracecontext.h
 create mode 100644 src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
 create mode 100644 src/plugins/debugger/cdb/cdbsymbolgroupcontext.h

diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri
index 27186577c30..8ba7d3a6865 100644
--- a/src/plugins/debugger/cdb/cdb.pri
+++ b/src/plugins/debugger/cdb/cdb.pri
@@ -22,12 +22,16 @@ HEADERS += \
     $$PWD/cdbdebugengine.h \
     $$PWD/cdbdebugengine_p.h \
     $$PWD/cdbdebugeventcallback.h \
-    $$PWD/cdbdebugoutput.h
+    $$PWD/cdbdebugoutput.h \
+    $$PWD/cdbsymbolgroupcontext.h \
+    $$PWD/cdbstacktracecontext.h
 
 SOURCES += \
     $$PWD/cdbdebugengine.cpp \
     $$PWD/cdbdebugeventcallback.cpp \
-    $$PWD/cdbdebugoutput.cpp
+    $$PWD/cdbdebugoutput.cpp \
+    $$PWD/cdbsymbolgroupcontext.cpp \
+    $$PWD/cdbstacktracecontext.cpp
 } else {
    message("Debugging Tools for Windows could not be found in $$CDB_PATH")
 }
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp
index 73d3953ffb3..7d054e68dd4 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine.cpp
+++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp
@@ -29,6 +29,8 @@
 
 #include "cdbdebugengine.h"
 #include "cdbdebugengine_p.h"
+#include "cdbsymbolgroupcontext.h"
+#include "cdbstacktracecontext.h"
 
 #include "debuggermanager.h"
 #include "breakhandler.h"
@@ -55,7 +57,12 @@
 static const char *dbgEngineDllC = "dbgeng";
 static const char *debugCreateFuncC = "DebugCreate";
 
-static QString msgDebugEngineComResult(HRESULT hr)
+static const char *localSymbolRootC = "local";
+
+namespace Debugger {
+namespace Internal {
+
+QString msgDebugEngineComResult(HRESULT hr)
 {
     switch (hr) {
         case S_OK:
@@ -87,13 +94,12 @@ static QString msgStackIndexOutOfRange(int idx, int size)
     return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size);
 }
 
-static QString msgComFailed(const char *func, HRESULT hr)
+QString msgComFailed(const char *func, HRESULT hr)
 {
     return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(func), msgDebugEngineComResult(hr));
 }
 
-namespace Debugger {
-namespace Internal {
+static const char *msgNoStackTraceC = "Internal error: no stack trace present.";
 
 DebuggerEngineLibrary::DebuggerEngineLibrary() :
     m_debugCreate(0)
@@ -138,6 +144,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn
     m_engine(engine),
     m_debuggerManager(parent),
     m_debuggerManagerAccess(parent->engineInterface()),
+    m_currentStackTrace(0),
     m_mode(AttachCore)
 {   
 }
@@ -205,6 +212,7 @@ IDebuggerEngine *CdbDebugEngine::create(DebuggerManager *parent)
 
 CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
 {
+    cleanStackTrace();
     if (m_pDebugClient)
         m_pDebugClient->Release();
     if (m_pDebugControl)
@@ -217,6 +225,17 @@ CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
         m_pDebugRegisters->Release();
 }
 
+void CdbDebugEnginePrivate::cleanStackTrace()
+{
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO;
+
+    if (m_currentStackTrace) {
+        delete m_currentStackTrace;
+        m_currentStackTrace = 0;
+    }
+}
+
 CdbDebugEngine::CdbDebugEngine(DebuggerManager *parent) :
     IDebuggerEngine(parent),
     m_d(new CdbDebugEnginePrivate(parent, this))
@@ -366,6 +385,7 @@ void CdbDebugEngine::processTerminated(unsigned long exitCode)
     if (debugCDB)
         qDebug() << Q_FUNC_INFO << exitCode;
 
+    m_d->cleanStackTrace();
     m_d->setDebuggeeHandles(0, 0);
     m_d->m_debuggerManagerAccess->notifyInferiorExited();
     m_d->m_debuggerManager->exitDebugger();
@@ -377,6 +397,7 @@ void CdbDebugEngine::exitDebugger()
         qDebug() << Q_FUNC_INFO;
 
     if (m_d->m_hDebuggeeProcess) {
+        m_d->cleanStackTrace();
         // Terminate or detach if we are running
         HRESULT hr;
         switch (m_d->m_mode) {
@@ -407,47 +428,22 @@ void CdbDebugEngine::exitDebugger()
     killWatchTimer();
 }
 
-// Retrieve a symbol
-static WatchData symbolToWatchData(ULONG index, const QString &namePrefix,
-                                   IDebugSymbolGroup2 *pDbgSymGroup)
+class ModelBuildIterator {
+public:
+    explicit ModelBuildIterator(WatchHandler *wh) : m_wh(wh) {}
+
+    ModelBuildIterator & operator*() { return *this; }
+    ModelBuildIterator &operator=(const WatchData &wd);
+    ModelBuildIterator &operator++() { return *this; }
+
+private:
+    WatchHandler *m_wh;
+};
+
+ModelBuildIterator &ModelBuildIterator::operator=(const WatchData &wd)
 {
-    // retrieve symbol names and value strings
-    ULONG nameLength;
-    static WCHAR nameBuffer[MAX_PATH + 1];
-    // Name
-    pDbgSymGroup->GetSymbolNameWide(index, nameBuffer, MAX_PATH, &nameLength);
-    nameBuffer[nameLength] = 0;
-    const QString name = QString::fromUtf16(nameBuffer);
-    // Type name
-    pDbgSymGroup->GetSymbolTypeNameWide(index, nameBuffer, MAX_PATH, &nameLength);
-    nameBuffer[nameLength] = 0;
-    const QString type = QString::fromUtf16(nameBuffer);
-    // Value
-    QString value;
-    const HRESULT hr = pDbgSymGroup->GetSymbolValueTextWide(index, nameBuffer, MAX_PATH, &nameLength);
-    if (SUCCEEDED(hr)) {
-        nameBuffer[nameLength] = 0;
-        value = QString::fromUtf16(nameBuffer);
-    } else {
-        value = QLatin1String("<unknown>");
-    }
-    WatchData wd;
-    wd.iname =namePrefix + name;
-    wd.name = name;
-    wd.value = value;
-    wd.type = type;
-    if (isPointerType(type)) {
-        wd.setTypeUnneeded();
-        wd.setValueUnneeded();
-    } else {
-        wd.setAllUnneeded();
-    }
-    if (debugCDB) {
-        qDebug() << Q_FUNC_INFO << index << "state=0x" << QString::number(wd.state, 16)
-                << wd.name << " type=" << wd.type << " (" << type << ')'
-                << " value " << wd.value << " (" << value << ')';
-    }
-    return wd;
+    m_wh->insertData(wd);
+    return *this;
 }
 
 bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
@@ -456,82 +452,36 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
 {
     if (debugCDB)
         qDebug() << Q_FUNC_INFO << frameIndex;
-
-    CdbStackTrace cdbStackTrace;
-    if (!getCdbStrackTrace(&cdbStackTrace, errorMessage))
-        return false;
-
-    if ((unsigned)frameIndex >= cdbStackTrace.frameCount) {
-        *errorMessage = msgStackIndexOutOfRange(frameIndex, cdbStackTrace.frameCount);
-        return false;
-    }
-
-    IDebugSymbolGroup2 *pDbgSymGroup = 0;
-    DEBUG_SYMBOL_PARAMETERS *symParams = 0;
     bool success = false;
-
+    wh->cleanup();
     do {
-        HRESULT hr = m_pDebugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &pDbgSymGroup);
-        if (FAILED(hr)) {
-            *errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
-            break;
-        }
-
-        hr = m_pDebugSymbols->SetScope(0, cdbStackTrace.frames + frameIndex, NULL, 0);
-        if (FAILED(hr)) {
-            *errorMessage = msgComFailed("SetScope", hr);
-            break;
-        }
-        // refresh with current frame
-        hr = m_pDebugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, pDbgSymGroup, &pDbgSymGroup);
-        if (FAILED(hr)) {
-            *errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
-            break;
-        }
-
-        ULONG symbolCount;
-        hr = pDbgSymGroup->GetNumberSymbols(&symbolCount);
-        if (FAILED(hr)) {
-            *errorMessage = msgComFailed("GetNumberSymbols", hr);
+        if (!m_currentStackTrace) {
+            *errorMessage = QLatin1String(msgNoStackTraceC);
             break;
         }
 
-        symParams = new DEBUG_SYMBOL_PARAMETERS[symbolCount];
-        hr = pDbgSymGroup->GetSymbolParameters(0, symbolCount, symParams);
-        if (FAILED(hr)) {
-            *errorMessage = msgComFailed("GetSymbolParameters", hr);
+        CdbSymbolGroupContext *sgc = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage);
+        if (!sgc) {
             break;
         }
-        wh->cleanup();
-        // retrieve symbol names and value strings.
-        // Add a dummy place holder in case children are needed
-        const QString localPrefix = QLatin1String("local.");
-        for (ULONG s = 0 ; s < symbolCount ; s++ ) {
-            WatchData wd = symbolToWatchData(s, localPrefix, pDbgSymGroup);
-            if (wd.isSomethingNeeded()) {
-                wh->insertData(wd.pointerChildPlaceHolder());
-                wd.setAllUnneeded();
-                wd.setChildCount(1);
-            }
-            wh->insertData(wd);
-        }
-        wh->rebuildModel();
+        ModelBuildIterator it(wh);
+        sgc->getSymbols(sgc->prefix(), it);
         success = true;
     } while (false);
+    wh->rebuildModel();
 
-    delete [] symParams;
-    if (pDbgSymGroup)
-        pDbgSymGroup->Release();
     return success;
 }
 
-
 void CdbDebugEngine::updateWatchModel()
 {
     WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
     const QList<WatchData> incomplete = watchHandler->takeCurrentIncompletes();
+
     if (debugCDB)
         qDebug() << Q_FUNC_INFO << incomplete.size();
+    foreach (const WatchData& wd, incomplete)
+        qDebug() << Q_FUNC_INFO << wd.toString();
 }
 
 void CdbDebugEngine::stepExec()
@@ -540,8 +490,9 @@ void CdbDebugEngine::stepExec()
         qDebug() << Q_FUNC_INFO;
 
     //m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
-    HRESULT hr;
-    hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO);
+    m_d->cleanStackTrace();
+    const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO);
+    Q_UNUSED(hr)
     m_d->m_bIgnoreNextDebugEvent = true;
     startWatchTimer();
 }
@@ -594,6 +545,7 @@ void CdbDebugEngine::nextExec()
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
 
+    m_d->cleanStackTrace();
     const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
     if (SUCCEEDED(hr)) {
         startWatchTimer();
@@ -612,6 +564,7 @@ void CdbDebugEngine::nextIExec()
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
 
+    m_d->cleanStackTrace();
     const HRESULT hr = m_d->m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
     if (SUCCEEDED(hr)) {
         startWatchTimer();
@@ -625,6 +578,7 @@ void CdbDebugEngine::continueInferior()
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
 
+    m_d->cleanStackTrace();
     killWatchTimer();
     m_d->m_debuggerManager->resetLocation();
 
@@ -714,7 +668,7 @@ void CdbDebugEngine::activateFrame(int frameIndex)
         }
 
         const StackFrame &frame = stackHandler->currentFrame();
-        if (frame.file.isEmpty() || !QFileInfo(frame.file).isReadable()) {
+        if (!frame.isUsable()) {
             errorMessage = QString::fromLatin1("%1: file %2 unusable.").
                            arg(QLatin1String(Q_FUNC_INFO), frame.file);
             break;
@@ -933,78 +887,29 @@ void CdbDebugEnginePrivate::updateThreadList()
     th->setThreads(threads);
 }
 
-// Get CDB stack trace
-bool CdbDebugEnginePrivate::getCdbStrackTrace(CdbStackTrace *st, QString *errorMessage)
-{
-    HRESULT hr = m_pDebugSystemObjects->SetCurrentThreadId(m_currentThreadId);
-    if (FAILED(hr)) {
-        *errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
-                        arg(QString::fromLatin1(Q_FUNC_INFO)).
-                        arg(m_currentThreadId).
-                        arg(msgDebugEngineComResult(hr));
-        return false;
-    }
-    hr = m_pDebugControl->GetStackTrace(0, 0, 0, st->frames, CdbStackTrace::maxFrames, &(st->frameCount));
-    if (FAILED(hr)) {
-        *errorMessage = *errorMessage = msgComFailed("GetStackTrace", hr);
-        return false;
-    }
-    return true;
-}
-
-bool CdbDebugEnginePrivate::getStackTrace(QList<StackFrame> *stackFrames,
-                                          int *current, QString *errorMessage)
-{
-    stackFrames->clear();
-    *current = -1;
-    // Get the CDB trace and convert into debugger plugin structures
-    CdbStackTrace cdbStackTrace;
-    if (!getCdbStrackTrace(&cdbStackTrace, errorMessage))
-        return false;
-
-    WCHAR wszBuf[MAX_PATH];
-    for (ULONG i=0; i < cdbStackTrace.frameCount; ++i) {
-        StackFrame frame;
-        frame.line = 0;
-        frame.level = i;
-        frame.address = QString::fromLatin1("0x%1").arg(cdbStackTrace.frames[i].InstructionOffset, 0, 16);
-
-        m_pDebugSymbols->GetNameByOffsetWide(cdbStackTrace.frames[i].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
-        frame.function = QString::fromUtf16(wszBuf);
-
-        ULONG ulLine;
-        ULONG ulFileNameSize;
-        ULONG64 ul64Displacement;
-        const HRESULT hr = m_pDebugSymbols->GetLineByOffsetWide(cdbStackTrace.frames[i].InstructionOffset, &ulLine, wszBuf, MAX_PATH, &ulFileNameSize, &ul64Displacement);
-        if (SUCCEEDED(hr)) {
-            frame.line = ulLine;
-            frame.file = QString::fromUtf16(wszBuf, ulFileNameSize);
-        }
-        stackFrames->append(frame);
-    }
-
-    // find the first usable frame and select it
-    const int count = stackFrames->count();
-    for (int i=0; i < count; ++i) {
-        const StackFrame &frame = stackFrames->at(i);
-        const bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
-        if (usable) {
-            *current = i;
-            break;
-        }
-    }
-    return true;
-}
-
 void CdbDebugEnginePrivate::updateStackTrace()
 {
     if (debugCDB)
         qDebug() << Q_FUNC_INFO;
-    QList<StackFrame> stackFrames;
-    int current;
+    // Create a new context
+    cleanStackTrace();
     QString errorMessage;
-    if (getStackTrace(&stackFrames, &current, &errorMessage))
-        qWarning("%s", qPrintable(errorMessage));
+    m_currentStackTrace =
+            CdbStackTraceContext::create(m_pDebugControl, m_pDebugSystemObjects,
+                                         m_pDebugSymbols, m_currentThreadId, &errorMessage);
+    if (!m_currentStackTrace) {
+        qWarning("%s: failed to create trace context: %s", Q_FUNC_INFO, qPrintable(errorMessage));
+        return;
+    }
+    const QList<StackFrame> stackFrames = m_currentStackTrace->frames();
+    // find the first usable frame and select it
+    int current = -1;
+    const int count = stackFrames.count();
+    for (int i=0; i < count; ++i)
+        if (stackFrames.at(i).isUsable()) {
+            current = i;
+            break;
+        }
 
     m_debuggerManagerAccess->stackHandler()->setFrames(stackFrames);
     if (current >= 0) {
diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h
index e6235569a07..e1991f707de 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine_p.h
+++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h
@@ -42,6 +42,8 @@ namespace Internal {
 class DebuggerManager;
 class IDebuggerManagerAccessForEngines;
 class WatchHandler;
+class CdbSymbolGroupContext;
+class CdbStackTraceContext;
 
 // Thin wrapper around the 'DBEng' debugger engine shared library
 // which is loaded at runtime.
@@ -61,16 +63,6 @@ private:
     DebugCreateFunction m_debugCreate;
 };
 
-
-// Helper struct for stack traces
-struct CdbStackTrace {
-    CdbStackTrace() : frameCount(0) {}
-    enum { maxFrames = 100 };
-
-    ULONG frameCount;
-    DEBUG_STACK_FRAME frames[maxFrames];
-};
-
 struct CdbDebugEnginePrivate
 {    
     explicit CdbDebugEnginePrivate(DebuggerManager *parent,  CdbDebugEngine* engine);
@@ -84,10 +76,9 @@ struct CdbDebugEnginePrivate
     void updateThreadList();
     void updateStackTrace();
     bool updateLocals(int frameIndex, WatchHandler *wh, QString *errorMessage);
-    bool getCdbStrackTrace(CdbStackTrace *st, QString *errorMessage);
-    bool getStackTrace(QList<StackFrame> *stackFrames, int *current, QString *errorMessage);
     void handleDebugOutput(const char* szOutputString);
     void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP);
+    void cleanStackTrace();
 
     HANDLE                  m_hDebuggeeProcess;
     HANDLE                  m_hDebuggeeThread;
@@ -106,10 +97,16 @@ struct CdbDebugEnginePrivate
     CdbDebugEngine* m_engine;
     DebuggerManager *m_debuggerManager;
     IDebuggerManagerAccessForEngines *m_debuggerManagerAccess;
+    CdbStackTraceContext *m_currentStackTrace;
+
     DebuggerStartMode m_mode;
     Core::Utils::ConsoleProcess m_consoleStubProc;
 };
 
+// Message
+QString msgDebugEngineComResult(HRESULT hr);
+QString msgComFailed(const char *func, HRESULT hr);
+
 enum { debugCDB = 0 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp
new file mode 100644
index 00000000000..d569c6a9bae
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp
@@ -0,0 +1,161 @@
+/**************************************************************************
+**
+** 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 "cdbstacktracecontext.h"
+#include "cdbsymbolgroupcontext.h"
+#include "cdbdebugengine_p.h"
+
+namespace Debugger {
+namespace Internal {
+
+CdbStackTraceContext::CdbStackTraceContext(IDebugSystemObjects4* pDebugSystemObjects,
+                                           IDebugSymbols3* pDebugSymbols) :
+        m_pDebugSystemObjects(pDebugSystemObjects),
+        m_pDebugSymbols(pDebugSymbols)
+{
+}
+
+CdbStackTraceContext *CdbStackTraceContext::create(IDebugControl4* pDebugControl,
+                                                   IDebugSystemObjects4* pDebugSystemObjects,
+                                                   IDebugSymbols3* pDebugSymbols,
+                                                   unsigned long threadId,
+                                                   QString *errorMessage)
+{    
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << threadId;
+    HRESULT hr = pDebugSystemObjects->SetCurrentThreadId(threadId);
+    if (FAILED(hr)) {
+        *errorMessage = QString::fromLatin1("%1: SetCurrentThreadId %2 failed: %3").
+                        arg(QString::fromLatin1(Q_FUNC_INFO)).
+                        arg(threadId).
+                        arg(msgDebugEngineComResult(hr));
+        return 0;
+    }
+    // fill the DEBUG_STACK_FRAME array
+    ULONG frameCount;
+    CdbStackTraceContext *ctx = new CdbStackTraceContext(pDebugSystemObjects, pDebugSymbols);
+    hr = pDebugControl->GetStackTrace(0, 0, 0, ctx->m_cdbFrames, CdbStackTraceContext::maxFrames, &frameCount);
+    if (FAILED(hr)) {
+        delete ctx;
+         *errorMessage = msgComFailed("GetStackTrace", hr);
+        return 0;
+    }
+    if (!ctx->init(frameCount, errorMessage)) {
+        delete ctx;
+        return 0;
+
+    }
+    return ctx;
+}
+
+CdbStackTraceContext::~CdbStackTraceContext()
+{
+    qDeleteAll(m_symbolContexts);
+}
+
+bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessage*/)
+{
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << frameCount;
+
+    m_symbolContexts.resize(frameCount);
+    qFill(m_symbolContexts, static_cast<CdbSymbolGroupContext*>(0));
+
+    // Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
+    WCHAR wszBuf[MAX_PATH];
+    for (ULONG i=0; i < frameCount; ++i) {
+        StackFrame frame(i);
+        const ULONG64 instructionOffset = m_cdbFrames[i].InstructionOffset;
+        frame.address = QString::fromLatin1("0x%1").arg(instructionOffset, 0, 16);
+
+        m_pDebugSymbols->GetNameByOffsetWide(instructionOffset, wszBuf, MAX_PATH, 0, 0);
+        frame.function = QString::fromUtf16(wszBuf);
+
+        ULONG ulLine;
+        ULONG ulFileNameSize;
+        ULONG64 ul64Displacement;
+        const HRESULT hr = m_pDebugSymbols->GetLineByOffsetWide(instructionOffset, &ulLine, wszBuf, MAX_PATH, &ulFileNameSize, &ul64Displacement);
+        if (SUCCEEDED(hr)) {
+            frame.line = ulLine;
+            frame.file = QString::fromUtf16(wszBuf, ulFileNameSize);
+        }
+        m_frames.push_back(frame);
+    }
+    return true;
+}
+
+CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupContextAt(int index, QString *errorMessage)
+{
+    // Create a symbol group on demand
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << index << m_symbolContexts.at(index);
+
+    if (index < 0 || index >= m_symbolContexts.size()) {
+        *errorMessage = QString::fromLatin1("%1: Index %2 out of range %3.").
+                        arg(QLatin1String(Q_FUNC_INFO)).arg(index).arg(m_symbolContexts.size());
+        return 0;
+    }
+
+    if (m_symbolContexts.at(index))
+        return m_symbolContexts.at(index);
+    IDebugSymbolGroup2 *sg  = createSymbolGroup(index, errorMessage);
+    if (!sg)
+        return 0;
+    CdbSymbolGroupContext *sc = new CdbSymbolGroupContext(QLatin1String("local"), sg);
+    m_symbolContexts[index] = sc;
+    return sc;
+}
+
+IDebugSymbolGroup2 *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
+{
+    IDebugSymbolGroup2 *sg = 0;
+    HRESULT hr = m_pDebugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &sg);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
+        return 0;
+    }
+
+    hr = m_pDebugSymbols->SetScope(0, m_cdbFrames + index, NULL, 0);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("SetScope", hr);
+        sg->Release();
+        return 0;
+    }
+    // refresh with current frame
+    hr = m_pDebugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, sg, &sg);
+    if (FAILED(hr)) {
+        *errorMessage = msgComFailed("GetScopeSymbolGroup", hr);
+        sg->Release();
+        return 0;
+    }
+    return sg;
+}
+
+}
+}
diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h
new file mode 100644
index 00000000000..bb8286859ef
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h
@@ -0,0 +1,86 @@
+/**************************************************************************
+**
+** 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 CDBSTACKTRACECONTEXT_H
+#define CDBSTACKTRACECONTEXT_H
+
+#include "stackhandler.h"
+
+#include <windows.h>
+#include <inc/dbgeng.h>
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+namespace Debugger {
+namespace Internal {
+
+class CdbSymbolGroupContext;
+
+/* Context representing a break point stack consisting of several frames.
+ * Maintains an on-demand constructed list of CdbSymbolGroupContext
+ * containining the local variables of the stack. */
+
+class CdbStackTraceContext        
+{
+    Q_DISABLE_COPY(CdbStackTraceContext)
+
+    explicit CdbStackTraceContext(IDebugSystemObjects4* pDebugSystemObjects,
+                                  IDebugSymbols3* pDebugSymbols);
+public:
+    enum { maxFrames = 100 };
+
+    ~CdbStackTraceContext();
+    static CdbStackTraceContext *create(IDebugControl4* pDebugControl,
+                                        IDebugSystemObjects4* pDebugSystemObjects,
+                                        IDebugSymbols3* pDebugSymbols,
+                                        unsigned long threadid,
+                                        QString *errorMessage);
+
+    QList<StackFrame> frames() const { return m_frames; }
+    inline int frameCount() const { return m_frames.size(); }
+
+    CdbSymbolGroupContext *symbolGroupContextAt(int index, QString *errorMessage);
+
+private:
+    bool init(unsigned long frameCount, QString *errorMessage);
+    IDebugSymbolGroup2 *createSymbolGroup(int index, QString *errorMessage);
+
+    IDebugSystemObjects4*   m_pDebugSystemObjects;
+    IDebugSymbols3*         m_pDebugSymbols;
+
+    DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
+    QVector <CdbSymbolGroupContext*> m_symbolContexts;
+    QList<StackFrame> m_frames;
+};
+
+}
+}
+
+#endif // CDBSTACKTRACECONTEXT_H
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
new file mode 100644
index 00000000000..aa52ef55e1a
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
@@ -0,0 +1,177 @@
+/**************************************************************************
+**
+** 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 "cdbsymbolgroupcontext.h"
+#include "cdbdebugengine_p.h"
+#include "watchhandler.h"
+
+// A helper function to extract a string value from a member function of
+// IDebugSymbolGroup2 taking the symbol index and a character buffer.
+// Pass in the the member function as '&IDebugSymbolGroup2::GetSymbolNameWide'
+
+typedef HRESULT  (__stdcall IDebugSymbolGroup2::*WideStringRetrievalFunction)(ULONG, PWSTR, ULONG, PULONG);
+
+static inline QString getSymbolString(IDebugSymbolGroup2 *sg,
+                                      WideStringRetrievalFunction wsf,
+                                      unsigned long index)
+{
+    static WCHAR nameBuffer[MAX_PATH + 1];
+    // Name
+    ULONG nameLength;
+    const HRESULT hr = (sg->*wsf)(index, nameBuffer, MAX_PATH, &nameLength);
+    if (SUCCEEDED(hr)) {
+        nameBuffer[nameLength] = 0;
+        return QString::fromUtf16(nameBuffer);
+    }
+    return QString();
+}
+
+namespace Debugger {
+    namespace Internal {
+
+CdbSymbolGroupContext::CdbSymbolGroupContext(const QString &prefix,
+                                             IDebugSymbolGroup2 *symbolGroup) :
+    m_prefix(prefix),
+    m_nameDelimiter(QLatin1Char('.')),
+    m_symbolGroup(symbolGroup)
+{
+}
+
+CdbSymbolGroupContext::~CdbSymbolGroupContext()
+{
+    m_symbolGroup->Release();
+}
+
+CdbSymbolGroupContext::Range
+    CdbSymbolGroupContext::getSymbolRange(const QString &prefix)
+{
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << prefix;
+    const ChildRangeMap::const_iterator it = m_childRanges.constFind(prefix);
+    if (it != m_childRanges.constEnd())
+        return it.value();
+    const Range r = prefix == m_prefix ? allocateRootSymbols() : allocateChildSymbols(prefix);
+    m_childRanges.insert(prefix, r);
+    return r;
+}
+
+CdbSymbolGroupContext::Range
+    CdbSymbolGroupContext::allocateChildSymbols(const QString &prefix)
+{
+    unsigned long startPos = 0;
+    unsigned long count = 0;
+
+    bool success = false;
+    QString errorMessage;
+    do {
+        const int parentIndex = m_symbolINames.indexOf(prefix);
+        if (parentIndex == -1) {
+            errorMessage = QString::fromLatin1("Prefix not found '%1'").arg(prefix);
+            break;
+        }
+
+        success = true;
+    } while (false);
+    if (!success) {
+        qWarning("%s\n", qPrintable(errorMessage));
+    }
+    return Range(startPos, count);
+}
+
+CdbSymbolGroupContext::Range
+    CdbSymbolGroupContext::allocateRootSymbols()
+{
+    unsigned long startPos = 0;
+    unsigned long count = 0;
+    bool success = false;
+
+    QString errorMessage;
+    do {
+        HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
+        if (FAILED(hr)) {
+            errorMessage = msgComFailed("GetNumberSymbols", hr);
+            break;
+        }
+
+        m_symbolParameters.reserve(3u * count);
+        m_symbolParameters.resize(count);
+
+        hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
+        if (FAILED(hr)) {
+            errorMessage = msgComFailed("GetSymbolParameters", hr);
+            break;
+        }
+        const QString symbolPrefix = m_prefix + m_nameDelimiter;
+        for (unsigned long i = 0; i < count; i++)
+            m_symbolINames.push_back(symbolPrefix + getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i));
+
+        success = true;
+    } while (false);
+    if (!success) {
+        clear();
+        count = 0;
+        qWarning("%s\n", qPrintable(errorMessage));
+    }
+    return Range(startPos, count);
+}
+
+void CdbSymbolGroupContext::clear()
+{
+    m_symbolParameters.clear();
+    m_childRanges.clear();
+    m_symbolINames.clear();
+}
+
+WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const
+{
+    if (debugCDB)
+        qDebug() << Q_FUNC_INFO << index;
+
+    WatchData wd;
+    wd.iname = m_symbolINames.at(index);
+    const int lastDelimiterPos = wd.iname.lastIndexOf(m_nameDelimiter);
+    wd.name = lastDelimiterPos == -1 ? wd.iname : wd.iname.mid(lastDelimiterPos + 1);
+    wd.type = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index);
+    wd.value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
+    const DEBUG_SYMBOL_PARAMETERS &params = m_symbolParameters.at(index);
+    if (params.SubElements) {
+        wd.setTypeUnneeded();
+        wd.setValueUnneeded();
+        wd.setChildCount(1);
+    } else {
+        wd.setAllUnneeded();
+    }
+    if (debugCDB) {
+        qDebug() << Q_FUNC_INFO << wd.toString();
+    }
+    return wd;
+}
+
+}
+}
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h
new file mode 100644
index 00000000000..e5c4ea7c9c7
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h
@@ -0,0 +1,110 @@
+/**************************************************************************
+**
+** 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 CDBSYMBOLGROUPCONTEXT_H
+#define CDBSYMBOLGROUPCONTEXT_H
+
+#include <windows.h>
+#include <inc/dbgeng.h>
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QList>
+#include <QtCore/QStringList>
+#include <QtCore/QPair>
+#include <QtCore/QMap>
+
+namespace Debugger {
+    namespace Internal {
+
+class WatchData;
+
+/* A thin wrapper around the IDebugSymbolGroup2 interface which represents
+ * a flat list of symbols using an index (for example, belonging to a stack frame).
+ * It uses the hierarchical naming convention of WatchHandler as:
+ * "local" (invisible root)
+ * "local.string" (local class variable)
+ * "local.string.data" (class member).
+ * IDebugSymbolGroup2 can "expand" expandable symbols, appending to the flat list.
+ */
+
+class CdbSymbolGroupContext
+{
+    Q_DISABLE_COPY(CdbSymbolGroupContext);
+
+    // Start position and length of range in m_symbolParameters
+    typedef QPair<unsigned long, unsigned long> Range;
+
+public:
+    explicit CdbSymbolGroupContext(const QString &prefix,
+                                   IDebugSymbolGroup2 *symbolGroup);
+    ~CdbSymbolGroupContext();
+
+    QString prefix() const { return m_prefix; }
+
+    // Retrieve child symbols of prefix as a sequence of WatchData.
+    template <class OutputIterator>
+            void getSymbols(const QString &prefix, OutputIterator it);
+
+private:
+    void clear();
+    Range getSymbolRange(const QString &prefix);
+    Range allocateChildSymbols(const QString &prefix);
+    Range allocateRootSymbols();
+    WatchData symbolAt(unsigned long index) const;
+
+    inline DEBUG_SYMBOL_PARAMETERS *symbolParameters() { return &(*m_symbolParameters.begin()); }
+    inline const DEBUG_SYMBOL_PARAMETERS *symbolParameters() const { return &(*m_symbolParameters.constBegin()); }
+
+    const QString m_prefix;
+    const QChar m_nameDelimiter;
+    IDebugSymbolGroup2 *m_symbolGroup;
+
+    QStringList m_symbolINames;
+    QVector<DEBUG_SYMBOL_PARAMETERS> m_symbolParameters;
+
+    typedef QMap<QString, Range> ChildRangeMap;
+
+    ChildRangeMap m_childRanges;
+};
+
+template <class OutputIterator>
+void CdbSymbolGroupContext::getSymbols(const QString &prefix, OutputIterator it)
+{
+    const Range  r = getSymbolRange(prefix);
+    const unsigned long end = r.first + r.second;
+    for (unsigned long i = r.first; i < end; i++) {
+        *it = symbolAt(i);
+        ++it;
+    }
+}
+
+}
+}
+#endif // CDBSYMBOLGROUPCONTEXT_H
diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp
index daf2e6e0c0b..4ef2b4c4555 100644
--- a/src/plugins/debugger/gdbengine.cpp
+++ b/src/plugins/debugger/gdbengine.cpp
@@ -2447,8 +2447,7 @@ void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
     for (int i = 0; i != stack.childCount(); ++i) {
         //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
         const GdbMi frameMi = stack.childAt(i);
-        StackFrame frame;
-        frame.level = i;
+        StackFrame frame(i);
         QStringList files;
         files.append(frameMi.findChild("fullname").data());
         files.append(frameMi.findChild("file").data());
@@ -2488,8 +2487,7 @@ void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
     if (0 && topFrame != -1) {
         // updates of locals already triggered early
         const StackFrame &frame = qq->stackHandler()->currentFrame();
-        bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
-        if (usable)
+        if (frame.isUsable())
             q->gotoLocation(frame.file, frame.line, true);
         else
             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
@@ -2500,8 +2498,7 @@ void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
     if (topFrame != -1) {
         // updates of locals already triggered early
         const StackFrame &frame = qq->stackHandler()->currentFrame();
-        bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
-        if (usable)
+        if (frame.isUsable())
             q->gotoLocation(frame.file, frame.line, true);
         else
             qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
@@ -2551,8 +2548,7 @@ void GdbEngine::activateFrame(int frameIndex)
 
     const StackFrame &frame = stackHandler->currentFrame();
 
-    bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
-    if (usable)
+    if (frame.isUsable())
         q->gotoLocation(frame.file, frame.line, true);
     else
         qDebug() << "FULL NAME NOT USABLE: " << frame.file;
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
index fcc28bdd19d..a131bc00428 100644
--- a/src/plugins/debugger/stackhandler.cpp
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -37,6 +37,16 @@
 
 using namespace Debugger::Internal;
 
+StackFrame::StackFrame(int l) :
+    level(l),
+    line(0)
+{
+}
+
+bool StackFrame::isUsable() const
+{
+    return !file.isEmpty() && QFileInfo(file).isReadable();
+}
 
 ////////////////////////////////////////////////////////////////////////
 //
diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h
index d427c93e921..ae073e3d136 100644
--- a/src/plugins/debugger/stackhandler.h
+++ b/src/plugins/debugger/stackhandler.h
@@ -46,6 +46,9 @@ namespace Internal {
 
 struct StackFrame
 {
+    StackFrame(int level = 0);    
+    bool isUsable() const;
+
     int level;
     QString function;
     QString file;  // we try to put an absolute file name in there
-- 
GitLab