From 56d3664934cb825dabffab0cb9b5735849d9c11d Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Thu, 18 Nov 2010 13:55:52 +0100
Subject: [PATCH] Debugger: Add extension library to be loaded into CDB.exe

---
 src/libs/libs.pro                             |   2 +
 src/libs/qtcreatorcdbext/base64.cpp           |  80 ++
 src/libs/qtcreatorcdbext/base64.h             |  38 +
 src/libs/qtcreatorcdbext/cdb_detect.pri       |  13 +
 src/libs/qtcreatorcdbext/common.cpp           | 120 +++
 src/libs/qtcreatorcdbext/common.h             |  76 ++
 src/libs/qtcreatorcdbext/eventcallback.cpp    | 302 +++++++
 src/libs/qtcreatorcdbext/eventcallback.h      | 157 ++++
 src/libs/qtcreatorcdbext/extensioncontext.cpp | 319 +++++++
 src/libs/qtcreatorcdbext/extensioncontext.h   | 134 +++
 src/libs/qtcreatorcdbext/gdbmihelpers.cpp     | 561 ++++++++++++
 src/libs/qtcreatorcdbext/gdbmihelpers.h       | 148 ++++
 src/libs/qtcreatorcdbext/iinterfacepointer.h  |  88 ++
 src/libs/qtcreatorcdbext/outputcallback.cpp   |  98 +++
 src/libs/qtcreatorcdbext/outputcallback.h     |  64 ++
 src/libs/qtcreatorcdbext/qtcreatorcdbext.pro  |  14 +
 .../qtcreatorcdbext/qtcreatorcdbext_build.pro |  67 ++
 .../qtcreatorcdbext/qtcreatorcdbextension.cpp | 368 ++++++++
 src/libs/qtcreatorcdbext/stringutils.cpp      | 180 ++++
 src/libs/qtcreatorcdbext/stringutils.h        | 122 +++
 src/libs/qtcreatorcdbext/symbolgroup.cpp      | 804 ++++++++++++++++++
 src/libs/qtcreatorcdbext/symbolgroup.h        | 224 +++++
 src/libs/qtcreatorcdbext/test32.bat           |  24 +
 src/libs/qtcreatorcdbext/test64.bat           |  23 +
 24 files changed, 4026 insertions(+)
 create mode 100644 src/libs/qtcreatorcdbext/base64.cpp
 create mode 100644 src/libs/qtcreatorcdbext/base64.h
 create mode 100644 src/libs/qtcreatorcdbext/cdb_detect.pri
 create mode 100644 src/libs/qtcreatorcdbext/common.cpp
 create mode 100644 src/libs/qtcreatorcdbext/common.h
 create mode 100644 src/libs/qtcreatorcdbext/eventcallback.cpp
 create mode 100644 src/libs/qtcreatorcdbext/eventcallback.h
 create mode 100644 src/libs/qtcreatorcdbext/extensioncontext.cpp
 create mode 100644 src/libs/qtcreatorcdbext/extensioncontext.h
 create mode 100644 src/libs/qtcreatorcdbext/gdbmihelpers.cpp
 create mode 100644 src/libs/qtcreatorcdbext/gdbmihelpers.h
 create mode 100644 src/libs/qtcreatorcdbext/iinterfacepointer.h
 create mode 100644 src/libs/qtcreatorcdbext/outputcallback.cpp
 create mode 100644 src/libs/qtcreatorcdbext/outputcallback.h
 create mode 100644 src/libs/qtcreatorcdbext/qtcreatorcdbext.pro
 create mode 100644 src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro
 create mode 100644 src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
 create mode 100644 src/libs/qtcreatorcdbext/stringutils.cpp
 create mode 100644 src/libs/qtcreatorcdbext/stringutils.h
 create mode 100644 src/libs/qtcreatorcdbext/symbolgroup.cpp
 create mode 100644 src/libs/qtcreatorcdbext/symbolgroup.h
 create mode 100644 src/libs/qtcreatorcdbext/test32.bat
 create mode 100644 src/libs/qtcreatorcdbext/test64.bat

diff --git a/src/libs/libs.pro b/src/libs/libs.pro
index 570785f8344..fce7bc63f8b 100644
--- a/src/libs/libs.pro
+++ b/src/libs/libs.pro
@@ -14,3 +14,5 @@ SUBDIRS   = \
     qmleditorwidgets \
     symbianutils \
     3rdparty
+
+win32:SUBDIRS += qtcreatorcdbext
diff --git a/src/libs/qtcreatorcdbext/base64.cpp b/src/libs/qtcreatorcdbext/base64.cpp
new file mode 100644
index 00000000000..d6121daafc0
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/base64.cpp
@@ -0,0 +1,80 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "base64.h"
+
+#include <ostream>
+
+static void base64EncodeTriple(std::ostream &str, const unsigned char triple[3], size_t length = 3)
+{
+    static const char base64Encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    // Convert 3 bytes of triplet into 4 bytes, write out 64 bit-wise using a character mapping,
+    // see description of base64.
+    unsigned tripleValue = triple[0] * 256;
+    tripleValue += triple[1];
+    tripleValue *= 256;
+    tripleValue += triple[2];
+
+    char result[4]= {0, 0, 0, 0};
+    for (int i = 3; i >= 0 ; i--) {
+        result[i] = base64Encoding[tripleValue % 64];
+        tripleValue /= 64;
+    }
+
+    // Write out quad and pad if it is a padding triple.
+    const size_t writeLength = length + 1;
+    size_t i = 0;
+    for ( ; i < writeLength; i++)
+        str << result[i];
+    for ( ; i < 4; i++)
+        str << '=';
+}
+
+void base64Encode(std::ostream &str, const unsigned char *source, size_t sourcelen)
+{
+    if (!sourcelen) {
+        str << "====";
+        return;
+    }
+    /* Encode triples */
+    const unsigned char *sourceEnd = source + sourcelen;
+    for (const unsigned char *triple = source; triple < sourceEnd; ) {
+        const unsigned char *nextTriple = triple + 3;
+        if (nextTriple <= sourceEnd) { // Encode full triple, including very last one
+            base64EncodeTriple(str, triple);
+            triple = nextTriple;
+        } else { // Past end and some padding required.
+            unsigned char paddingTriple[3] = {0, 0, 0};
+            std::copy(triple, sourceEnd, paddingTriple);
+            base64EncodeTriple(str, paddingTriple, sourceEnd - triple);
+            break;
+        }
+    }
+}
diff --git a/src/libs/qtcreatorcdbext/base64.h b/src/libs/qtcreatorcdbext/base64.h
new file mode 100644
index 00000000000..bfafc2b4117
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/base64.h
@@ -0,0 +1,38 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef BASE64_H
+#define BASE64_H
+
+#include <iosfwd>
+
+// Base 64 encoding helper
+void base64Encode(std::ostream &str, const unsigned char *source, size_t sourcelen);
+
+#endif // BASE64_H
diff --git a/src/libs/qtcreatorcdbext/cdb_detect.pri b/src/libs/qtcreatorcdbext/cdb_detect.pri
new file mode 100644
index 00000000000..da024eb6008
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/cdb_detect.pri
@@ -0,0 +1,13 @@
+# Detect presence of "Debugging Tools For Windows"
+# in case MS VS compilers are used.
+
+CDB_PATH=""
+win32 {
+    contains(QMAKE_CXX, cl) {
+        CDB_PATH="$$(CDB_PATH)"
+        isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk"
+        !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk"
+        !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk"
+        !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk"
+    }
+}
diff --git a/src/libs/qtcreatorcdbext/common.cpp b/src/libs/qtcreatorcdbext/common.cpp
new file mode 100644
index 00000000000..309a202b2d7
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/common.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "common.h"
+#include "iinterfacepointer.h"
+#include <sstream>
+
+std::string winErrorMessage(unsigned long error)
+{
+    char *lpMsgBuf;
+    const int len = FormatMessageA(
+            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL, error, 0, (LPSTR)&lpMsgBuf, 0, NULL);
+    if (len) {
+        const std::string rc(lpMsgBuf, len);
+        LocalFree(lpMsgBuf);
+        return rc;
+    }
+    std::ostringstream str;
+    str << "Unknown error " << error;
+    return str.str();
+}
+
+std::string winErrorMessage()
+{
+    return winErrorMessage(GetLastError());
+}
+
+std::string msgDebugEngineComResult(HRESULT hr)
+{
+    switch (hr) {
+    case S_OK:
+        return std::string("S_OK");
+    case S_FALSE:
+        return std::string("S_FALSE");
+    case E_FAIL:
+        break;
+    case E_INVALIDARG:
+        return std::string("E_INVALIDARG");
+    case E_NOINTERFACE:
+        return std::string("E_NOINTERFACE");
+    case E_OUTOFMEMORY:
+        return std::string("E_OUTOFMEMORY");
+    case E_UNEXPECTED:
+        return std::string("E_UNEXPECTED");
+    case E_NOTIMPL:
+        return std::string("E_NOTIMPL");
+    }
+    if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
+        return std::string("ERROR_ACCESS_DENIED");;
+    if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
+        return std::string("STATUS_CONTROL_C_EXIT");
+    return std::string("E_FAIL ") + winErrorMessage(HRESULT_CODE(hr));
+}
+
+std::string msgDebugEngineComFailed(const char *func, HRESULT hr)
+{
+    std::string rc = func;
+    rc += " failed: ";
+    rc += msgDebugEngineComResult(hr);
+    return rc;
+}
+
+ULONG currentThreadId(IDebugSystemObjects *sysObjects)
+{
+    ULONG id = 0;
+    if (sysObjects->GetCurrentThreadId(&id) == S_OK)
+        return id;
+    return 0;
+}
+
+ULONG currentThreadId(CIDebugClient *client)
+{
+    IInterfacePointer<IDebugSystemObjects> sysObjects(client);
+    if (sysObjects)
+        return currentThreadId(sysObjects.data());
+    return 0;
+}
+
+ULONG currentProcessId(IDebugSystemObjects *sysObjects)
+{
+    ULONG64 handle = 0;
+    if (sysObjects->GetCurrentProcessHandle(&handle) == S_OK)
+        return GetProcessId((HANDLE)handle);
+    return 0;
+}
+
+ULONG currentProcessId(CIDebugClient *client)
+{
+    IInterfacePointer<IDebugSystemObjects> sysObjects(client);
+    if (sysObjects)
+        return currentProcessId(sysObjects.data());
+    return 0;
+}
diff --git a/src/libs/qtcreatorcdbext/common.h b/src/libs/qtcreatorcdbext/common.h
new file mode 100644
index 00000000000..198cb1eb7a5
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/common.h
@@ -0,0 +1,76 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+// Define KDEXT_64BIT to make all wdbgexts APIs recognize 64 bit addresses
+// It is recommended for extensions to use 64 bit headers from wdbgexts so
+// the extensions could support 64 bit targets.
+
+#include <string>
+#include <sstream>
+
+#include <windows.h>
+#define KDEXT_64BIT
+#include <wdbgexts.h>
+#include <dbgeng.h>
+
+static const char creatorOutputPrefixC[] = "QtCreatorExt: ";
+
+typedef IDebugControl CIDebugControl;
+typedef IDebugSymbols3 CIDebugSymbols;
+typedef IDebugSymbolGroup2 CIDebugSymbolGroup;
+typedef IDebugClient5 CIDebugClient;
+typedef IDebugSystemObjects CIDebugSystemObjects;
+typedef IDebugDataSpaces4 CIDebugDataSpaces;
+typedef IDebugAdvanced2 CIDebugAdvanced;
+typedef IDebugRegisters2 CIDebugRegisters;
+
+// Utility messages
+std::string winErrorMessage(unsigned long error);
+std::string winErrorMessage();
+std::string msgDebugEngineComResult(HRESULT hr);
+std::string msgDebugEngineComFailed(const char *func, HRESULT hr);
+
+// Debug helper for anything streamable as in 'DebugPrint() << object'
+// Derives from std::ostringstream and print out everything accumulated in destructor.
+struct DebugPrint : public std::ostringstream {
+    DebugPrint() {}
+    ~DebugPrint() {
+        dprintf("DEBUG: %s\n", str().c_str());
+    }
+};
+
+ULONG currentThreadId(IDebugSystemObjects *sysObjects);
+ULONG currentThreadId(CIDebugClient *client);
+ULONG currentProcessId(IDebugSystemObjects *sysObjects);
+ULONG currentProcessId(CIDebugClient *client);
+
+#endif // COMMON_H
diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp
new file mode 100644
index 00000000000..f05f66d2adc
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/eventcallback.cpp
@@ -0,0 +1,302 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "eventcallback.h"
+#include "extensioncontext.h"
+#include "stringutils.h"
+#include "gdbmihelpers.h"
+
+static const char eventContextC[] = "event";
+static const char moduleContextC[] = "module";
+
+// Special exception codes (see dbgwinutils.cpp).
+enum { winExceptionCppException = 0xe06d7363,
+       winExceptionStartupCompleteTrap = 0x406d1388,
+       winExceptionRpcServerUnavailable = 0x6ba,
+       winExceptionRpcServerInvalid = 0x6a6,
+       winExceptionDllNotFound = 0xc0000135,
+       winExceptionDllEntryPointNoFound = 0xc0000139,
+       winExceptionDllInitFailed = 0xc0000142,
+       winExceptionMissingSystemFile = 0xc0000143,
+       winExceptionAppInitFailed = 0xc0000143
+};
+
+EventCallback::EventCallback(IDebugEventCallbacks *wrapped) :
+    m_wrapped(wrapped)
+{
+}
+
+EventCallback::~EventCallback()
+{
+}
+
+STDMETHODIMP EventCallback::QueryInterface(
+    THIS_
+    IN REFIID InterfaceId,
+    OUT PVOID* Interface)
+{
+    *Interface = NULL;
+
+    if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+        IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))  {
+        *Interface = (IDebugOutputCallbacks *)this;
+        AddRef();
+        return S_OK;
+    } else {
+        return E_NOINTERFACE;
+    }
+}
+
+STDMETHODIMP_(ULONG) EventCallback::AddRef(THIS)
+{
+    // This class is designed to be static so
+    // there's no true refcount.
+    return 1;
+}
+
+STDMETHODIMP_(ULONG) EventCallback::Release(THIS)
+{
+    // This class is designed to be static so
+    // there's no true refcount.
+    return 0;
+}
+
+STDMETHODIMP EventCallback::GetInterestMask(THIS_ __out PULONG mask)
+{
+    if (m_wrapped)
+        m_wrapped->GetInterestMask(mask);
+
+    *mask |= DEBUG_EVENT_CREATE_PROCESS  | DEBUG_EVENT_EXIT_PROCESS
+            | DEBUG_EVENT_BREAKPOINT
+            | DEBUG_EVENT_EXCEPTION | DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE;
+    return S_OK;
+}
+
+STDMETHODIMP EventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT b)
+{
+    // Breakpoint hit - Set the stop reason parameters on the extension context.
+    typedef ExtensionContext::StopReasonMap StopReasonMap;
+    typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
+
+    ULONG id = 0;
+    ULONG64 address = 0;
+    b->GetId(&id);
+    b->GetOffset(&address);
+
+    StopReasonMap stopReason;
+    stopReason.insert(StopReasonMapValue(std::string("breakpointId"), toString(id)));
+    if (address)
+        stopReason.insert(StopReasonMapValue(std::string("breakpointAddress"), toString(address)));
+    ExtensionContext::instance().setStopReason(stopReason, "breakpoint");
+    return m_wrapped ? m_wrapped->Breakpoint(b) : S_OK;
+}
+
+static inline ExtensionContext::StopReasonMap exceptionParameters(const EXCEPTION_RECORD64 &e,
+                                                                  unsigned firstChance)
+{
+    typedef ExtensionContext::StopReasonMap StopReasonMap;
+    typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
+    // Fill exception record
+    StopReasonMap parameters;
+    parameters.insert(StopReasonMapValue(std::string("firstChance"), toString(firstChance)));
+    parameters.insert(StopReasonMapValue(std::string("exceptionAddress"),
+                                         toString(e.ExceptionAddress)));
+    parameters.insert(StopReasonMapValue(std::string("exceptionCode"),
+                                         toString(e.ExceptionCode)));
+    parameters.insert(StopReasonMapValue(std::string("exceptionFlags"),
+                                         toString(e.ExceptionFlags)));
+    // Hard code some parameters (used for access violations)
+    if (e.NumberParameters >= 1)
+        parameters.insert(StopReasonMapValue(std::string("exceptionInformation0"),
+                                             toString(e.ExceptionInformation[0])));
+    if (e.NumberParameters >= 2)
+        parameters.insert(StopReasonMapValue(std::string("exceptionInformation1"),
+                                             toString(e.ExceptionInformation[1])));
+    // Add top stack frame if possible
+    StackFrame frame;
+    std::string errorMessage;
+    // If it is a C++ exception, get frame #2 (first outside MSVC runtime)
+    const unsigned frameNumber = e.ExceptionCode == winExceptionCppException ? 2 : 0;
+    if (getFrame(frameNumber, &frame, &errorMessage)) {
+        if (!frame.fullPathName.empty()) {
+            parameters.insert(StopReasonMapValue(std::string("exceptionFile"),
+                                                 wStringToString(frame.fullPathName)));
+            parameters.insert(StopReasonMapValue(std::string("exceptionLine"),
+                                                 toString(frame.line)));
+        }
+        if (!frame.function.empty())
+            parameters.insert(StopReasonMapValue(std::string("exceptionFunction"),
+                                                 wStringToString(frame.function)));
+    } // getCurrentFrame
+    return parameters;
+}
+
+STDMETHODIMP EventCallback::Exception(
+    THIS_
+    __in PEXCEPTION_RECORD64 Ex,
+    __in ULONG FirstChance
+    )
+{
+    // Report the exception as GBMI and set potential stop reason
+    const ExtensionContext::StopReasonMap parameters =
+            exceptionParameters(*Ex, FirstChance);
+
+    std::ostringstream str;
+    formatGdbmiHash(str, parameters);
+    ExtensionContext::instance().setStopReason(parameters, "exception");
+    ExtensionContext::instance().report('E', 0, "exception", "%s", str.str().c_str());
+    return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
+}
+
+STDMETHODIMP EventCallback::CreateThread(
+    THIS_
+    __in ULONG64 Handle,
+    __in ULONG64 DataOffset,
+    __in ULONG64 StartOffset
+    )
+{
+    return m_wrapped ? m_wrapped->CreateThread(Handle, DataOffset, StartOffset) : S_OK;
+}
+
+STDMETHODIMP EventCallback::ExitThread(
+    THIS_
+    __in ULONG  ExitCode
+    )
+{
+    return m_wrapped ? m_wrapped->ExitThread(ExitCode) : S_OK;
+}
+
+STDMETHODIMP EventCallback::CreateProcess(
+    THIS_
+    __in ULONG64 ImageFileHandle,
+    __in ULONG64 Handle,
+    __in ULONG64 BaseOffset,
+    __in ULONG    ModuleSize,
+    __in_opt PCSTR ModuleName,
+    __in_opt PCSTR ImageName,
+    __in ULONG  CheckSum,
+    __in ULONG  TimeDateStamp,
+    __in ULONG64 InitialThreadHandle,
+    __in ULONG64 ThreadDataOffset,
+    __in ULONG64 StartOffset
+    )
+{
+    return m_wrapped ? m_wrapped->CreateProcess(ImageFileHandle, Handle,
+                                                 BaseOffset, ModuleSize, ModuleName,
+                                                 ImageName, CheckSum, TimeDateStamp,
+                                                 InitialThreadHandle, ThreadDataOffset,
+                                                 StartOffset) : S_OK;
+}
+
+STDMETHODIMP EventCallback::ExitProcess(
+    THIS_
+    __in ULONG ExitCode
+    )
+{
+    ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)",
+                                        ExitCode);
+
+    dprintf("%s ExitProcess %u\n", creatorOutputPrefixC, ExitCode);
+    return m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
+}
+
+STDMETHODIMP EventCallback::LoadModule(
+    THIS_
+    __in ULONG64 ImageFileHandle,
+    __in ULONG64 BaseOffset,
+    __in ULONG ModuleSize,
+    __in_opt PCSTR ModuleName,
+    __in_opt PCSTR ImageName,
+    __in ULONG CheckSum,
+    __in ULONG TimeDateStamp
+    )
+{
+    ExtensionContext::instance().report('E', 0, moduleContextC, "L:%s:%s:0x%llx:0x%llx\n",
+                                        ModuleName, ImageName, BaseOffset, ModuleSize);
+
+    return m_wrapped ? m_wrapped->LoadModule(ImageFileHandle, BaseOffset,
+                                             ModuleSize, ModuleName, ImageName,
+                                             CheckSum, TimeDateStamp) : S_OK;
+}
+
+STDMETHODIMP EventCallback::UnloadModule(
+    THIS_
+    __in_opt PCSTR ImageBaseName,
+    __in ULONG64 BaseOffset
+    )
+{
+    ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n",
+                                        ImageBaseName);
+
+    return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK;
+}
+
+STDMETHODIMP EventCallback::SystemError(
+    THIS_
+    __in ULONG Error,
+    __in ULONG Level
+    )
+{
+    return m_wrapped ? m_wrapped->SystemError(Error, Level) : S_OK;
+}
+
+STDMETHODIMP EventCallback::SessionStatus(
+    THIS_
+    __in ULONG Status
+    )
+{
+    return m_wrapped ? m_wrapped->SessionStatus(Status) : S_OK;
+}
+
+STDMETHODIMP EventCallback::ChangeDebuggeeState(
+    THIS_
+    __in ULONG Flags,
+    __in ULONG64 Argument
+    )
+{
+    return m_wrapped ? m_wrapped->ChangeDebuggeeState(Flags, Argument) : S_OK;
+}
+
+STDMETHODIMP EventCallback::ChangeEngineState(
+    THIS_
+    __in ULONG Flags,
+    __in ULONG64 Argument
+    )
+{
+    return m_wrapped ? m_wrapped->ChangeEngineState(Flags, Argument) : S_OK;
+}
+
+STDMETHODIMP EventCallback::ChangeSymbolState(
+    THIS_
+    __in ULONG Flags,
+    __in ULONG64 Argument
+    )
+{
+    return m_wrapped ? m_wrapped->ChangeSymbolState(Flags, Argument) : S_OK;
+}
diff --git a/src/libs/qtcreatorcdbext/eventcallback.h b/src/libs/qtcreatorcdbext/eventcallback.h
new file mode 100644
index 00000000000..579bb7c138f
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/eventcallback.h
@@ -0,0 +1,157 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef DEBUGEVENTHANDLER_H
+#define DEBUGEVENTHANDLER_H
+
+#include "common.h"
+#include "extensioncontext.h"
+
+/* IDebugEventCallbacks event handler wrapping IDebugEventCallbacks to catch some output */
+
+class EventCallback : public IDebugEventCallbacks
+{
+public:
+    explicit EventCallback(IDebugEventCallbacks *wrapped);
+    virtual ~EventCallback();
+    // IUnknown.
+    STDMETHOD(QueryInterface)(
+            THIS_
+            IN REFIID InterfaceId,
+            OUT PVOID* Interface
+            );
+    STDMETHOD_(ULONG, AddRef)(
+            THIS
+            );
+    STDMETHOD_(ULONG, Release)(
+            THIS
+            );
+
+    // IDebugEventCallbacks.
+
+    STDMETHOD(GetInterestMask)(
+        THIS_
+        __out PULONG mask
+        );
+
+
+    STDMETHOD(Breakpoint)(
+            THIS_
+            __in PDEBUG_BREAKPOINT Bp
+            );
+
+    STDMETHOD(Exception)(
+            THIS_
+            __in PEXCEPTION_RECORD64 Exception,
+            __in ULONG FirstChance
+            );
+
+    STDMETHOD(CreateThread)(
+            THIS_
+            __in ULONG64 Handle,
+            __in ULONG64 DataOffset,
+            __in ULONG64 StartOffset
+            );
+    STDMETHOD(ExitThread)(
+            THIS_
+            __in ULONG ExitCode
+            );
+
+    STDMETHOD(CreateProcess)(
+            THIS_
+            __in ULONG64 ImageFileHandle,
+            __in ULONG64 Handle,
+            __in ULONG64 BaseOffset,
+            __in ULONG ModuleSize,
+            __in_opt PCSTR ModuleName,
+            __in_opt PCSTR ImageName,
+            __in ULONG CheckSum,
+            __in ULONG TimeDateStamp,
+            __in ULONG64 InitialThreadHandle,
+            __in ULONG64 ThreadDataOffset,
+            __in ULONG64 StartOffset
+            );
+
+    STDMETHOD(ExitProcess)(
+            THIS_
+            __in ULONG ExitCode
+            );
+
+    // Call handleModuleLoad() when reimplementing this
+    STDMETHOD(LoadModule)(
+            THIS_
+            __in ULONG64 ImageFileHandle,
+            __in ULONG64 BaseOffset,
+            __in ULONG ModuleSize,
+            __in_opt PCSTR ModuleName,
+            __in_opt PCSTR ImageName,
+            __in ULONG CheckSum,
+            __in ULONG TimeDateStamp
+            );
+
+    // Call handleModuleUnload() when reimplementing this
+    STDMETHOD(UnloadModule)(
+            THIS_
+            __in_opt PCSTR ImageBaseName,
+            __in ULONG64 BaseOffset
+            );
+
+    STDMETHOD(SystemError)(
+            THIS_
+            __in ULONG Error,
+            __in ULONG Level
+            );
+
+    STDMETHOD(SessionStatus)(
+            THIS_
+            __in ULONG Status
+            );
+
+    STDMETHOD(ChangeDebuggeeState)(
+            THIS_
+            __in ULONG Flags,
+            __in ULONG64 Argument
+            );
+
+    STDMETHOD(ChangeEngineState)(
+            THIS_
+            __in ULONG Flags,
+            __in ULONG64 Argument
+            );
+
+    STDMETHOD(ChangeSymbolState)(
+            THIS_
+            __in ULONG Flags,
+            __in ULONG64 Argument
+            );
+
+    private:
+        IDebugEventCallbacks *m_wrapped;
+};
+#endif // DEBUGEVENTHANDLER_H
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp
new file mode 100644
index 00000000000..b393752d89a
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp
@@ -0,0 +1,319 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "extensioncontext.h"
+#include "symbolgroup.h"
+#include "eventcallback.h"
+#include "outputcallback.h"
+#include "stringutils.h"
+
+// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
+// and it's inline functions rely on its existence.
+WINDBG_EXTENSION_APIS   ExtensionApis = {sizeof(WINDBG_EXTENSION_APIS), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const char *ExtensionContext::stopReasonKeyC = "reason";
+
+ExtensionContext::ExtensionContext() :
+    m_hookedClient(0),
+    m_oldEventCallback(0), m_oldOutputCallback(0),
+    m_creatorEventCallback(0), m_creatorOutputCallback(0)
+{
+}
+
+ExtensionContext::~ExtensionContext()
+{
+    unhookCallbacks();
+}
+
+ExtensionContext &ExtensionContext::instance()
+{
+    static ExtensionContext extContext;
+    return extContext;
+}
+
+// Redirect the event/output callbacks
+void ExtensionContext::hookCallbacks(CIDebugClient *client)
+{
+    if (!client || m_hookedClient || m_creatorEventCallback)
+        return;
+    // Store the hooked client. Any other client obtained
+    // is invalid for unhooking
+    m_hookedClient = client;
+    if (client->GetEventCallbacks(&m_oldEventCallback) == S_OK) {
+        m_creatorEventCallback = new EventCallback(m_oldEventCallback);
+        client->SetEventCallbacks(m_creatorEventCallback);
+    }
+    if (client->GetOutputCallbacksWide(&m_oldOutputCallback) == S_OK) {
+        m_creatorOutputCallback = new OutputCallback(m_oldOutputCallback);
+        client->SetOutputCallbacksWide(m_creatorOutputCallback);
+    }
+}
+
+void ExtensionContext::setStopReason(const StopReasonMap &r, const std::string &reason)
+{
+    m_stopReason = r;
+    if (!reason.empty())
+        m_stopReason.insert(StopReasonMap::value_type(stopReasonKeyC, reason));
+}
+
+// Restore the callbacks.
+void ExtensionContext::unhookCallbacks()
+{
+    if (!m_hookedClient || (!m_creatorEventCallback && !m_creatorOutputCallback))
+        return;
+
+    if (m_creatorEventCallback) {
+        m_hookedClient->SetEventCallbacks(m_oldEventCallback);
+        delete m_creatorEventCallback;
+        m_creatorEventCallback = 0;
+        m_oldEventCallback = 0;
+    }
+
+    if (m_creatorOutputCallback) {
+        m_hookedClient->SetOutputCallbacksWide(m_oldOutputCallback);
+        delete m_creatorOutputCallback;
+        m_creatorOutputCallback = 0;
+        m_oldOutputCallback  = 0;
+    }
+    m_hookedClient = 0;
+}
+
+HRESULT ExtensionContext::initialize(PULONG Version, PULONG Flags)
+{
+    if (isInitialized())
+        return S_OK;
+
+    *Version = DEBUG_EXTENSION_VERSION(1, 0);
+    *Flags = 0;
+
+    IInterfacePointer<CIDebugClient> client;
+    if (!client.create())
+        return client.hr();
+    m_control.create(client.data());
+    if (!m_control)
+        return m_control.hr();
+    return m_control->GetWindbgExtensionApis64(&ExtensionApis);
+}
+
+bool ExtensionContext::isInitialized() const
+{
+    return ExtensionApis.lpOutputRoutine != 0;
+}
+
+ULONG ExtensionContext::executionStatus() const
+{
+    ULONG ex = 0;
+    return (m_control && SUCCEEDED(m_control->GetExecutionStatus(&ex))) ? ex : ULONG(0);
+}
+
+// Complete stop parameters with common parameters and report
+static inline ExtensionContext::StopReasonMap
+    completeStopReasons(ExtensionContext::StopReasonMap stopReasons, ULONG ex)
+{
+    typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
+
+    stopReasons.insert(StopReasonMapValue(std::string("executionStatus"), toString(ex)));
+
+    IInterfacePointer<CIDebugClient> client;
+    if (client.create()) {
+        if (const ULONG processId = currentProcessId(client.data()))
+            stopReasons.insert(StopReasonMapValue(std::string("processId"), toString(processId)));
+        const ULONG threadId = currentThreadId(client.data());
+        stopReasons.insert(StopReasonMapValue(std::string("threadId"), toString(threadId)));
+    }
+    // Any reason?
+    const std::string reasonKey = std::string(ExtensionContext::stopReasonKeyC);
+    if (stopReasons.find(reasonKey) == stopReasons.end())
+        stopReasons.insert(StopReasonMapValue(reasonKey, "unknown"));
+    return stopReasons;
+}
+
+void ExtensionContext::notifyIdle()
+{
+    discardSymbolGroup();
+
+    const StopReasonMap stopReasons = completeStopReasons(m_stopReason, executionStatus());
+    m_stopReason.clear();
+    // Format
+    std::ostringstream str;
+    formatGdbmiHash(str, stopReasons);
+    report('E', 0, "session_idle", "%s", str.str().c_str());
+    m_stopReason.clear();
+}
+
+void ExtensionContext::notifyState(ULONG Notify)
+{
+    const ULONG ex = executionStatus();
+    switch (Notify) {
+    case DEBUG_NOTIFY_SESSION_ACTIVE:
+        report('E', 0, "session_active", "%u", ex);
+        break;
+    case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted
+        report('E', 0, "session_accessible", "%u", ex);
+        break;
+    case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
+        report('E', 0, "session_inaccessible", "%u", ex);
+        break;
+    case DEBUG_NOTIFY_SESSION_INACTIVE:
+        report('E', 0, "session_inactive", "%u", ex);
+        discardSymbolGroup();
+        // We lost the debuggee, at this point restore output.
+        if (ex & DEBUG_STATUS_NO_DEBUGGEE)
+            unhookCallbacks();
+        break;
+    }
+}
+
+SymbolGroup *ExtensionContext::symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage)
+{
+    if (m_symbolGroup.get() && m_symbolGroup->frame() == frame && m_symbolGroup->threadId() == threadId)
+        return m_symbolGroup.get();
+    SymbolGroup *newSg = SymbolGroup::create(m_control.data(), symbols, threadId, frame, errorMessage);
+    if (!newSg)
+        return 0;
+    m_symbolGroup.reset(newSg);
+    return newSg;
+}
+
+int ExtensionContext::symbolGroupFrame() const
+{
+    if (m_symbolGroup.get())
+        return m_symbolGroup->frame();
+    return -1;
+}
+
+void ExtensionContext::discardSymbolGroup()
+{
+    if (m_symbolGroup.get())
+        m_symbolGroup.reset();
+}
+
+bool ExtensionContext::report(char code, int token, const char *serviceName, PCSTR Format, ...)
+{
+    if (!isInitialized())
+        return false;
+    // '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
+    m_control->Output(DEBUG_OUTPUT_NORMAL, "<qtcreatorcdbext>|%c|%d|%s|", code, token, serviceName);
+    va_list Args;
+    va_start(Args, Format);
+    m_control->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
+    va_end(Args);
+    m_control->Output(DEBUG_OUTPUT_NORMAL, "\n");
+    return true;
+}
+
+// Exported C-functions
+extern "C" {
+
+HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags)
+{
+    return ExtensionContext::instance().initialize(Version, Flags);
+}
+
+void CALLBACK DebugExtensionUninitialize(void)
+{
+}
+
+void CALLBACK DebugExtensionNotify(ULONG Notify, ULONG64)
+{
+    ExtensionContext::instance().notifyState(Notify);
+}
+
+} // extern "C"
+
+// -------- ExtensionCommandContext
+
+ExtensionCommandContext *ExtensionCommandContext::m_instance = 0;
+
+ExtensionCommandContext::ExtensionCommandContext(CIDebugClient *client) : m_client(client)
+{
+    ExtensionCommandContext::m_instance = this;
+}
+
+ExtensionCommandContext::~ExtensionCommandContext()
+{
+    ExtensionCommandContext::m_instance = 0;
+}
+
+CIDebugControl *ExtensionCommandContext::control()
+{
+    if (!m_control)
+        m_control.create(m_client);
+    return m_control.data();
+}
+
+ExtensionCommandContext *ExtensionCommandContext::instance()
+{
+    return m_instance;
+}
+
+CIDebugSymbols *ExtensionCommandContext::symbols()
+{
+    if (!m_symbols)
+        m_symbols.create(m_client);
+    return m_symbols.data();
+}
+
+CIDebugSystemObjects *ExtensionCommandContext::systemObjects()
+{
+    if (!m_systemObjects)
+        m_systemObjects.create(m_client);
+    return m_systemObjects.data();
+}
+
+CIDebugAdvanced *ExtensionCommandContext::advanced()
+{
+    if (!m_advanced)
+        m_advanced.create(m_client);
+    return m_advanced.data();
+}
+
+CIDebugRegisters *ExtensionCommandContext::registers()
+{
+    if (!m_registers)
+        m_registers.create(m_client);
+    return m_registers.data();
+}
+
+CIDebugDataSpaces *ExtensionCommandContext::dataSpaces()
+{
+    if (!m_dataSpaces)
+        m_dataSpaces.create(m_client);
+    return m_dataSpaces.data();
+}
+
+ULONG ExtensionCommandContext::threadId()
+{
+    if (CIDebugSystemObjects *so = systemObjects()) {
+        ULONG threadId = 0;
+        if (SUCCEEDED(so->GetCurrentThreadId(&threadId)))
+            return threadId;
+    }
+    return 0;
+}
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h
new file mode 100644
index 00000000000..79933dffece
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/extensioncontext.h
@@ -0,0 +1,134 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef EXTENSIONCONTEXT_H
+#define EXTENSIONCONTEXT_H
+
+#include "common.h"
+#include "iinterfacepointer.h"
+
+#include <memory>
+#include <map>
+
+class SymbolGroup;
+
+// Global singleton with context.
+// Caches a symbolgroup per frame and thread as long as the session is accessible.
+class ExtensionContext {
+    ExtensionContext(const ExtensionContext&);
+    ExtensionContext& operator=(const ExtensionContext&);
+
+    ExtensionContext();
+public:
+    // Key used to report stop reason in StopReasonMap
+    static const char *stopReasonKeyC;
+    // Map of parameters reported with the next stop as GDBMI
+    typedef std::map<std::string, std::string> StopReasonMap;
+
+    ~ExtensionContext();
+
+    static ExtensionContext &instance();
+    // Call from DLL initialization.
+    HRESULT initialize(PULONG Version, PULONG Flags);
+
+    // Hook up our Event and Output callbacks that report exceptions and events.
+    // Call this from the first extension command that gets a client.
+    // Does not work when called from initialization.
+    void hookCallbacks(CIDebugClient *client);
+
+    // Report output in standardized format understood by Qt Creator.
+    // '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
+    // Char code is 'R' command reply, 'N' command fail, 'E' event notification
+    bool report(char code, int token, const char *serviceName, PCSTR Format, ...);
+
+    ULONG executionStatus() const;
+    // Call from notify handler, tell engine about state.
+    void notifyState(ULONG Notify);
+    // register as '.idle_cmd' to notify creator about stop
+    void notifyIdle();
+
+    // Return symbol group for frame (cache as long as frame does not change).
+    SymbolGroup *symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage);
+    int symbolGroupFrame() const;
+
+    // Stop reason is reported with the next idle notification
+    void setStopReason(const StopReasonMap &, const std::string &reason = std::string());
+
+private:
+    void unhookCallbacks();
+    bool isInitialized() const;
+    void discardSymbolGroup();
+
+    IInterfacePointer<CIDebugControl> m_control;
+    std::auto_ptr<SymbolGroup> m_symbolGroup;
+
+    CIDebugClient *m_hookedClient;
+    IDebugEventCallbacks *m_oldEventCallback;
+    IDebugOutputCallbacksWide *m_oldOutputCallback;
+    IDebugEventCallbacks *m_creatorEventCallback;
+    IDebugOutputCallbacksWide *m_creatorOutputCallback;
+
+    StopReasonMap m_stopReason;
+};
+
+// Context for extension commands to be instantiated on stack in a command handler.
+class ExtensionCommandContext
+{
+    ExtensionCommandContext(const ExtensionCommandContext&);
+    ExtensionCommandContext &operator=(const ExtensionCommandContext&);
+public:
+    explicit ExtensionCommandContext(CIDebugClient *Client);
+    ~ExtensionCommandContext();
+
+    // For accessing outside commands. Might return 0, based on the
+    // assumption that there is only once instance active.
+    static ExtensionCommandContext *instance();
+
+    CIDebugControl *control();
+    CIDebugSymbols *symbols();
+    CIDebugSystemObjects *systemObjects();
+    CIDebugAdvanced *advanced();
+    CIDebugRegisters *registers();
+    CIDebugDataSpaces *dataSpaces();
+
+    ULONG threadId();
+
+private:
+    static ExtensionCommandContext *m_instance;
+
+    CIDebugClient *m_client;
+    IInterfacePointer<CIDebugControl> m_control;
+    IInterfacePointer<CIDebugSymbols> m_symbols;
+    IInterfacePointer<CIDebugAdvanced> m_advanced;
+    IInterfacePointer<CIDebugSystemObjects> m_systemObjects;
+    IInterfacePointer<CIDebugRegisters> m_registers;
+    IInterfacePointer<CIDebugDataSpaces> m_dataSpaces;
+};
+
+#endif // EXTENSIONCONTEXT_H
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
new file mode 100644
index 00000000000..aff59c3a0e3
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
@@ -0,0 +1,561 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "gdbmihelpers.h"
+#include "stringutils.h"
+#include "iinterfacepointer.h"
+#include "base64.h"
+
+#include <vector>
+
+StackFrame::StackFrame(ULONG64 a) : address(a), line(0) {}
+
+std::wstring StackFrame::fileName() const
+{
+    std::wstring::size_type lastSlash = fullPathName.rfind(L'\\');
+    if (lastSlash == std::wstring::npos)
+        return fullPathName;
+    return fullPathName.substr(lastSlash + 1, fullPathName.size() - lastSlash  - 1);
+}
+
+void StackFrame::formatGDBMI(std::ostream &str, unsigned level) const
+{
+    str << "frame={level=\"" << level << "\",addr=\"0x"
+        << std::hex << address << std::dec << '"';
+    if (!function.empty())
+        str << ",func=\"" << gdbmiWStringFormat(function) << '"';
+    if (!fullPathName.empty()) { // Creator/gdbmi expects 'clean paths'
+        std::wstring cleanPath = fullPathName;
+        replace(cleanPath, L'\\', L'/');
+        str << ",fullname=\"" << gdbmiWStringFormat(fullPathName)
+            << "\",file=\"" << gdbmiWStringFormat(fileName()) << "\",line=\"" << line << '"';
+    }
+    str << '}';
+}
+
+Thread::Thread(ULONG i, ULONG sysId) : id(i), systemId(sysId) {}
+
+void Thread::formatGDBMI(std::ostream &str) const
+{
+    str << "{id=\"" << id << "\",target-id=\"" << systemId << "\",";
+    frame.formatGDBMI(str);
+    if (!name.empty())
+        str << ",name=\"" << gdbmiWStringFormat(name) << '"';
+    if (!state.empty())
+        str << ",state=\"" << state << '"';
+    str << '}';
+}
+
+static inline std::string msgGetThreadsFailed(const std::string &why)
+{
+    return std::string("Unable to determine the thread information: ") + why;
+}
+
+static inline bool setCurrentThread(CIDebugSystemObjects *debugSystemObjects,
+                                    ULONG id, std::string *errorMessage)
+{
+    const HRESULT hr = debugSystemObjects->SetCurrentThreadId(id);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("SetCurrentThreadId", hr);
+        return false;
+    }
+    return true;
+}
+
+// Fill in stack frame info
+void getFrame(CIDebugSymbols *debugSymbols,
+              const DEBUG_STACK_FRAME &s, StackFrame *f)
+{
+    WCHAR wBuf[MAX_PATH];
+    f->address = s.InstructionOffset;
+    HRESULT hr = debugSymbols->GetNameByOffsetWide(f->address, wBuf, MAX_PATH, 0, 0);
+    if (SUCCEEDED(hr)) {
+        f->function = wBuf;
+    } else {
+        f->function.clear();
+    }
+    ULONG64 ul64Displacement = 0;
+    hr = debugSymbols->GetLineByOffsetWide(f->address, &(f->line), wBuf, MAX_PATH, 0, &ul64Displacement);
+    if (SUCCEEDED(hr)) {
+        f->fullPathName = wBuf;
+    } else {
+        f->fullPathName.clear();
+        f->line = 0;
+    }
+}
+
+// Determine the frames of the threads.
+// Note: Current thread is switched as a side effect!
+static bool getThreadFrames(CIDebugSymbols *debugSymbols,
+                            CIDebugSystemObjects *debugSystemObjects,
+                            CIDebugControl *debugControl,
+                            Threads *threads, std::string *errorMessage)
+{
+    enum { MaxFrames = 1 };
+
+    DEBUG_STACK_FRAME frames[MaxFrames];
+    const Threads::iterator end = threads->end();
+    for (Threads::iterator it = threads->begin(); it != end; ++it) {
+        if (!setCurrentThread(debugSystemObjects, it->id, errorMessage))
+            return false;
+        ULONG frameCount;
+        const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
+        if (SUCCEEDED(hr))
+            getFrame(debugSymbols, frames[0], &(it->frame));
+    }
+    return true;
+}
+
+bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage)
+{
+    IInterfacePointer<CIDebugClient> client;
+    if (!client.create()) {
+        *errorMessage = "Cannot obtain client.";
+        return false;
+    }
+    IInterfacePointer<CIDebugSymbols> symbols(client.data());
+    IInterfacePointer<CIDebugControl> control(client.data());
+    if (!symbols || !control) {
+        *errorMessage = "Cannot obtain required objects.";
+        return false;
+    }
+    return getFrame(symbols.data(), control.data(), n, f, errorMessage);
+}
+
+bool getFrame(CIDebugSymbols *debugSymbols,
+              CIDebugControl *debugControl,
+              unsigned n, StackFrame *f, std::string *errorMessage)
+{
+    DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[n + 1];
+    ULONG frameCount;
+    const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, n + 1, &frameCount);
+    if (FAILED(hr)) {
+        delete [] frames;
+        *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
+        return false;
+    }
+    getFrame(debugSymbols, frames[n], f);
+    delete [] frames;
+    return true;
+}
+
+bool threadList(CIDebugSystemObjects *debugSystemObjects,
+                CIDebugSymbols *debugSymbols,
+                CIDebugControl *debugControl,
+                CIDebugAdvanced *debugAdvanced,
+                Threads* threads, ULONG *currentThreadId,
+                std::string *errorMessage)
+{
+    threads->clear();
+    ULONG threadCount;
+    *currentThreadId = 0;
+    // Get count
+    HRESULT hr= debugSystemObjects->GetNumberThreads(&threadCount);
+    if (FAILED(hr)) {
+        *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetNumberThreads", hr));
+        return false;
+    }
+    // Get index of current
+    if (!threadCount)
+        return true;
+    hr = debugSystemObjects->GetCurrentThreadId(currentThreadId);
+    if (FAILED(hr)) {
+        *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetCurrentThreadId", hr));
+        return false;
+    }
+    // Get Identifiers
+    threads->reserve(threadCount);
+    ULONG *ids = new ULONG[threadCount];
+    ULONG *systemIds = new ULONG[threadCount];
+    hr = debugSystemObjects->GetThreadIdsByIndex(0, threadCount, ids, systemIds);
+    if (FAILED(hr)) {
+        *errorMessage= msgGetThreadsFailed(msgDebugEngineComFailed("GetThreadIdsByIndex", hr));
+        return false;
+    }
+    // Create entries
+    static WCHAR name[256];
+    for (ULONG i= 0; i < threadCount ; i++) {
+        const ULONG id = ids[i];
+        Thread thread(id, systemIds[i]);
+        // Thread name
+        ULONG bytesReceived = 0;
+        hr = debugAdvanced->GetSystemObjectInformation(DEBUG_SYSOBJINFO_THREAD_NAME_WIDE,
+                                                       0, id, name,
+                                                       sizeof(name), &bytesReceived);
+        if (SUCCEEDED(hr) && bytesReceived)
+            thread.name = name;
+        threads->push_back(thread);
+    }
+    delete [] ids;
+    delete [] systemIds;
+    // Get frames and at all events,
+    // restore current thread after switching frames.
+    const bool framesOk = getThreadFrames(debugSymbols,
+                                          debugSystemObjects,
+                                          debugControl,
+                                          threads, errorMessage);
+    const bool restoreOk =setCurrentThread(debugSystemObjects, *currentThreadId, errorMessage);
+    return framesOk && restoreOk;
+}
+
+/* Format as: \code
+
+52^done,threads=[{id="1",target-id="Thread 4740.0xb30",
+frame={level="0",addr="0x00403487",func="foo",
+args=[{name="this",value="0x27fee4"}],file="mainwindow.cpp",fullname="c:\\qt\\projects\\gitguim\\app/mainwindow.cpp",line="298"},state="stopped"}],
+current-thread-id="1"
+*/
+
+std::string gdbmiThreadList(CIDebugSystemObjects *debugSystemObjects,
+                            CIDebugSymbols *debugSymbols,
+                            CIDebugControl *debugControl,
+                            CIDebugAdvanced *debugAdvanced,
+                            std::string *errorMessage)
+{
+    Threads threads;
+    ULONG currentThreadId;
+    if (!threadList(debugSystemObjects, debugSymbols, debugControl,
+                    debugAdvanced, &threads, &currentThreadId, errorMessage))
+        return std::string();
+    std::ostringstream str;
+    str << "{threads=[";
+    const Threads::const_iterator cend = threads.end();
+    for (Threads::const_iterator it = threads.begin(); it != cend; ++it)
+        it->formatGDBMI(str);
+    str << "],current-thread-id=\"" << currentThreadId << "\"}";
+    return str.str();
+}
+
+Module::Module() : deferred(false), base(0), size(0)
+{
+}
+
+Modules getModules(CIDebugSymbols *syms, std::string *errorMessage)
+{
+    enum { BufSize = 1024 };
+
+    char nameBuf[BufSize];
+    char fileBuf[BufSize];
+
+    ULONG Loaded;
+    ULONG Unloaded;
+    HRESULT hr = syms->GetNumberModules(&Loaded, &Unloaded);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr);
+        return Modules();
+    }
+    const ULONG count = Loaded + Unloaded;
+    Modules rc;
+    rc.reserve(count);
+    DEBUG_MODULE_PARAMETERS *parameters = new DEBUG_MODULE_PARAMETERS[count];
+    hr = syms->GetModuleParameters(count, NULL, 0, parameters);
+    if (FAILED(hr)) {
+        delete [] parameters;
+        *errorMessage = msgDebugEngineComFailed("GetModuleParameters", hr);
+        return Modules();
+    }
+
+    for (ULONG m = 0; m < count; m++) {
+        Module module;
+        module.base = parameters[m].Base;
+        module.size = parameters[m].Size;
+        module.deferred = parameters[m].Flags == DEBUG_SYMTYPE_DEFERRED;
+        hr = syms->GetModuleNames(m, 0, fileBuf, BufSize, NULL,
+                                  nameBuf, BufSize, NULL, NULL, NULL, NULL);
+        if (FAILED(hr))
+            break; // Fail silently should unloaded modules not work.
+        module.name = nameBuf;
+        module.image = fileBuf;
+        rc.push_back(module);
+    }
+    return rc;
+}
+
+std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *errorMessage)
+{
+    const Modules modules = getModules(syms, errorMessage);
+    if (modules.empty())
+        return std::string();
+
+    std::ostringstream str;
+    str << '[' << std::hex << std::showbase;
+    const Modules::size_type size = modules.size();
+    for (Modules::size_type m = 0; m < size; m++) {
+        const Module &module = modules.at(m);
+        if (m)
+            str << ',';
+        str << "{name=\"" << module.name
+            << "\",image=\"" << gdbmiStringFormat(module.image)
+            << "\",start=\"" << module.base << "\",end=\""
+            << (module.base + module.size - 1) << '"';
+        if (module.deferred)
+            str << "{deferred=\"true\"";
+        str << '}';
+        if (humanReadable)
+            str << '\n';
+    }
+    str << ']';
+    return str.str();
+}
+
+// Description of a DEBUG_VALUE type field
+const wchar_t *valueType(ULONG type)
+{
+    switch (type) {
+    case DEBUG_VALUE_INT8:
+        return L"I8";
+    case DEBUG_VALUE_INT16:
+        return L"I16";
+    case DEBUG_VALUE_INT32:
+        return L"I32";
+    case DEBUG_VALUE_INT64:
+        return  L"I64";
+    case DEBUG_VALUE_FLOAT32:
+        return L"F32";
+    case DEBUG_VALUE_FLOAT64:
+        return L"F64";
+    case DEBUG_VALUE_FLOAT80:
+        return L"F80";
+    case DEBUG_VALUE_FLOAT128:
+        return L"F128";
+    case DEBUG_VALUE_VECTOR64:
+        return L"V64";
+    case DEBUG_VALUE_VECTOR128:
+        return L"V128";
+    }
+    return L"";
+}
+
+// Format a 128bit vector register by adding digits in reverse order
+void formatVectorRegister(std::ostream &str, const unsigned char *array, int size)
+{
+    const char oldFill = str.fill('0');
+
+    str << "0x" << std::hex;
+    for (int i = size - 1; i >= 0; i--) {
+        str.width(2);
+        str << unsigned(array[i]);
+    }
+    str << std::dec;
+
+    str.fill(oldFill);
+}
+
+void formatDebugValue(std::ostream &str, const DEBUG_VALUE &dv, CIDebugControl *ctl)
+{
+    const std::ios::fmtflags savedState = str.flags();
+    switch (dv.Type) {
+    // Do not use showbase to get the hex prefix as this omits it for '0'. Grmpf.
+    case DEBUG_VALUE_INT8:
+        str << std::hex << "0x" << unsigned(dv.I8);
+        break;
+    case DEBUG_VALUE_INT16:
+        str << std::hex << "0x" << dv.I16;
+        break;
+    case DEBUG_VALUE_INT32:
+        str << std::hex << "0x" << dv.I32;
+        break;
+    case DEBUG_VALUE_INT64:
+        str << std::hex << "0x" << dv.I64;
+        break;
+    case DEBUG_VALUE_FLOAT32:
+        str << dv.F32;
+        break;
+    case DEBUG_VALUE_FLOAT64:
+        str << dv.F64;
+        break;
+    case DEBUG_VALUE_FLOAT80:
+    case DEBUG_VALUE_FLOAT128: { // Convert to double
+            DEBUG_VALUE doubleValue;
+            if (ctl && SUCCEEDED(ctl->CoerceValue(const_cast<DEBUG_VALUE*>(&dv), DEBUG_VALUE_FLOAT64, &doubleValue)))
+                str << dv.F64;
+            }
+        break;
+    case DEBUG_VALUE_VECTOR64:
+        formatVectorRegister(str, dv.VI8, 8);
+        break;
+    case DEBUG_VALUE_VECTOR128:
+        formatVectorRegister(str, dv.VI8, 16);
+        break;
+    }
+    str.flags(savedState);
+}
+
+Register::Register() : subRegister(false), pseudoRegister(false)
+{
+    value.Type = DEBUG_VALUE_INT32;
+    value.I32 = 0;
+}
+
+static inline std::wstring registerDescription(const DEBUG_REGISTER_DESCRIPTION &d)
+{
+    std::wostringstream str;
+    str << valueType(d.Type);
+    if (d.Flags & DEBUG_REGISTER_SUB_REGISTER)
+        str << ", sub-register of " << d.SubregMaster;
+    return str.str();
+}
+
+Registers getRegisters(CIDebugRegisters *regs,
+                       unsigned flags,
+                       std::string *errorMessage)
+{
+    enum { bufSize= 128 };
+    WCHAR buf[bufSize];
+
+    ULONG registerCount = 0;
+    HRESULT hr = regs->GetNumberRegisters(&registerCount);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("GetNumberRegisters", hr);
+        return Registers();
+    }
+    ULONG pseudoRegisterCount = 0;
+    if (flags & IncludePseudoRegisters) {
+        hr = regs->GetNumberPseudoRegisters(&pseudoRegisterCount);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetNumberPseudoRegisters", hr);
+            return Registers();
+        }
+    }
+
+    Registers rc;
+    rc.reserve(registerCount + pseudoRegisterCount);
+
+    // Standard registers
+    DEBUG_REGISTER_DESCRIPTION description;
+    DEBUG_VALUE value;
+    for (ULONG r = 0; r < registerCount; r++) {
+        hr = regs->GetDescriptionWide(r, buf, bufSize, NULL, &description);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetDescription", hr);
+            return Registers();
+        }
+        hr = regs->GetValue(r, &value);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetValue", hr);
+            return Registers();
+        }
+        const bool isSubRegister = (description.Flags & DEBUG_REGISTER_SUB_REGISTER);
+        if (!isSubRegister || (flags & IncludeSubRegisters)) {
+            Register reg;
+            reg.name = buf;
+            reg.description = registerDescription(description);
+            reg.subRegister = isSubRegister;
+            reg.value = value;
+            rc.push_back(reg);
+        }
+    }
+
+    // Pseudo
+    for (ULONG r = 0; r < pseudoRegisterCount; r++) {
+        ULONG type;
+        hr = regs->GetPseudoDescriptionWide(r, buf, bufSize, NULL, NULL, &type);
+        if (FAILED(hr))
+            continue; // Fails for some pseudo registers
+        hr = regs->GetValue(r, &value);
+        if (FAILED(hr))
+            continue; // Fails for some pseudo registers
+        Register reg;
+        reg.pseudoRegister = true;
+        reg.name = buf;
+        reg.description = valueType(type);
+        reg.value = value;
+        rc.push_back(reg);
+    }
+    return rc;
+}
+
+std::string gdbmiRegisters(CIDebugRegisters *regs,
+                           CIDebugControl *control,
+                           bool humanReadable,
+                           unsigned flags,
+                           std::string *errorMessage)
+{
+    if (regs == 0 || control == 0) {
+        *errorMessage = "Required interfaces missing for registers dump.";
+        return std::string();
+    }
+
+    const Registers registers = getRegisters(regs, flags, errorMessage);
+    if (registers.empty())
+        return std::string();
+
+    std::ostringstream str;
+    str << '[';
+    if (humanReadable)
+        str << '\n';
+    const Registers::size_type size = registers.size();
+    for (Registers::size_type r = 0; r < size; r++) {
+        const Register &reg = registers.at(r);
+        if (r)
+            str << ',';
+        str << "{number=\"" << r << "\",name=\"" << gdbmiWStringFormat(reg.name) << '"';
+        if (!reg.description.empty())
+            str << ",description=\"" << gdbmiWStringFormat(reg.description) << '"';
+        if (reg.subRegister)
+            str << ",subregister=\"true\"";
+        if (reg.pseudoRegister)
+            str << ",pseudoregister=\"true\"";
+        str << ",value=\"";
+        formatDebugValue(str, reg.value, control);
+        str << "\"}";
+        if (humanReadable)
+            str << '\n';
+    }
+    str << ']';
+    if (humanReadable)
+        str << '\n';
+    return str.str();
+}
+
+std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage)
+{
+    unsigned char *buffer = new unsigned char[length];
+    std::fill(buffer, buffer + length, 0);
+    ULONG received = 0;
+    const HRESULT hr = ds->ReadVirtual(address, buffer, length, &received);
+    if (FAILED(hr)) {
+        delete [] buffer;
+        std::ostringstream estr;
+        estr << "Cannot read " << length << " bytes from " << address << ": "
+                << msgDebugEngineComFailed("ReadVirtual", hr);
+        *errorMessage = estr.str();
+        return std::string();
+    }
+    if (received < length) {
+        std::ostringstream estr;
+        estr << "Warning: Received only " << received << " bytes of " << length << " requested at " << address << '.';
+        *errorMessage = estr.str();
+    }
+
+    std::ostringstream str;
+    base64Encode(str, buffer, length);
+    delete [] buffer;
+    return str.str();
+}
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h
new file mode 100644
index 00000000000..73622668cc4
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h
@@ -0,0 +1,148 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef THREADLIST_H
+#define THREADLIST_H
+
+#include "common.h"
+#include <vector>
+
+/* Various helpers to the extension commands to retrieve debuggee information
+ * in suitable formats for the debugger engine. */
+
+/* Helpers to retrieve threads and their top stack frame
+ * in one roundtrip, conveniently in GDBMI format. */
+
+struct StackFrame
+{
+    StackFrame(ULONG64 a = 0);
+    void formatGDBMI(std::ostream &str, unsigned level = 0) const;
+    std::wstring fileName() const;
+
+    ULONG64 address;
+    std::wstring function;
+    std::wstring fullPathName;
+    ULONG line;
+};
+
+bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage);
+bool getFrame(CIDebugSymbols *debugSymbols, CIDebugControl *debugControl,
+              unsigned n, StackFrame *f, std::string *errorMessage);
+
+struct Thread
+{
+    Thread(ULONG i = 0, ULONG sysId = 0);
+    void formatGDBMI(std::ostream &str) const;
+
+    ULONG id;
+    ULONG systemId;
+    ULONG64 address;
+    std::string state;
+    std::wstring name;
+    StackFrame frame;
+};
+
+typedef std::vector<Thread> Threads;
+
+// Get list of threads.
+bool threadList(CIDebugSystemObjects *debugSystemObjects,
+                CIDebugSymbols *debugSymbols,
+                CIDebugControl *debugControl,
+                CIDebugAdvanced *debugAdvanced,
+                Threads* threads, ULONG *currentThreadId,
+                std::string *errorMessage);
+
+// Convenience formatting as GDBMI
+std::string gdbmiThreadList(CIDebugSystemObjects *debugSystemObjects,
+                            CIDebugSymbols *debugSymbols,
+                            CIDebugControl *debugControl,
+                            CIDebugAdvanced *debugAdvanced,
+                            std::string *errorMessage);
+
+/* Helpers for retrieving module lists */
+
+struct Module {
+    Module();
+
+    std::string name;
+    std::string image;
+    bool deferred;
+    ULONG64 base;
+    ULONG64 size;
+};
+
+typedef std::vector<Module> Modules;
+
+// Retrieve modules info
+Modules getModules(CIDebugSymbols *syms, std::string *errorMessage);
+
+// Format modules as GDBMI
+std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *errorMessage);
+
+/* Helpers for registers */
+struct Register
+{
+    Register();
+
+    std::wstring name;
+    std::wstring description;
+    bool subRegister;
+    bool pseudoRegister;
+    DEBUG_VALUE value;
+};
+
+typedef std::vector<Register> Registers;
+
+// Description of a DEBUG_VALUE type field
+const wchar_t *valueType(ULONG type);
+// Helper to format a DEBUG_VALUE
+void formatDebugValue(std::ostream &str, const DEBUG_VALUE &dv, CIDebugControl *ctl = 0);
+
+enum RegisterFlags {
+    IncludePseudoRegisters = 0x1,
+    IncludeSubRegisters = 0x2
+};
+
+// Retrieve current register snapshot using RegisterFlags
+Registers getRegisters(CIDebugRegisters *regs,
+                       unsigned flags,
+                       std::string *errorMessage);
+
+// Format current register snapshot using RegisterFlags as GDBMI
+// This is not exactly using the actual GDB formatting as this is
+// to verbose (names and values separated)
+std::string gdbmiRegisters(CIDebugRegisters *regs,
+                           CIDebugControl *control,
+                           bool humanReadable,
+                           unsigned flags,
+                           std::string *errorMessage);
+
+std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage);
+
+#endif // THREADLIST_H
diff --git a/src/libs/qtcreatorcdbext/iinterfacepointer.h b/src/libs/qtcreatorcdbext/iinterfacepointer.h
new file mode 100644
index 00000000000..558d27b2dca
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/iinterfacepointer.h
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef IINTERFACEPOINTER_H
+#define IINTERFACEPOINTER_H
+
+#include "common.h"
+
+/* AutoPtr for a IDebugInterface */
+template <class IInterface>
+class IInterfacePointer
+{
+    explicit IInterfacePointer(const IInterfacePointer&);
+    IInterfacePointer& operator=(const IInterfacePointer&);
+
+public:
+    inline IInterfacePointer() : m_instance(0), m_hr(S_OK) {}
+
+    // IDebugClient4 does not inherit IDebugClient.
+    inline explicit IInterfacePointer(IDebugClient *client) : m_instance(0), m_hr(S_OK)  { create(client); }
+    inline explicit IInterfacePointer(CIDebugClient *client) : m_instance(0), m_hr(S_OK) { create(client); }
+    inline ~IInterfacePointer() { release(); }
+
+    inline IInterface *operator->() const { return m_instance; }
+    inline IInterface *data() const { return m_instance; }
+    inline bool isNull() const   { return m_instance == 0; }
+    inline operator bool() const { return !isNull(); }
+    inline HRESULT hr() const { return m_hr; }
+
+    // This is for creating a IDebugClient from scratch. Not matches by a constructor,
+    // unfortunately
+    inline bool create() {
+        release();
+        m_hr = DebugCreate(__uuidof(IInterface), (void **)&m_instance);
+        return m_hr == S_OK;
+    }
+
+    inline bool create(IDebugClient *client) {
+        release();
+        m_hr = client->QueryInterface(__uuidof(IInterface), (void **)&m_instance);
+        return m_hr == S_OK;
+    }
+    inline bool create(CIDebugClient *client) {
+        release();
+        m_hr = client->QueryInterface(__uuidof(IInterface), (void **)&m_instance);
+        return m_hr == S_OK;
+    }
+
+
+private:
+    void release() {
+        if (m_instance) {
+            m_instance->Release();
+            m_instance = 0;
+            m_hr = S_OK;
+        }
+    }
+    IInterface *m_instance;
+    HRESULT m_hr;
+};
+
+#endif // IINTERFACEPOINTER_H
diff --git a/src/libs/qtcreatorcdbext/outputcallback.cpp b/src/libs/qtcreatorcdbext/outputcallback.cpp
new file mode 100644
index 00000000000..026d2ab3a1e
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/outputcallback.cpp
@@ -0,0 +1,98 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "outputcallback.h"
+#include "stringutils.h"
+#include "extensioncontext.h"
+#include "base64.h"
+
+#include <cstring>
+
+OutputCallback::OutputCallback(IDebugOutputCallbacksWide *wrapped) : m_wrapped(wrapped)
+{
+}
+
+OutputCallback::~OutputCallback() // must be present to avoid exit crashes
+{
+}
+
+STDMETHODIMP OutputCallback::QueryInterface(
+        THIS_
+        IN REFIID InterfaceId,
+        OUT PVOID* Interface
+        )
+{
+    *Interface = NULL;
+
+    if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
+        IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacksWide)))
+    {
+        *Interface = (IDebugOutputCallbacksWide*)this;
+        AddRef();
+        return S_OK;
+    } else {
+        return E_NOINTERFACE;
+    }
+}
+
+STDMETHODIMP_(ULONG) OutputCallback::AddRef(THIS)
+{
+    // This class is designed to be static so
+    // there's no true refcount.
+    return 1;
+}
+
+STDMETHODIMP_(ULONG) OutputCallback::Release(THIS)
+{
+    // This class is designed to be static so
+    // there's no true refcount.
+    return 0;
+}
+
+STDMETHODIMP OutputCallback::Output(
+        THIS_
+        IN ULONG mask,
+        IN PCWSTR text
+        )
+{
+    // Do not unconditionally output ourselves here, as this causes an endless
+    // recursion. Suppress prompts (note that sequences of prompts may mess parsing up)
+    if (!m_wrapped || mask == DEBUG_OUTPUT_PROMPT)
+        return S_OK;
+    // Wrap debuggee output in gdbmi such that creator recognizes it
+    if (mask != DEBUG_OUTPUT_DEBUGGEE) {
+        m_wrapped->Output(mask, text);
+        return S_OK;
+    }
+    // Base encode as GDBMI is not really made for wide chars
+    std::ostringstream str;
+    base64Encode(str, reinterpret_cast<const unsigned char *>(text), sizeof(wchar_t) * std::wcslen(text));
+    ExtensionContext::instance().report('E', 0, "debuggee_output", str.str().c_str());
+    return S_OK;
+}
diff --git a/src/libs/qtcreatorcdbext/outputcallback.h b/src/libs/qtcreatorcdbext/outputcallback.h
new file mode 100644
index 00000000000..76f73ad3c41
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/outputcallback.h
@@ -0,0 +1,64 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef DEBUGEVENTOUTPUT_H
+#define DEBUGEVENTOUTPUT_H
+
+#include "common.h"
+
+class OutputCallback : public IDebugOutputCallbacksWide
+{
+public:
+    explicit OutputCallback(IDebugOutputCallbacksWide *wrapped);
+    virtual ~OutputCallback();
+    // IUnknown.
+    STDMETHOD(QueryInterface)(
+            THIS_
+            IN REFIID InterfaceId,
+            OUT PVOID* Interface
+            );
+    STDMETHOD_(ULONG, AddRef)(
+            THIS
+            );
+    STDMETHOD_(ULONG, Release)(
+            THIS
+            );
+
+    // IDebugOutputCallbacks.
+    STDMETHOD(Output)(
+            THIS_
+            IN ULONG mask,
+            IN PCWSTR text
+            );
+
+private:
+    IDebugOutputCallbacksWide *m_wrapped;
+};
+
+#endif // DEBUGEVENTOUTPUT_H
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro
new file mode 100644
index 00000000000..5012a427d38
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro
@@ -0,0 +1,14 @@
+TEMPLATE  = subdirs
+
+# Build the Qt Creator CDB extension depending on whether
+# CDB is actually detected.
+# In order to do detection and building in one folder,
+# use a subdir profile in '.'.
+
+include(cdb_detect.pri)
+
+!isEmpty(CDB_PATH) {
+    SUBDIRS += lib_qtcreator_cdbext
+
+    lib_qtcreator_cdbext.file = qtcreatorcdbext_build.pro
+}
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro
new file mode 100644
index 00000000000..9733de7b5b2
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro
@@ -0,0 +1,67 @@
+# Build the Qt Creator CDB extension
+TEMPLATE = lib
+
+include(../../../qtcreator.pri)
+include(cdb_detect.pri)
+
+CONFIG -= precompile_header
+CONFIG += hide_symbols
+
+# Switch to statically linked CRT. Note: There will be only one
+# global state of the CRT, reconsider if other DLLs are required!
+# TODO: No effect, currently?
+QMAKE_CXXFLAGS_RELEASE    -= -MD
+QMAKE_CXXFLAGS_DEBUG      -= -MDd
+QMAKE_CXXFLAGS_RELEASE    += -MT
+QMAKE_CXXFLAGS_DEBUG      += -MTd
+
+BASENAME=qtcreatorcdbext
+
+DEF_FILE=qtcreatorcdbext.def
+
+IDE_BASE_PATH=$$dirname(IDE_APP_PATH)
+
+# Find out 64/32bit and determine target directories accordingly.
+# TODO: This is an ugly hack. Better check compiler (stderr) or something?
+ENV_LIB_PATH=$$(LIBPATH)
+
+
+contains(ENV_LIB_PATH, ^.*amd64.*$) {
+    DESTDIR=$$IDE_BASE_PATH/lib/$${BASENAME}64
+    CDB_PLATFORM=amd64
+} else {
+    DESTDIR=$$IDE_BASE_PATH/lib/$${BASENAME}32
+    CDB_PLATFORM=i386
+}
+
+TARGET = $$BASENAME
+
+message("Compiling Qt Creator CDB extension $$TARGET $$DESTDIR for $$CDB_PLATFORM using $$CDB_PATH")
+
+INCLUDEPATH += $$CDB_PATH/inc
+LIBS+= -L$$CDB_PATH/lib/$$CDB_PLATFORM -ldbgeng
+
+CONFIG -= qt
+QT -= gui
+QT -= core
+
+SOURCES += qtcreatorcdbextension.cpp \
+    extensioncontext.cpp \
+    eventcallback.cpp \
+    symbolgroup.cpp \
+    common.cpp \
+    stringutils.cpp \
+    gdbmihelpers.cpp \
+    outputcallback.cpp \
+    base64.cpp
+
+HEADERS += extensioncontext.h \
+    common.h \
+    iinterfacepointer.h \
+    eventcallback.h \
+    symbolgroup.h \
+    stringutils.h \
+    gdbmihelpers.h \
+    outputcallback.h \
+    base64.h
+
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
new file mode 100644
index 00000000000..8212b32620e
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
@@ -0,0 +1,368 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "extensioncontext.h"
+#include "outputcallback.h"
+#include "eventcallback.h"
+#include "symbolgroup.h"
+#include "stringutils.h"
+#include "gdbmihelpers.h"
+
+#include <cstdio>
+#include <sstream>
+#include <list>
+#include <iterator>
+
+/* QtCreatorCDB ext is an extension loaded into CDB.exe (see cdbengine2.cpp):
+ * - Notification about the state of the debugging session:
+ *   + idle: (hooked with .idle_cmd) debuggee stopped
+ *   + accessible: Debuggee stopped, cdb.exe accepts commands
+ *   + inaccessible: Debuggee runs, no way to post commands
+ *   + session active/inactive: Lost debuggee, terminating.
+ * - Hook up with output/event callbacks and produce formatted output
+ * - Provide some extension commands that produce output in a standardized (GDBMI)
+ *   format that ends up in handleExtensionMessage().
+ *   + pid     Return debuggee pid for interrupting.
+ *   + locals  Print locals from SymbolGroup
+ *   + expandLocals Expand locals in symbol group
+ *   + registers, modules, threads */
+
+typedef std::vector<std::string> StringVector;
+typedef std::list<std::string> StringList;
+
+// Split & Parse the arguments of a command and extract the
+// optional first integer token argument ('command -t <number> remaining arguments')
+// Each command takes the 'token' argument and includes it in its output
+// so that the calling CDB engine in Qt Creator can match the callback.
+
+template<class StringContainer>
+static inline StringContainer commandTokens(PCSTR args, int *token = 0)
+{
+    typedef StringContainer::iterator ContainerIterator;
+
+    if (token)
+        *token = 0;
+    std::string cmd(args);
+    simplify(cmd);
+    StringContainer tokens;
+    split(cmd, ' ', std::back_inserter(tokens));
+
+    // Check for token
+    ContainerIterator it = tokens.begin();
+    if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
+        if (token)
+            std::istringstream(*it) >> *token;
+        tokens.erase(tokens.begin(), ++it);
+    }
+    return tokens;
+}
+
+// Extension command 'pid':
+// Report back PID so that Qt Creator engine knows whom to DebugBreakProcess.
+extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
+{
+    ExtensionContext::instance().hookCallbacks(client);
+
+    int token;
+    commandTokens<StringList>(args, &token);
+
+    if (const ULONG pid = currentProcessId(client)) {
+        ExtensionContext::instance().report('R', token, "pid", "%u", pid);
+    } else {
+        ExtensionContext::instance().report('N', token, "pid", "0");
+    }
+    return S_OK;
+}
+
+// Extension command 'expandlocals':
+// Expand a comma-separated iname-list of local variables.
+
+extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
+{
+    ExtensionCommandContext exc(client);
+    unsigned frame = 0;
+    SymbolGroup *symGroup = 0;
+    std::string errorMessage;
+
+    int token;
+    const StringVector tokens = commandTokens<StringVector>(args, &token);
+    StringVector inames;
+    if (tokens.size() == 2u && sscanf(tokens.front().c_str(), "%u", &frame) ==  1) {
+        symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
+        split(tokens.at(1), ',', std::back_inserter(inames));
+    } else {
+        std::ostringstream str;
+        str << "Invalid parameter: '" << args << "' (usage expand <frame> iname1,iname2..).";
+        errorMessage = str.str();
+    }
+    if (symGroup) {
+        const unsigned succeeded = symGroup->expandList(inames, &errorMessage);
+        ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
+                                            succeeded, unsigned(inames.size()), errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str());
+    }
+    return S_OK;
+}
+
+static inline std::string msgLocalsUsage(PCSTR args)
+{
+    std::ostringstream str;
+    str << "Invalid parameter: '" << args << "' (usage: locals [-d] <frame> [iname]).";
+    return str.str();
+}
+
+// Extension command 'locals':
+// Display local variables of symbol group in GDBMI or debug output form.
+// Takes an optional iname which is expanded before displaying (for updateWatchData)
+
+static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
+{
+    // Parse the command
+    unsigned debugOutput = 0;
+    bool humanReadableGdbmi = false;
+    std::string iname;
+
+    StringList tokens = commandTokens<StringList>(args, token);
+    while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
+        // Parse options -d (debug)/- humanreadable GDBMI
+        switch (tokens.front().at(1)) {
+        case 'd':
+            debugOutput++;
+            break;
+        case 'h':
+            humanReadableGdbmi = true;
+            break;
+        }
+        tokens.pop_front();
+    }
+    unsigned frame;
+    if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
+        *errorMessage = msgLocalsUsage(args);
+        return std::string();
+    }
+
+    tokens.pop_front();
+    if (!tokens.empty())
+        iname = tokens.front();
+
+    SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
+    if (!symGroup)
+        return std::string();
+
+    // Complete dump
+    if (iname.empty())
+        return debugOutput ? symGroup->debug(debugOutput - 1) : symGroup->dump(humanReadableGdbmi);
+    // Look up iname
+    return symGroup->dump(iname, humanReadableGdbmi, errorMessage);
+}
+
+extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
+{
+    ExtensionCommandContext exc(client);
+    std::string errorMessage;
+    int token;
+    const std::string output = commmandLocals(exc, args, &token, &errorMessage);
+    if (output.empty()) {
+        ExtensionContext::instance().report('N', token, "locals", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('R', token, "locals", "%s", output.c_str());
+    }
+    return S_OK;
+}
+
+// Extension command 'assign':
+// Assign locals by iname: 'assign locals.x=5'
+
+extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
+{
+    ExtensionCommandContext exc(client);
+
+    std::string errorMessage;
+    bool success = false;
+
+    int token = 0;
+    do {
+        const StringList tokens = commandTokens<StringList>(argsIn, &token);
+        // Parse 'assign locals.x=5'
+        const std::string::size_type equalsPos = tokens.size() == 1 ? tokens.front().find('=') : std::string::npos;
+        if (equalsPos == std::string::npos) {
+            errorMessage = "Syntax error, expecting 'locals.x=5'.";
+            break;
+        }
+        const std::string iname = tokens.front().substr(0, equalsPos);
+        const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
+        // get the symbolgroup
+        const int currentFrame = ExtensionContext::instance().symbolGroupFrame();
+        if (currentFrame < 0) {
+            errorMessage = "No current frame.";
+            break;
+        }
+        SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
+        if (!symGroup)
+            break;
+        success = symGroup->assign(iname, value, &errorMessage);
+    } while (false);
+
+    if (success) {
+        ExtensionContext::instance().report('R', token, "assign", "Ok");
+    } else {
+        ExtensionContext::instance().report('N', token, "assign", errorMessage.c_str());
+    }
+    return S_OK;
+}
+
+// Extension command 'threads':
+// List all thread info in GDBMI syntax
+
+extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR  argsIn)
+{
+    ExtensionCommandContext exc(client);
+    std::string errorMessage;
+
+    int token;
+    commandTokens<StringList>(argsIn, &token);
+
+    const std::string gdbmi = gdbmiThreadList(exc.systemObjects(),
+                                              exc.symbols(),
+                                              exc.control(),
+                                              exc.advanced(),
+                                              &errorMessage);
+    if (gdbmi.empty()) {
+        ExtensionContext::instance().report('N', token, "threads", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('R', token, "threads", gdbmi.c_str());
+    }
+    return S_OK;
+}
+
+// Extension command 'registers':
+// List all registers in GDBMI syntax
+
+extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
+{
+    ExtensionCommandContext exc(Client);
+    std::string errorMessage;
+
+    int token;
+    const StringList tokens = commandTokens<StringList>(argsIn, &token);
+    const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
+    const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
+    if (regs.empty()) {
+        ExtensionContext::instance().report('N', token, "registers", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('R', token, "registers", regs.c_str());
+    }
+    return S_OK;
+}
+
+// Extension command 'modules':
+// List all modules in GDBMI syntax
+
+extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
+{
+    ExtensionCommandContext exc(Client);
+    std::string errorMessage;
+
+    int token;
+    const StringList tokens = commandTokens<StringList>(argsIn, &token);
+    const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
+    const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
+    if (modules.empty()) {
+        ExtensionContext::instance().report('N', token, "modules", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('R', token, "modules", modules.c_str());
+    }
+    return S_OK;
+}
+
+// Report stop of debuggee to Creator. This is hooked up as .idle_cmd command
+// by the Creator engine to reliably get notified about stops.
+extern "C" HRESULT CALLBACK idle(CIDebugClient *, PCSTR)
+{
+    ExtensionContext::instance().notifyIdle();
+    return S_OK;
+}
+
+// Extension command 'help':
+// Display version
+
+extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
+{
+    dprintf("Qt Creator CDB extension built %s\n", __DATE__);
+    return S_OK;
+}
+
+// Extension command 'memory':
+// Display memory as base64
+
+extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
+{
+    ExtensionCommandContext exc(Client);
+    std::string errorMessage;
+    std::string memory;
+
+    int token;
+    ULONG64 address = 0;
+    ULONG length = 0;
+
+    const StringVector tokens = commandTokens<StringVector>(argsIn, &token);
+    if (tokens.size()  == 2
+            && integerFromString(tokens.front(), &address)
+            && integerFromString(tokens.at(1), &length)) {
+        memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
+    } else {
+        errorMessage = "Invalid parameters to memory command.";
+    }
+
+    if (memory.empty()) {
+        ExtensionContext::instance().report('N', token, "memory", errorMessage.c_str());
+    } else {
+        ExtensionContext::instance().report('R', token, "memory", memory.c_str());
+        if (!errorMessage.empty())
+            ExtensionContext::instance().report('W', token, "memory", errorMessage.c_str());
+    }
+    return S_OK;
+}
+
+// Hook for dumping Known Structs. Not currently used.
+// Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText.
+
+extern "C"  HRESULT CALLBACK KnownStructOutput(ULONG Flag, ULONG64 Address, PSTR StructName, PSTR Buffer, PULONG /* BufferSize */)
+{
+    if (Flag == DEBUG_KNOWN_STRUCT_GET_NAMES) {
+        memcpy(Buffer, "\0\0", 2);
+        return S_OK;
+    }
+    // Usually 260 chars buf
+    std::ostringstream str;
+    str << " KnownStructOutput 0x" << std::hex << Address << ' '<< StructName;
+    strcpy(Buffer, str.str().c_str());
+    return S_OK;
+}
diff --git a/src/libs/qtcreatorcdbext/stringutils.cpp b/src/libs/qtcreatorcdbext/stringutils.cpp
new file mode 100644
index 00000000000..ed0c7309fda
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/stringutils.cpp
@@ -0,0 +1,180 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "stringutils.h"
+
+#include <cctype>
+#include <iostream>
+#include <sstream>
+
+static const char whiteSpace[] = " \t\r\n";
+
+void trimFront(std::string &s)
+{
+    if (s.empty())
+        return;
+    std::string::size_type pos = s.find_first_not_of(whiteSpace);
+    if (pos == 0)
+        return;
+    if (pos == std::string::npos) { // All blanks?!
+        s.clear();
+    } else {
+        s.erase(0, pos);
+    }
+}
+
+void trimBack(std::string &s)
+{
+    if (s.empty())
+        return;
+    std::string::size_type pos = s.find_last_not_of(whiteSpace);
+    if (pos == std::string::npos) { // All blanks?!
+        s.clear();
+    } else {
+        if (++pos != s.size())
+            s.erase(pos, s.size() - pos);
+    }
+}
+
+void simplify(std::string &s)
+{
+    trimFront(s);
+    trimBack(s);
+    if (s.empty())
+        return;
+
+    // 1) All blanks
+    const std::string::size_type size1 = s.size();
+    std::string::size_type pos = 0;
+    for ( ; pos < size1; pos++)
+        if (std::isspace(s.at(pos)))
+                s[pos] = ' ';
+    // 2) Simplify
+    for (pos = 0; pos < s.size(); ) {
+        std::string::size_type blankpos = s.find(' ', pos);
+        if (blankpos == std::string::npos)
+            break;
+        std::string::size_type tokenpos = blankpos + 1;
+        while (tokenpos < s.size() && s.at(tokenpos) == ' ')
+            tokenpos++;
+        if (tokenpos - blankpos > 1)
+            s.erase(blankpos, tokenpos - blankpos - 1);
+        pos = blankpos + 1;
+    }
+}
+
+void replace(std::wstring &s, wchar_t before, wchar_t after)
+{
+    const std::wstring::size_type size = s.size();
+    for (std::wstring::size_type i = 0; i < size; i++)
+        if (s.at(i) == before)
+            s[i] = after;
+}
+
+static inline void formatGdbmiChar(std::ostream &str, wchar_t c)
+{
+    switch (c) {
+    case L'\n':
+        str << "\\n";
+        break;
+    case L'\t':
+        str << "\\t";
+        break;
+    case L'\r':
+        str << "\\r";
+        break;
+    case L'\\':
+    case L'"':
+        str << '\\' << char(c);
+        break;
+    default:
+        if (c < 128) {
+            str << char(c);
+        } else {
+            // Always pad up to 3 digits in case a digit follows
+            const char oldFill = str.fill('0');
+            str << '\\' << std::oct;
+            str.width(3);
+            str << unsigned(c) << std::dec;
+            str.fill(oldFill);
+        }
+        break;
+    }
+}
+
+// Stream  a wstring onto a char stream doing octal escaping
+// suitable for GDBMI.
+
+void gdbmiStringFormat::format(std::ostream &str) const
+{
+    const std::string::size_type size = m_s.size();
+    for (std::string::size_type i = 0; i < size; i++)
+        formatGdbmiChar(str, wchar_t(m_s.at(i)));
+}
+
+void gdbmiWStringFormat::format(std::ostream &str) const
+{
+    const std::wstring::size_type size = m_w.size();
+    for (std::wstring::size_type i = 0; i < size; i++)
+        formatGdbmiChar(str, m_w.at(i));
+}
+
+std::string wStringToGdbmiString(const std::wstring &w)
+{
+    std::ostringstream str;
+    str << gdbmiWStringFormat(w);
+    return str.str();
+}
+
+std::string wStringToString(const std::wstring &w)
+{
+    if (w.empty())
+        return std::string();
+    const std::string::size_type size = w.size();
+    std::string rc;
+    rc.reserve(size);
+    for (std::string::size_type i = 0; i < size; i++)
+        rc.push_back(char(w.at(i)));
+    return rc;
+}
+
+// Format a map as a GDBMI hash {key="value",..}
+void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &m)
+{
+    typedef std::map<std::string, std::string>::const_iterator It;
+    const It begin = m.begin();
+    const It cend = m.end();
+    os << '{';
+    for (It it = begin; it != cend; ++it) {
+        if (it != begin)
+            os << ',';
+        os << it->first << "=\"" << gdbmiStringFormat(it->second) << '"';
+    }
+    os << '}';
+}
diff --git a/src/libs/qtcreatorcdbext/stringutils.h b/src/libs/qtcreatorcdbext/stringutils.h
new file mode 100644
index 00000000000..cb809aeb918
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/stringutils.h
@@ -0,0 +1,122 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SPLIT_H
+#define SPLIT_H
+
+#include <string>
+#include <sstream>
+#include <map>
+
+void trimFront(std::string &s);
+void trimBack(std::string &s);
+void simplify(std::string &s);
+
+// Split by character separator.
+template <class Iterator>
+void split(const std::string &s, char sep, Iterator it)
+{
+    const std::string::size_type size = s.size();
+    for (std::string::size_type pos = 0; pos < size; ) {
+        std::string::size_type nextpos = s.find(sep, pos);
+        if (nextpos == std::string::npos)
+            nextpos = size;
+        const std::string token = s.substr(pos, nextpos - pos);
+        *it = token;
+        ++it;
+        pos = nextpos + 1;
+    }
+}
+
+// Format numbers, etc, as a string.
+template <class Streamable>
+std::string toString(const Streamable s)
+{
+    std::ostringstream str;
+    str << s;
+    return str.str();
+}
+
+// Read an integer from a string as '10' or '0xA'
+template <class Integer>
+bool integerFromString(const std::string &s, Integer *v)
+{
+    const bool isHex = s.compare(0, 2, "0x") == 0;
+    std::istringstream str(isHex ? s.substr(2, s.size() - 2) : s);
+    if (isHex)
+        str >> std::hex;
+    str >> *v;
+    return !str.fail();
+}
+
+void replace(std::wstring &s, wchar_t before, wchar_t after);
+
+// Stream  a string onto a char stream doing backslash & octal escaping
+// suitable for GDBMI usable as 'str << gdbmiStringFormat(wstring)'
+class gdbmiStringFormat {
+public:
+    explicit gdbmiStringFormat(const std::string &s) : m_s(s) {}
+
+    void format(std::ostream &) const;
+
+private:
+    const std::string &m_s;
+};
+
+// Stream  a wstring onto a char stream doing backslash & octal escaping
+// suitable for GDBMI usable as 'str << gdbmiWStringFormat(wstring)'
+class gdbmiWStringFormat {
+public:
+    explicit gdbmiWStringFormat(const std::wstring &w) : m_w(w) {}
+
+    void format(std::ostream &) const;
+
+private:
+    const std::wstring &m_w;
+};
+
+inline std::ostream &operator<<(std::ostream &str, const gdbmiStringFormat &sf)
+{
+    sf.format(str);
+    return str;
+}
+
+inline std::ostream &operator<<(std::ostream &str, const gdbmiWStringFormat &wsf)
+{
+    wsf.format(str);
+    return str;
+}
+
+std::string wStringToGdbmiString(const std::wstring &w);
+std::string wStringToString(const std::wstring &w);
+
+// Format a map as a GDBMI hash {key="value",..}
+void formatGdbmiHash(std::ostream &os, const std::map<std::string, std::string> &);
+
+#endif // SPLIT_H
diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp
new file mode 100644
index 00000000000..ba33d804d83
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp
@@ -0,0 +1,804 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "symbolgroup.h"
+#include "stringutils.h"
+#include "base64.h"
+
+#include <algorithm>
+#include <sstream>
+#include <list>
+#include <iterator>
+#include <set>
+
+enum { debug = 0 };
+
+typedef std::vector<int>::size_type VectorIndexType;
+
+const char rootNameC[] = "local";
+
+enum { BufSize = 2048 };
+
+std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS &parameters)
+{
+    str << "parent=";
+    if (parameters.ParentSymbol == DEBUG_ANY_ID) {
+        str << "DEBUG_ANY_ID";
+    } else {
+        str << parameters.ParentSymbol ;
+    }
+    str << " flags=" << parameters.Flags;
+    // Detailed flags:
+    if (parameters.Flags & DEBUG_SYMBOL_EXPANDED)
+        str << " EXPANDED";
+    if (parameters.Flags & DEBUG_SYMBOL_READ_ONLY)
+        str << " READONLY";
+    if (parameters.Flags & DEBUG_SYMBOL_IS_ARRAY)
+        str << " ARRAY";
+    if (parameters.Flags & DEBUG_SYMBOL_IS_FLOAT)
+        str << " FLOAT";
+    if (parameters.Flags & DEBUG_SYMBOL_IS_ARGUMENT)
+        str << " ARGUMENT";
+    if (parameters.Flags & DEBUG_SYMBOL_IS_LOCAL)
+        str << " LOCAL";
+    str << " typeId=" << parameters.TypeId << " subElements="
+              << parameters.SubElements;
+    return str;
+}
+
+SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
+                         const SymbolParameterVector &vec,
+                         ULONG threadId,
+                         unsigned frame) :
+    m_symbolGroup(sg),
+    m_threadId(threadId),
+    m_frame(frame),
+    m_root(SymbolGroupNode::create(sg, rootNameC, vec))
+{
+}
+
+SymbolGroup::~SymbolGroup()
+{
+    m_symbolGroup->Release();
+    delete m_root;
+}
+
+static inline bool getSymbolCount(CIDebugSymbolGroup *symbolGroup,
+                                  ULONG *count,
+                                  std::string *errorMessage)
+{
+    const HRESULT hr = symbolGroup->GetNumberSymbols(count);
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("GetNumberSymbols", hr);
+        return false;
+    }
+    return true;
+}
+
+bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup,
+                                      SymbolParameterVector *vec,
+                                      std::string *errorMessage)
+{
+    ULONG count;
+    return getSymbolCount(symbolGroup, &count, errorMessage)
+            && getSymbolParameters(symbolGroup, 0, count, vec, errorMessage);
+}
+
+bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup,
+                                      unsigned long start,
+                                      unsigned long count,
+                                      SymbolParameterVector *vec,
+                                      std::string *errorMessage)
+{
+    if (!count) {
+        vec->clear();
+        return true;
+    }
+    // Trim the count to the maximum count available. When expanding elements
+    // and passing SubElements as count, SubElements might be an estimate that
+    // is too large and triggers errors.
+    ULONG totalCount;
+    if (!getSymbolCount(symbolGroup, &totalCount, errorMessage))
+        return false;
+    if (start >= totalCount) {
+        std::ostringstream str;
+        str << "SymbolGroup::getSymbolParameters: Start parameter "
+            << start << " beyond total " << totalCount << '.';
+        *errorMessage = str.str();
+        return  false;
+    }
+    if (start + count > totalCount)
+        count = totalCount - start;
+    // Get parameters.
+    vec->resize(count);
+    const HRESULT hr = symbolGroup->GetSymbolParameters(start, count, &(*vec->begin()));
+    if (FAILED(hr)) {
+        std::ostringstream str;
+        str << "SymbolGroup::getSymbolParameters failed for index=" << start << ", count=" << count
+            << ": " << msgDebugEngineComFailed("GetSymbolParameters", hr);
+        *errorMessage = str.str();
+        return false;
+    }
+    return true;
+}
+
+SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugSymbols,
+                                 ULONG threadId, unsigned frame,
+                                 std::string *errorMessage)
+{
+    errorMessage->clear();
+
+    ULONG obtainedFrameCount = 0;
+    const ULONG frameCount = frame + 1;
+
+    DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[frameCount];
+    IDebugSymbolGroup2 *idebugSymbols = 0;
+    bool success = false;
+    SymbolParameterVector parameters;
+
+    // Obtain symbol group at stack frame.
+    do {
+        HRESULT hr = control->GetStackTrace(0, 0, 0, frames, frameCount, &obtainedFrameCount);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
+            break;
+        }
+        if (obtainedFrameCount < frameCount ) {
+            std::ostringstream str;
+            str << "Unable to obtain frame " << frame << " (" << obtainedFrameCount << ").";
+            *errorMessage = str.str();
+            break;
+        }
+        hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr);
+            break;
+        }
+        hr = debugSymbols->SetScope(0, frames + frame, NULL, 0);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("SetScope", hr);
+            break;
+        }
+        // refresh with current frame
+        hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, idebugSymbols, &idebugSymbols);
+        if (FAILED(hr)) {
+            *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr);
+            break;
+        }
+        if (!SymbolGroup::getSymbolParameters(idebugSymbols, &parameters, errorMessage))
+            break;
+
+        success = true;
+    } while (false);
+    delete [] frames;
+    if (!success) {
+        if (idebugSymbols)
+            idebugSymbols->Release();
+        return 0;
+    }
+    return new SymbolGroup(idebugSymbols, parameters, threadId, frame);
+}
+
+// ------- SymbolGroupNode
+SymbolGroupNode::SymbolGroupNode(CIDebugSymbolGroup *symbolGroup,
+                                 const std::string &n,
+                                 SymbolGroupNode *parent) :
+    m_symbolGroup(symbolGroup), m_parent(parent), m_name(n)
+{
+    memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
+    m_parameters.ParentSymbol = DEBUG_ANY_ID;
+}
+
+void SymbolGroupNode::removeChildren()
+{
+    if (!m_children.empty()) {
+        const SymbolGroupNodePtrVectorIterator end = m_children.end();
+        for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it)
+            delete *it;
+        m_children.clear();
+    }
+}
+
+bool SymbolGroupNode::isArrayElement() const
+{
+    return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY);
+}
+
+// iName: Fix array elements to be named 'array.0' instead of 'array.[0]' so
+// that sorting in Qt Creator works.
+std::string SymbolGroupNode::iName() const
+{
+    std::string rc = m_name;
+
+    //rc += isArrayElement() ? 'a' : 'n';
+    if (isArrayElement() && !rc.empty() && rc.at(0) == '[') {
+        const std::string::size_type last = rc.size() - 1;
+        if (rc.at(last) == ']') {
+            rc.erase(last, 1);
+            rc.erase(0, 1);
+        }
+    }
+    return rc;
+}
+
+// Return full iname as 'locals.this.m_sth'.
+std::string SymbolGroupNode::fullIName() const
+{
+    std::string rc = iName();
+    for (const SymbolGroupNode *p = parent(); p; p = p->parent()) {
+        rc.insert(0, 1, '.');
+        rc.insert(0, p->iName());
+    }
+    return rc;
+}
+
+// Index: Index of symbol, parameterOffset: Looking only at a part of the symbol array, offset
+void SymbolGroupNode::parseParameters(VectorIndexType index,
+                                      VectorIndexType parameterOffset,
+                                      const SymbolGroup::SymbolParameterVector &vec)
+{
+    static char buf[BufSize];
+    ULONG obtainedSize;
+
+    const bool isTopLevel = index == DEBUG_ANY_ID;
+    if (isTopLevel) {
+        m_parameters.Flags |= DEBUG_SYMBOL_EXPANDED;
+    } else {
+        m_parameters = vec.at(index - parameterOffset);
+        if (m_parameters.SubElements == 0 || !(m_parameters.Flags & DEBUG_SYMBOL_EXPANDED))
+            return; // No children
+    }
+    if (m_parameters.SubElements > 1)
+        m_children.reserve(m_parameters.SubElements);
+
+    const VectorIndexType size = vec.size();
+    // Scan the top level elements
+    const VectorIndexType startIndex = isTopLevel ? 0 : index + 1;
+    for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) {
+        if (vec.at(pos).ParentSymbol == index) {
+            const VectorIndexType symbolGroupIndex = pos + parameterOffset;
+            HRESULT hr = m_symbolGroup->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize);
+            const std::string name = SUCCEEDED(hr) ? std::string(buf) : std::string("unnamed");
+            SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, name, this);
+            child->parseParameters(symbolGroupIndex, parameterOffset, vec);
+            m_children.push_back(child);
+        }
+    }
+    if (isTopLevel)
+        m_parameters.SubElements = ULONG(m_children.size());
+}
+
+SymbolGroupNode *SymbolGroupNode::create(CIDebugSymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec)
+{
+    SymbolGroupNode *rc = new SymbolGroupNode(sg, name);
+    rc->parseParameters(DEBUG_ANY_ID, 0, vec);
+    return rc;
+}
+
+// Fix some oddities in CDB values
+static void fixValue(const std::string &type, std::wstring *value)
+{
+    // Pointers: fix '0x00000000`00000AD class bla' ... to "0xAD", but leave
+    // 'const char *' values as is.
+    if (!type.empty() && type.at(type.size() - 1) == L'*' && value->compare(0, 2, L"0x") == 0) {
+        // Remove dumb 64bit separator
+        if (value->size() > 10 && value->at(10) == L'`')
+            value->erase(10, 1);
+        const std::string::size_type firstNonNullDigit = value->find_first_not_of(L"0", 2);
+        // No on-null digits: plain null ptr.
+        if (firstNonNullDigit == std::string::npos || value->at(firstNonNullDigit) == ' ') {
+            *value = L"0x0";
+            return;
+        }
+        // Strip
+        if (firstNonNullDigit > 2)
+            value->erase(2, firstNonNullDigit - 2);
+        // Strip ' Class bla"
+        std::wstring::size_type classPos = value->find(L" struct", 2);
+        if (classPos == std::string::npos)
+            classPos = value->find(L" class", 2);
+        if (classPos != std::string::npos)
+            value->erase(classPos, value->size() - classPos);
+        return;
+    }
+    // Integers: fix '0n10' -> '10'
+    if (value->size() >= 3 && value->compare(0, 2, L"0n") == 0
+        && (isdigit(value->at(2)) || value->at(2) == L'-')) {
+        value->erase(0, 2);
+        return;
+    }
+}
+
+// Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline.
+static bool isSevenBitClean(const wchar_t *buf, ULONG size)
+{
+    const wchar_t *bufEnd = buf + size;
+    for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) {
+        const wchar_t c = *bufPtr;
+        if (c > 127 || (c < 32 && c != 9))
+            return false;
+    }
+    return true;
+}
+
+std::string SymbolGroupNode::getType(ULONG index) const
+{
+    static char buf[BufSize];
+    const HRESULT hr = m_symbolGroup->GetSymbolTypeName(index, buf, BufSize, NULL);
+    return SUCCEEDED(hr) ? std::string(buf) : std::string();
+}
+
+wchar_t *SymbolGroupNode::getValue(ULONG index,
+                                   ULONG *obtainedSizeIn /* = 0 */) const
+{
+    // Determine size and return allocated buffer
+    if (obtainedSizeIn)
+        *obtainedSizeIn = 0;
+    const ULONG maxValueSize = 262144;
+    ULONG obtainedSize = 0;
+    HRESULT hr = m_symbolGroup->GetSymbolValueTextWide(index, NULL, maxValueSize, &obtainedSize);
+    if (FAILED(hr))
+        return 0;
+    if (obtainedSize > maxValueSize)
+        obtainedSize = maxValueSize;
+    wchar_t *buffer = new wchar_t[obtainedSize];
+    hr = m_symbolGroup->GetSymbolValueTextWide(index, buffer, obtainedSize, &obtainedSize);
+    if (FAILED(hr)) { // Whoops, should not happen
+        delete [] buffer;
+        return 0;
+    }
+    if (obtainedSizeIn)
+        *obtainedSizeIn = obtainedSize;
+    return buffer;
+}
+
+ULONG64 SymbolGroupNode::address(ULONG index) const
+{
+    ULONG64 address = 0;
+    const HRESULT hr = m_symbolGroup->GetSymbolOffset(index, &address);
+    if (SUCCEEDED(hr))
+        return address;
+    return 0;
+}
+
+std::wstring SymbolGroupNode::rawValue(ULONG index) const
+{
+    std::wstring rc;
+    if (const wchar_t *wbuf = getValue(index)) {
+        rc = wbuf;
+        delete[] wbuf;
+    }
+    return rc;
+}
+
+std::wstring SymbolGroupNode::fixedValue(ULONG index) const
+{
+    std::wstring value = rawValue(index);
+    fixValue(getType(index), &value);
+    return value;
+}
+
+SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const
+{
+    return i < m_children.size() ? m_children.at(i) : static_cast<SymbolGroupNode *>(0);
+}
+
+static inline void indentStream(std::ostream &str, unsigned depth)
+{
+    for (unsigned d = 0; d < depth; d++)
+        str << "  ";
+}
+
+void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
+                           bool humanReadable, ULONG &index) const
+{
+    const std::string iname = fullIName();
+    const std::string type = getType(index);
+
+    if (child) { // Separate list of children
+        str << ',';
+        if (humanReadable)
+            str << '\n';
+    }
+
+    if (humanReadable)
+        indentStream(str, depth);
+    str << "{iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name
+        << "\",type=\"" << type << '"';
+
+    if (const ULONG64 addr = address(index))
+        str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec
+               << '"';
+
+    ULONG obtainedSize = 0;
+    if (const wchar_t *wbuf = getValue(index, &obtainedSize)) {
+        const ULONG valueSize = obtainedSize - 1;
+        // ASCII or base64?
+        if (isSevenBitClean(wbuf, valueSize)) {
+            std::wstring value = wbuf;
+            fixValue(type, &value);
+            str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
+        } else {
+            str << ",valueencoded=\"2\",value=\"";
+            base64Encode(str, reinterpret_cast<const unsigned char *>(wbuf), valueSize * sizeof(wchar_t));
+            str << '"';
+        }
+        delete [] wbuf;
+    }
+    // Children: Dump all known or subelements (guess).
+    const VectorIndexType childCountGuess = m_children.empty() ? m_parameters.SubElements : m_children.size();
+    // No children..suppose we are editable and enabled
+    if (childCountGuess == 0)
+        str << ",valueenabled=\"true\",valueeditable=\"true\"";
+    str << ",numchild=\"" << childCountGuess << '"';
+    if (!m_children.empty()) {
+        str << ",children=[";
+        if (humanReadable)
+            str << '\n';
+    }
+}
+
+void SymbolGroupNode::dumpChildrenVisited(std::ostream &str, bool humanReadable) const
+{
+    if (!m_children.empty())
+        str << ']';
+    str << '}';
+    if (humanReadable)
+        str << '\n';
+
+}
+bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth, ULONG &index) const
+{
+    // If we happen to be the root node, just skip over
+
+    const bool invisibleRoot = index == DEBUG_ANY_ID;
+    const unsigned childDepth = invisibleRoot ? 0 : depth + 1;
+
+    if (invisibleRoot) {
+        index = 0;
+    } else {
+        // Visit us and move index forward.
+        if (visitor.visit(this, child, depth, index))
+            return true;
+        index++;
+    }
+
+    const unsigned childCount = unsigned(m_children.size());
+    for (unsigned c = 0; c < childCount; c++)
+        if (m_children.at(c)->accept(visitor, c, childDepth, index))
+            return true;
+    if (!invisibleRoot)
+        visitor.childrenVisited(this, depth);
+    return false;
+}
+
+void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned depth, ULONG index) const
+{
+    indentStream(str, depth);
+    str << '"' << m_name << "\" Children=" << m_children.size() << ' ' << m_parameters;
+    if (verbosity)
+        str << " Address=0x" << std::hex << address(index) << std::dec << " Type=\"" << getType(index) << "\" Value=\"" << gdbmiWStringFormat(rawValue(index)) << '"';
+    str << '\n';
+}
+
+// Index offset when stepping past this node in a symbol parameter array. Basically
+// self + recursive all child counts.
+ULONG SymbolGroupNode::recursiveIndexOffset() const
+{
+    ULONG rc = 1u;
+    if (!m_children.empty()) {
+        const SymbolGroupNodePtrVectorConstIterator cend = m_children.end();
+        for (SymbolGroupNodePtrVectorConstIterator it = m_children.begin(); it != cend; ++it)
+            rc += (*it)->recursiveIndexOffset();
+    }
+    return rc;
+}
+
+// Expand!
+bool SymbolGroupNode::expand(ULONG index, std::string *errorMessage)
+{
+    if (::debug > 1)
+        DebugPrint() << "SymbolGroupNode::expand "  << m_name << ' ' << index;
+    if (!m_children.empty())
+        return true;
+    if (m_parameters.SubElements == 0) {
+        *errorMessage = "No subelements to expand in node: " + fullIName();
+        return false;
+    }
+
+    const HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
+
+    if (FAILED(hr)) {
+        *errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr);
+        return false;
+    }
+    SymbolGroup::SymbolParameterVector parameters;
+    // Retrieve parameters (including self, re-retrieve symbol parameters to get new 'expanded' flag
+    // and corrected SubElement count (might be estimate)) and create child nodes.
+    if (!SymbolGroup::getSymbolParameters(m_symbolGroup,
+                                          index, m_parameters.SubElements + 1,
+                                          &parameters, errorMessage))
+        return false;
+    parseParameters(index, index, parameters);
+    return true;
+}
+
+static inline std::string msgNotFound(const std::string &nodeName)
+{
+    std::ostringstream str;
+    str << "Node '" << nodeName << "' not found.";
+    return str.str();
+}
+
+std::string SymbolGroup::dump(bool humanReadable) const
+{
+    std::ostringstream str;
+    DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
+    if (humanReadable)
+        str << '\n';
+    str << '[';
+    accept(visitor);
+    str << ']';
+    return str.str();
+}
+
+// Dump a node, potentially expand
+std::string SymbolGroup::dump(const std::string &name, bool humanReadable, std::string *errorMessage)
+{
+    ULONG index;
+    SymbolGroupNode *const node = find(name, &index);
+    if (node == 0) {
+        *errorMessage = msgNotFound(name);
+        return std::string();
+    }
+    if (node->subElements() && node->children().empty()) {
+        if (!expand(name, errorMessage))
+            return false;
+    }
+    std::ostringstream str;
+    if (humanReadable)
+        str << '\n';
+    DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
+    str << '[';
+    node->accept(visitor, 0, 0, index);
+    str << ']';
+    return str.str();
+}
+
+std::string SymbolGroup::debug(unsigned verbosity) const
+{
+    std::ostringstream str;
+    DebugSymbolGroupNodeVisitor visitor(str, verbosity);
+    accept(visitor);
+    return str.str();
+}
+
+/* expandList: Expand a list of inames with a 'mkdir -p'-like behaviour, that is,
+ * expand all sub-paths. The list of inames has thus to be reordered to expand the
+ * parent items first, for example "locals.this.i1.data,locals.this.i2" --->:
+ * "locals, locals.this, locals.this.i1, locals.this.i2, locals.this.i1.data".
+ * This is done here by creating a set of name parts keyed by level and name
+ * (thus purging duplicates). */
+
+typedef std::pair<unsigned, std::string> InamePathEntry;
+
+struct InamePathEntryLessThan : public std::binary_function<InamePathEntry, InamePathEntry, bool> {
+    bool operator()(const InamePathEntry &i1, const InamePathEntry& i2) const
+    {
+        if (i1.first < i2.first)
+            return true;
+        if (i1.first != i2.first)
+            return false;
+        return i1.second < i2.second;
+    }
+};
+
+typedef std::set<InamePathEntry, InamePathEntryLessThan> InamePathEntrySet;
+
+// Expand a node list "locals.i1,locals.i2"
+unsigned SymbolGroup::expandList(const std::vector<std::string> &nodes, std::string *errorMessage)
+{
+    if (nodes.empty())
+        return 0;
+    // Create a set with a key <level, name>. Also required for 1 node.
+    InamePathEntrySet pathEntries;
+    const VectorIndexType nodeCount = nodes.size();
+    for (VectorIndexType i= 0; i < nodeCount; i++) {
+        const std::string &iname = nodes.at(i);
+        std::string::size_type pos = 0;
+        for (unsigned level = 0; pos < iname.size(); level++) {
+            std::string::size_type dotPos = iname.find('.', pos);
+            if (dotPos == std::string::npos)
+                dotPos = iname.size();
+            pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos)));
+            pos = dotPos + 1;
+        }
+    }
+    // Now expand going by level.
+    unsigned succeeded = 0;
+    std::string nodeError;
+    InamePathEntrySet::const_iterator cend = pathEntries.end();
+    for (InamePathEntrySet::const_iterator it = pathEntries.begin(); it != cend; ++it)
+        if (expand(it->second, &nodeError)) {
+            succeeded++;
+        } else {
+            if (!errorMessage->empty())
+                errorMessage->append(", ");
+            errorMessage->append(nodeError);
+        }
+    return succeeded;
+}
+
+bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage)
+{
+    ULONG index = DEBUG_ANY_ID;
+    SymbolGroupNode *node = find(nodeName, &index);
+    if (::debug)
+        DebugPrint() << "expand: " << nodeName << " found=" << (node != 0) << " index= " << index << '\n';
+    if (!node) {
+        *errorMessage = msgNotFound(nodeName);
+        return false;
+    }
+    if (node == m_root) // Shouldn't happen, still, all happy
+        return true;
+    return node->expand(index, errorMessage);
+}
+
+static inline std::string msgAssignError(const std::string &nodeName,
+                                         const std::string &value,
+                                         const std::string &why)
+{
+    std::ostringstream str;
+    str << "Unable to assign '" << value << "' to '" << nodeName << "': " << why;
+    return str.str();
+}
+
+bool SymbolGroup::assign(const std::string &nodeName, const std::string &value,
+                         std::string *errorMessage)
+{
+    ULONG index;
+    SymbolGroupNode *node = find(nodeName, &index);
+    if (node == 0) {
+        *errorMessage = msgAssignError(nodeName, value, "No such node");
+        return false;
+    }
+    const HRESULT hr = m_symbolGroup->WriteSymbol(index, const_cast<char *>(value.c_str()));
+    if (FAILED(hr)) {
+        *errorMessage = msgAssignError(nodeName, value, msgDebugEngineComFailed("WriteSymbol", hr));
+        return false;
+    }
+    return true;
+}
+
+bool SymbolGroup::accept(SymbolGroupNodeVisitor &visitor) const
+{
+    if (!m_root || m_root->children().empty())
+        return false;
+    ULONG index = DEBUG_ANY_ID;
+    return m_root->accept(visitor, 0, 0, index);
+}
+
+// Find  "locals.this.i1" and move index recursively
+static SymbolGroupNode *findNodeRecursion(const std::vector<std::string> &iname,
+                                          unsigned depth,
+                                          std::vector<SymbolGroupNode *> nodes,
+                                          ULONG *index = 0)
+{
+    typedef std::vector<SymbolGroupNode *>::const_iterator ConstIt;
+
+    if (::debug > 1)
+        DebugPrint() << "findNodeRecursion " << iname.size() << '/'
+                     << iname.back() << " depth="  << depth
+                     << " nodes=" << nodes.size() << " index=" << (index ? *index : ULONG(0));
+
+    if (nodes.empty())
+        return 0;
+    // Find the child that matches the iname part at depth
+    const ConstIt cend = nodes.end();
+    for (ConstIt it = nodes.begin(); it != cend; ++it) {
+        SymbolGroupNode *c = *it;
+        if (c->name() == iname.at(depth)) {
+            if (depth == iname.size() - 1) { // Complete iname matched->happy.
+                return c;
+            } else {
+                // Sub-part of iname matched. Forward index and check children.
+                if (index)
+                    (*index)++; // Skip ourselves
+                return findNodeRecursion(iname, depth + 1, c->children(), index);
+            }
+        } else {
+            if (index) // No match for this child, forward the index past all expanded children
+                *index += c->recursiveIndexOffset();
+        }
+    }
+    return 0;
+}
+
+SymbolGroupNode *SymbolGroup::findI(const std::string &iname, ULONG *index) const
+{
+    if (index)
+        *index = DEBUG_ANY_ID;
+
+    if (iname.empty())
+        return 0;
+    // Match the root element only: Shouldn't happen, still, all happy
+    if (iname == m_root->name())
+        return m_root;
+
+    std::vector<std::string> inameTokens;
+    split(iname, '.', std::back_inserter(inameTokens));
+
+    // Must begin with root
+    if (inameTokens.front() != m_root->name())
+        return 0;
+
+    // Start with index = 0 at root's children
+    if (index)
+        *index = 0;
+    return findNodeRecursion(inameTokens, 1, m_root->children(), index);
+}
+
+SymbolGroupNode *SymbolGroup::find(const std::string &iname, ULONG *index) const
+{
+    SymbolGroupNode *rc = findI(iname, index);
+    if (::debug > 1)
+        DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc << ' ' << (index ? *index : ULONG(0));
+    return rc;
+}
+
+// --------- DebugSymbolGroupNodeVisitor
+DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity) :
+    m_os(os), m_verbosity(verbosity)
+{
+}
+
+bool DebugSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node,
+                                        unsigned /* child */, unsigned depth, ULONG index)
+{
+    node->debug(m_os, m_verbosity, depth, index);
+    return false;
+}
+
+// --------------------- DumpSymbolGroupNodeVisitor
+DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
+                                                       bool humanReadable) :
+    m_os(os), m_humanReadable(humanReadable)
+{
+}
+
+bool DumpSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node, unsigned child, unsigned depth, ULONG index)
+{
+    node->dump(m_os, child, depth, m_humanReadable, index);
+    return false;
+}
+
+void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *node, unsigned)
+{
+    node->dumpChildrenVisited(m_os, m_humanReadable);
+}
diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h
new file mode 100644
index 00000000000..de9843ea3df
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/symbolgroup.h
@@ -0,0 +1,224 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SYMBOLGROUP_H
+#define SYMBOLGROUP_H
+
+#include "common.h"
+
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
+
+class SymbolGroupNodeVisitor;
+
+// Thin wrapper around a symbol group entry.
+class SymbolGroupNode {
+    SymbolGroupNode(const SymbolGroupNode&);
+    SymbolGroupNode& operator=(const SymbolGroupNode&);
+public:
+    typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
+    typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
+    typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
+    typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator;
+
+    explicit SymbolGroupNode(CIDebugSymbolGroup *symbolGroup,
+                             const std::string &name,
+                             SymbolGroupNode *parent = 0);
+
+    ~SymbolGroupNode() { removeChildren(); }
+
+    void removeChildren();
+    void parseParameters(SymbolParameterVector::size_type index,
+                         SymbolParameterVector::size_type parameterOffset,
+                         const SymbolParameterVector &vec);
+
+    static SymbolGroupNode *create(CIDebugSymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
+
+    const std::string &name() const { return m_name; }
+    std::string fullIName() const;
+    std::string iName() const;
+
+    const SymbolGroupNodePtrVector &children() const { return m_children; }
+    SymbolGroupNode *childAt(unsigned) const;
+    const SymbolGroupNode *parent() const { return m_parent; }
+
+    // I/O: Gdbmi dump for Visitors
+    void dump(std::ostream &str, unsigned child, unsigned depth,
+              bool humanReadable, ULONG &index) const;
+    void dumpChildrenVisited(std::ostream &str, bool humanReadable) const;
+    // I/O: debug for Visitors
+    void debug(std::ostream &os, unsigned verbosity, unsigned depth, ULONG index) const;
+
+    std::wstring rawValue(ULONG index) const;
+    std::wstring fixedValue(ULONG index) const;
+    ULONG64 address(ULONG index) const;
+
+    bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth, ULONG &index) const;
+
+    // Skip indexes of all children
+    ULONG recursiveIndexOffset() const;
+
+    bool expand(ULONG index, std::string *errorMessage);
+
+    ULONG subElements() const { return m_parameters.SubElements; }
+
+private:
+    // Return allocated wide string array of value
+    wchar_t *getValue(ULONG index, ULONG *obtainedSize = 0) const;
+    std::string getType(ULONG index) const;
+    bool isArrayElement() const;
+
+    CIDebugSymbolGroup *const m_symbolGroup;
+    SymbolGroupNode *m_parent;
+    DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct.
+    SymbolGroupNodePtrVector m_children;
+    const std::string m_name;
+};
+
+/* Visitor that takes care of iterating over the nodes and the index bookkeeping.
+ * visit() is not called for the (invisible) root node, but starting with the
+ * root's children with depth=0.
+ * Return true from visit() to terminate the recursion. */
+
+class SymbolGroupNodeVisitor {
+    SymbolGroupNodeVisitor(const SymbolGroupNodeVisitor&);
+    SymbolGroupNodeVisitor& operator=(const SymbolGroupNodeVisitor&);
+
+    friend class SymbolGroupNode;
+protected:
+    SymbolGroupNodeVisitor() {}
+public:
+    virtual ~SymbolGroupNodeVisitor() {}
+
+private:
+    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth, ULONG index) = 0;
+    // Helper for formatting output.
+    virtual void childrenVisited(const SymbolGroupNode * /* node */, unsigned /* depth */) {}
+};
+
+// Thin wrapper around a symbol group storing a tree of expanded symbols rooted on
+// a fake "locals" root element.
+// Provides a find() method based on inames ("locals.this.i1.data") that retrieves
+// that index based on the current expansion state.
+
+class SymbolGroup {
+public:
+    typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
+
+private:
+    explicit SymbolGroup(CIDebugSymbolGroup *,
+                         const SymbolParameterVector &vec,
+                         ULONG threadId, unsigned frame);
+
+    SymbolGroup(const SymbolGroup &);
+    SymbolGroup &operator=(const SymbolGroup &);
+
+public:
+    typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector;
+
+    static SymbolGroup *create(CIDebugControl *control,
+                               CIDebugSymbols *,
+                               ULONG threadId,
+                               unsigned frame,
+                               std::string *errorMessage);
+    ~SymbolGroup();
+
+    // Dump all
+    std::string dump(bool humanReadable = false) const;
+    // Expand node and dump
+    std::string dump(const std::string &name, bool humanReadable, std::string *errorMessage);
+    std::string debug(unsigned verbosity = 0) const;
+
+    unsigned frame() const { return m_frame; }
+    ULONG threadId() const { return m_threadId; }
+    const SymbolGroupNode *root() { return m_root; }
+
+    // Expand a node list "locals.i1,locals.i2", expanding all nested child nodes
+    // (think mkdir -p).
+    unsigned expandList(const std::vector<std::string> &nodes, std::string *errorMessage);
+
+    // Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible
+    // (think mkdir without -p).
+    bool expand(const std::string &node, std::string *errorMessage);
+
+    bool accept(SymbolGroupNodeVisitor &visitor) const;
+
+    // Assign a value by iname
+    bool assign(const std::string &node,
+                const std::string &value,
+                std::string *errorMessage);
+
+    static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup,
+                                    unsigned long start,
+                                    unsigned long count,
+                                    SymbolParameterVector *vec,
+                                    std::string *errorMessage);
+
+private:
+    SymbolGroupNode *find(const std::string &iname, ULONG *index = 0) const;
+    inline SymbolGroupNode *findI(const std::string &iname, ULONG *index = 0) const;
+    static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup,
+                                    SymbolParameterVector *vec,
+                                    std::string *errorMessage);
+
+    CIDebugSymbolGroup * const m_symbolGroup;
+    const unsigned m_frame;
+    const ULONG m_threadId;
+    SymbolGroupNode *const m_root;
+};
+
+// Debug output visitor.
+class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
+public:
+    explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
+
+private:
+    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth, ULONG index);
+
+    std::ostream &m_os;
+    const unsigned m_verbosity;
+};
+
+// Gdbmi dump output visitor.
+class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
+public:
+    explicit DumpSymbolGroupNodeVisitor(std::ostream &os, bool humanReadable);
+
+private:
+    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth, ULONG index);
+    virtual void childrenVisited(const SymbolGroupNode *  node, unsigned depth);
+
+    std::ostream &m_os;
+    const bool m_humanReadable;
+};
+
+#endif // SYMBOLGROUP_H
diff --git a/src/libs/qtcreatorcdbext/test32.bat b/src/libs/qtcreatorcdbext/test32.bat
new file mode 100644
index 00000000000..00693f786df
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/test32.bat
@@ -0,0 +1,24 @@
+@echo off
+
+REM test.bat: Test script to launch CDB.exe using the extension
+REM with the tests/manual demo project.
+
+REM !qtcreatorcdbext.help
+REM !qtcreatorcdbext.assign local.this.m_w=44
+REM !qtcreatorcdbext.locals 0
+
+set ROOT=d:\dev\qt4.7-vs8\creator
+
+set _NT_DEBUGGER_EXTENSION_PATH=%ROOT%\lib\qtcreatorcdbext32
+set EXT=qtcreatorcdbext.dll
+set EXE=%ROOT%\tests\manual\gdbdebugger\gui\debug\gui.exe
+
+set CDB=D:\Programme\Debugging Tools for Windows (x86)\cdb.exe
+
+echo %CDB%
+echo %EXT% %_NT_DEBUGGER_EXTENSION_PATH%
+
+echo "!qtcreatorcdbext.pid"
+
+REM Launch emulating cdbengine's setup with idle reporting
+"%CDB%" -G -a%EXT% -c ".idle_cmd !qtcreatorcdbext.idle" %EXE%
diff --git a/src/libs/qtcreatorcdbext/test64.bat b/src/libs/qtcreatorcdbext/test64.bat
new file mode 100644
index 00000000000..0185785f178
--- /dev/null
+++ b/src/libs/qtcreatorcdbext/test64.bat
@@ -0,0 +1,23 @@
+@echo off
+
+REM test.bat: Test script to launch CDB.exe using the extension
+REM with the tests/manual demo project.
+
+REM !qtcreatorcdbext.help
+REM !qtcreatorcdbext.assign local.this.m_w=44
+REM !qtcreatorcdbext.locals 0
+
+set ROOT=c:\qt\4.7-vs8\creator
+
+set _NT_DEBUGGER_EXTENSION_PATH=%ROOT%\lib\qtcreatorcdbext64
+set EXT=qtcreatorcdbext.dll
+set EXE=%ROOT%\tests\manual\gdbdebugger\gui\debug\gui.exe
+
+set CDB=C:\PROGRA~1\DEBUGG~1\cdb.exe
+
+echo %CDB%
+
+echo "!qtcreatorcdbext.pid"
+
+REM Launch emulating cdbengine's setup with idle reporting
+%CDB% -G -a%EXT% -c ".idle_cmd ^!qtcreatorcdbext.idle" %EXE%
-- 
GitLab