diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
index 2f84541610a3a67142421dedf1ad11c66d18eb80..6bd14850ed66017efdb447ca395eb43e03d8b5ba 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def
@@ -6,6 +6,8 @@ pid
 expandlocals
 locals
 dumplocal
+typecast
+addsymbol
 assign
 threads
 registers
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
index 32ed01315b1e5765f90aa59fc8834ae2e37387d2..eea8a86e047532ced34ab38000c88083fba35b53 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
@@ -49,14 +49,149 @@
  * - 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 */
+ */
+
+// Data struct and helpers for formatting help
+struct CommandDescription {
+    const char *name;
+    const char *description;
+    const char *usage;
+};
+
+// Single line of usage: For reporting usage errors back as a single line
+static std::string singleLineUsage(const CommandDescription &d)
+{
+    std::string rc = "Usage: ";
+    const char *endOfLine = strchr(d.usage, '\n');
+    rc += endOfLine ? std::string(d.usage, endOfLine -  d.usage) : std::string(d.usage);
+    return rc;
+}
+
+// Format description of a command
+std::ostream &operator<<(std::ostream &str, const CommandDescription &d)
+{
+    str << "Command '" << d.name << "': " << d.description << '\n';
+    if (d.usage[0])
+        str << "Usage: " << d.name << ' ' << d.usage << '\n';
+    str << '\n';
+    return str;
+}
+
+enum Command {
+    CmdPid,
+    CmdExpandlocals,
+    CmdLocals,
+    CmdDumplocal,
+    CmdTypecast,
+    CmdAddsymbol,
+    CmdAssign,
+    CmdThreads,
+    CmdRegisters,
+    CmdModules,
+    CmdIdle,
+    CmdHelp,
+    CmdMemory,
+    CmdStack,
+    CmdShutdownex
+};
+
+static const CommandDescription commandDescriptions[] = {
+{"pid",
+ "Prints inferior process id and hooks up output callbacks.",
+ "[-t token]"},
+{"expandlocals", "Expands local variables by iname in symbol group.",
+ "[-t token] <frame-number> <iname1-list>\n"
+ "iname1-list: Comma-separated list of inames"},
+{"locals",
+ "Prints local variables of symbol group in GDBMI or debug format",
+ "[-t token] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
+ "-h human-readable ouput\n"
+ "-d debug output\n"
+ "-e expand-list        Comma-separated list of inames to be expanded beforehand\n"
+ "-u uninitialized-list Comma-separated list of uninitialized inames"},
+{"dumplocal", "Dumps local variable using simple dumpers (testing command).",
+ "[-t token] <frame-number> <iname>"},
+{"typecast","Performs a type cast on an unexpanded iname of symbol group.",
+ "[-t token] <frame-number> <iname> <desired-type>"},
+{"addsymbol","Adds a symbol to symbol group (testing command).",
+ "[-t token] <frame-number> <name-expression> [optional-iname]"},
+{"assign","Assigns a value to a variable in current symbol group.",
+ "[-t token] <iname=value>"},
+{"threads","Lists threads in GDBMI format.","[-t token]"},
+{"registers","Lists registers in GDBMI format","[-t token]"},
+{"modules","Lists modules in GDBMI format.","[-t token]"},
+{"idle",
+ "Reports stop reason in GDBMI format.\n"
+ "Intended to be used with .idle_cmd to obtain proper stop notification.",""},
+{"help","Prints help.",""},
+{"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"},
+{"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"},
+{"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""}
+};
 
 typedef std::vector<std::string> StringVector;
 typedef std::list<std::string> StringList;
 
+// Helper for commandTokens() below:
+// Simple splitting of command lines allowing for '"'-quoted tokens
+// 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *')
+template<class Inserter>
+static inline void splitCommand(PCSTR args, Inserter it)
+{
+    enum State { WhiteSpace, WithinToken, WithinQuoted };
+
+    State state = WhiteSpace;
+    std::string current;
+    for (PCSTR p = args; *p; p++) {
+        char c = *p;
+        switch (state) {
+        case WhiteSpace:
+            switch (c) {
+            case ' ':
+                break;
+            case '"':
+                state = WithinQuoted;
+                current.clear();
+                break;
+            default:
+                state = WithinToken;
+                current.clear();
+                current.push_back(c);
+                break;
+            }
+            break;
+        case WithinToken:
+            switch (c) {
+            case ' ':
+                state = WhiteSpace;
+                *it = current;
+                ++it;
+                break;
+            default:
+                current.push_back(c);
+                break;
+            }
+            break;
+        case WithinQuoted:
+            switch (c) {
+            case '"':
+                state = WhiteSpace;
+                *it = current;
+                ++it;
+                break;
+            default:
+                current.push_back(c);
+                break;
+            }
+            break;
+        }
+    }
+    if (state == WithinToken) {
+        *it = current;
+        ++it;
+    }
+}
+
 // 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
@@ -69,11 +204,8 @@ static inline StringContainer commandTokens(PCSTR args, int *token = 0)
 
     if (token)
         *token = -1; // Handled as 'display' in engine, so that user can type commands
-    std::string cmd(args);
-    simplify(cmd);
     StringContainer tokens;
-    split(cmd, ' ', std::back_inserter(tokens));
-
+    splitCommand(args, std::back_inserter(tokens));
     // Check for token
     ContainerIterator it = tokens.begin();
     if (it != tokens.end() && *it == "-t" && ++it != tokens.end()) {
@@ -114,13 +246,11 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
     int token;
     const StringVector tokens = commandTokens<StringVector>(args, &token);
     StringVector inames;
-    if (tokens.size() == 2u && sscanf(tokens.front().c_str(), "%u", &frame) ==  1) {
+    if (tokens.size() == 2u && integerFromString(tokens.front(), &frame)) {
         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();
+        errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
     }
     if (symGroup) {
         const unsigned succeeded = symGroup->expandList(inames, &errorMessage);
@@ -132,17 +262,6 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
     return S_OK;
 }
 
-static inline std::string msgLocalsUsage(PCSTR args)
-{
-    std::ostringstream str;
-    str << "Invalid parameter: '" << args
-        << "'\nUsage: locals [-t token] [-h] [-d] [-e expandset] [-u uninitializedset] <frame> [iname]).\n"
-           "-h human-readable ouput\n"
-           "-d debug output\n-e expandset Comma-separated list of expanded inames\n"
-           "-u uninitializedset Comma-separated list of uninitialized inames\n";
-    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)
@@ -170,7 +289,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
             break;
         case 'u':
             if (tokens.empty()) {
-                *errorMessage = msgLocalsUsage(args);
+                *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
                 return std::string();
             }
             split(tokens.front(), ',', std::back_inserter(uninitializedInames));
@@ -178,7 +297,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
             break;
         case 'e':
             if (tokens.empty()) {
-                *errorMessage = msgLocalsUsage(args);
+                *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
                 return std::string();
             }
             split(tokens.front(), ',', std::back_inserter(expandedInames));
@@ -188,8 +307,8 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
     }
     // Frame and iname
     unsigned frame;
-    if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
-        *errorMessage = msgLocalsUsage(args);
+    if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
+        *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
         return std::string();
     }
 
@@ -231,21 +350,19 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
 // Extension command 'dumplocal':
 // Dump a local variable using dumpers (testing command).
 
-const char dumpLocalUsageC[] = "Usage: dumplocal <frame> <iname>";
-
 static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage)
 {
     // Parse the command
     StringList tokens = commandTokens<StringList>(args, token);
     // Frame and iname
     unsigned frame;
-    if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
-        *errorMessage = dumpLocalUsageC;
+    if (tokens.empty() || integerFromString(tokens.front(), &frame)) {
+        *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
         return std::string();
     }
     tokens.pop_front();
     if (tokens.empty()) {
-        *errorMessage = dumpLocalUsageC;
+        *errorMessage = singleLineUsage(commandDescriptions[CmdDumplocal]);
         return std::string();
     }
     const std::string iname = tokens.front();
@@ -281,6 +398,65 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR  argsIn)
     return S_OK;
 }
 
+// Extension command 'typecast':
+// Change the type of a symbol group entry (testing purposes)
+
+extern "C" HRESULT CALLBACK typecast(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);
+    std::string iname;
+    std::string desiredType;
+    if (tokens.size() == 3u && integerFromString(tokens.front(), &frame)) {
+        symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
+        iname = tokens.at(1);
+        desiredType = tokens.at(2);
+    } else {
+        errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
+    }
+    if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
+        ExtensionContext::instance().report('R', token, "typecast", "OK");
+    } else {
+        ExtensionContext::instance().report('N', token, "typecast", errorMessage.c_str());
+    }
+    return S_OK;
+}
+
+// Extension command 'addsymbol':
+// Adds a symbol to a symbol group by name (testing purposes)
+
+extern "C" HRESULT CALLBACK addsymbol(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);
+    std::string name;
+    std::string iname;
+    if (tokens.size() >= 2u && integerFromString(tokens.front(), &frame)) {
+        symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
+        name = tokens.at(1);
+        if (tokens.size() >= 3)
+            iname = tokens.at(2);
+    } else {
+        errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
+    }
+    if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) {
+        ExtensionContext::instance().report('R', token, "addsymbol", "OK");
+    } else {
+        ExtensionContext::instance().report('N', token, "addsymbol", errorMessage.c_str());
+    }
+    return S_OK;
+}
+
 // Extension command 'assign':
 // Assign locals by iname: 'assign locals.x=5'
 
@@ -297,7 +473,7 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
         // 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'.";
+            errorMessage = singleLineUsage(commandDescriptions[CmdAssign]);
             break;
         }
         const std::string iname = tokens.front().substr(0, equalsPos);
@@ -399,7 +575,13 @@ extern "C" HRESULT CALLBACK idle(CIDebugClient *, PCSTR)
 
 extern "C" HRESULT CALLBACK help(CIDebugClient *, PCSTR)
 {
-    dprintf("Qt Creator CDB extension built %s\n", __DATE__);
+    std::ostringstream str;
+    str << "### Qt Creator CDB extension built " << __DATE__ << "\n\n";
+
+    const size_t commandCount = sizeof(commandDescriptions)/sizeof(CommandDescription);
+    std::copy(commandDescriptions, commandDescriptions + commandCount,
+              std::ostream_iterator<CommandDescription>(str));
+    dprintf("%s\n", str.str().c_str());
     return S_OK;
 }
 
@@ -422,7 +604,7 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
             && integerFromString(tokens.at(1), &length)) {
         memory = memoryToBase64(exc.dataSpaces(), address, length, &errorMessage);
     } else {
-        errorMessage = "Invalid parameters to memory command.";
+        errorMessage = singleLineUsage(commandDescriptions[CmdMemory]);
     }
 
     if (memory.empty()) {
diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp
index a212755b7e73271b52a40c1fafad95bacee4ce62..e63a2102d88a8bcbe9c3bcaac5a8680259d82530 100644
--- a/src/libs/qtcreatorcdbext/symbolgroup.cpp
+++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp
@@ -468,7 +468,7 @@ static void fixValue(const std::string &type, std::wstring *value)
         return;
     }
     // Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>'
-    if (value->compare(0, 6, L"class ") == 0) {
+    if (value->compare(0, 6, L"class ") == 0 || value->compare(0, 7, L"struct ") == 0) {
         const std::string::size_type openTemplate = value->find(L'<');
         if (openTemplate != std::string::npos) {
             value->erase(openTemplate + 1, value->size() - openTemplate - 2);
@@ -665,6 +665,17 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
     str << '\n';
 }
 
+static inline std::string msgCannotCast(const std::string &nodeName,
+                                        const std::string &fromType,
+                                        const std::string &toType,
+                                        const std::string &why)
+{
+    std::ostringstream str;
+    str << "Cannot cast node '" << nodeName << "' from '" << fromType
+        << "' to '" << toType << "': " << why;
+    return str.str();
+}
+
 // Expand!
 bool SymbolGroupNode::expand(std::string *errorMessage)
 {
@@ -704,6 +715,65 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
     return true;
 }
 
+bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *errorMessage)
+{
+    const std::string fromType = type();
+    if (fromType == desiredType)
+        return true;
+    if (isExpanded()) {
+        *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, "Already expanded");
+        return false;
+    }
+    HRESULT hr = m_symbolGroup->debugSymbolGroup()->OutputAsType(m_index, desiredType.c_str());
+    if (FAILED(hr)) {
+        *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("OutputAsType", hr));
+        return false;
+    }
+    hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(m_index, 1, &m_parameters);
+    if (FAILED(hr)) { // Should never fail
+        *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("GetSymbolParameters", hr));
+        return false;
+    }
+    return true;
+}
+
+static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why)
+{
+    std::ostringstream str;
+    str << "Cannot add symbol '" << name << "': " << why;
+    return str.str();
+}
+
+// For root nodes, only: Add a new symbol by name
+bool SymbolGroupNode::addSymbolByName(const std::string &name,
+                                      const std::string &iname,
+                                      std::string *errorMessage)
+{
+    ULONG index = DEBUG_ANY_ID; // Append
+    HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
+    if (FAILED(hr)) {
+        *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
+        return false;
+    }
+    SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
+    hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
+    if (FAILED(hr)) { // Should never fail
+        *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("GetSymbolParameters", hr));
+        return false;
+    }
+    // Paranoia: Check for cuckoo's eggs (which should not happen)
+    if (parameters.front().ParentSymbol != m_index) {
+        *errorMessage = msgCannotAddSymbol(name, "Parent id mismatch");
+        return false;
+    }
+    SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index,
+                                                name, iname.empty() ? name : iname,
+                                                this);
+    node->parseParameters(0, 0, parameters);
+    m_children.push_back(node);
+    return true;
+}
+
 static inline std::string msgNotFound(const std::string &nodeName)
 {
     std::ostringstream str;
@@ -834,6 +904,26 @@ bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage)
     return node->expand(errorMessage);
 }
 
+// Cast an (unexpanded) node
+bool SymbolGroup::typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage)
+{
+    SymbolGroupNode *node = find(iname);
+    if (!node) {
+        *errorMessage = msgNotFound(iname);
+        return false;
+    }
+    if (node == m_root) {
+        *errorMessage = "Cannot cast root node";
+        return false;
+    }
+    return node->typeCast(desiredType, errorMessage);
+}
+
+bool SymbolGroup::addSymbol(const std::string &name, const std::string &iname, std::string *errorMessage)
+{
+    return m_root->addSymbolByName(name, iname, errorMessage);
+}
+
 // Mark uninitialized (top level only)
 void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
 {
diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h
index 950066bca41559ef93816f255018ecd5723604a7..8fd886045fc3fe0d6f50ed3a928b2c2cd5fea82a 100644
--- a/src/libs/qtcreatorcdbext/symbolgroup.h
+++ b/src/libs/qtcreatorcdbext/symbolgroup.h
@@ -53,6 +53,13 @@ struct SymbolGroupValueContext;
 class SymbolGroupNode {
     SymbolGroupNode(const SymbolGroupNode&);
     SymbolGroupNode& operator=(const SymbolGroupNode&);
+
+    explicit SymbolGroupNode(SymbolGroup *symbolGroup,
+                             ULONG index,
+                             const std::string &name,
+                             const std::string &iname,
+                             SymbolGroupNode *parent = 0);
+
 public:
     enum Flags {
         Uninitialized = 0x1,
@@ -67,12 +74,6 @@ public:
     typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
     typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator;
 
-    explicit SymbolGroupNode(SymbolGroup *symbolGroup,
-                             ULONG index,
-                             const std::string &name,
-                             const std::string &iname,
-                             SymbolGroupNode *parent = 0);
-
     ~SymbolGroupNode() { removeChildren(); }
 
     void removeChildren();
@@ -81,6 +82,10 @@ public:
                          const SymbolParameterVector &vec);
 
     static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
+    // For root nodes, only: Add a new symbol by name
+    bool addSymbolByName(const std::string &name,  // Expression like 'myarray[1]'
+                         const std::string &iname, // Desired iname, defaults to name
+                         std::string *errorMessage);
 
     const std::string &name() const { return m_name; }
     std::string fullIName() const;
@@ -111,6 +116,8 @@ public:
     bool expand(std::string *errorMessage);
     bool isExpanded() const { return !m_children.empty(); }
     bool canExpand() const { return m_parameters.SubElements > 0; }
+    // Cast to a different type. Works only on unexpanded nodes
+    bool typeCast(const std::string &desiredType, std::string *errorMessage);
 
     ULONG subElements() const { return m_parameters.SubElements; }
     ULONG index() const { return m_index; }
@@ -213,6 +220,12 @@ public:
     // 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);
+    // Cast an (unexpanded) node
+    bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage);
+    // Add a symbol by name expression
+    bool addSymbol(const std::string &name, // Expression like 'myarray[1]'
+                   const std::string &iname, // Desired iname, defaults to name
+                   std::string *errorMessage);
 
     bool accept(SymbolGroupNodeVisitor &visitor) const;