OSDN Git Service

Debugger[new CDB]: Work on locals.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Tue, 23 Nov 2010 12:36:39 +0000 (13:36 +0100)
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>
Tue, 23 Nov 2010 12:36:39 +0000 (13:36 +0100)
Add optional code model scope checking. Remove need to call
to 'expandlocals' command by giving 'locals' options for expanded
and uninitialized variables, saving one roundtrip.
Handle shadowed variables and __formal parameters. Differentiate
between name and iname in SymbolGroup.

src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
src/libs/qtcreatorcdbext/symbolgroup.cpp
src/libs/qtcreatorcdbext/symbolgroup.h
src/plugins/debugger/cdb2/bytearrayinputstream.cpp
src/plugins/debugger/cdb2/bytearrayinputstream.h
src/plugins/debugger/cdb2/cdbengine2.cpp
src/plugins/debugger/watchutils.cpp

index 387d386..a46ecd0 100644 (file)
@@ -134,7 +134,11 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
 static inline std::string msgLocalsUsage(PCSTR args)
 {
     std::ostringstream str;
-    str << "Invalid parameter: '" << args << "' (usage: locals [-d] <frame> [iname]).";
+    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();
 }
 
@@ -150,18 +154,38 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
     std::string iname;
 
     StringList tokens = commandTokens<StringList>(args, token);
+    StringVector expandedInames;
+    StringVector uninitializedInames;
+    // Parse away options
     while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
-        // Parse options -d (debug)/- humanreadable GDBMI
-        switch (tokens.front().at(1)) {
+        const char option = tokens.front().at(1);
+        tokens.pop_front();
+        switch (option) {
         case 'd':
             debugOutput++;
             break;
         case 'h':
             humanReadableGdbmi = true;
             break;
+        case 'u':
+            if (tokens.empty()) {
+                *errorMessage = msgLocalsUsage(args);
+                return std::string();
+            }
+            split(tokens.front(), ',', std::back_inserter(uninitializedInames));
+            tokens.pop_front();
+            break;
+        case 'e':
+            if (tokens.empty()) {
+                *errorMessage = msgLocalsUsage(args);
+                return std::string();
+            }
+            split(tokens.front(), ',', std::back_inserter(expandedInames));
+            tokens.pop_front();
+            break;
         }
-        tokens.pop_front();
     }
+    // Frame and iname
     unsigned frame;
     if (tokens.empty() || sscanf(tokens.front().c_str(), "%u", &frame) != 1) {
         *errorMessage = msgLocalsUsage(args);
@@ -175,7 +199,10 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
     SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
     if (!symGroup)
         return std::string();
-
+    if (!expandedInames.empty())
+        symGroup->expandList(expandedInames, errorMessage);
+    if (!uninitializedInames.empty())
+        symGroup->markUninitialized(uninitializedInames);
     // Complete dump
     if (iname.empty())
         return debugOutput ? symGroup->debug(debugOutput - 1) : symGroup->dump(humanReadableGdbmi);
index ba33d80..f08a629 100644 (file)
@@ -40,6 +40,7 @@
 enum { debug = 0 };
 
 typedef std::vector<int>::size_type VectorIndexType;
+typedef std::vector<std::string> StringVector;
 
 const char rootNameC[] = "local";
 
@@ -207,9 +208,10 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS
 
 // ------- SymbolGroupNode
 SymbolGroupNode::SymbolGroupNode(CIDebugSymbolGroup *symbolGroup,
-                                 const std::string &n,
+                                 const std::string &name,
+                                 const std::string &iname,
                                  SymbolGroupNode *parent) :
-    m_symbolGroup(symbolGroup), m_parent(parent), m_name(n)
+    m_symbolGroup(symbolGroup), m_parent(parent), m_name(name), m_iname(iname), m_flags(0)
 {
     memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
     m_parameters.ParentSymbol = DEBUG_ANY_ID;
@@ -230,23 +232,6 @@ 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
 {
@@ -258,6 +243,58 @@ std::string SymbolGroupNode::fullIName() const
     return rc;
 }
 
+// Fix an array iname "[0]" -> "0" for sorting to work correctly
+static inline void fixArrayIname(std::string *iname)
+{
+    if (!iname->empty() && iname->at(0) == '[') {
+        const std::string::size_type last = iname->size() - 1;
+        if (iname->at(last) == ']') {
+            iname->erase(last, 1);
+            iname->erase(0, 1);
+        }
+    }
+}
+
+// Fix up names and inames
+static inline void fixNames(bool isTopLevel, StringVector *names, StringVector *inames)
+{
+    if (names->empty())
+        return;
+    unsigned unnamedId = 1;
+    /* 1) Fix name="__formal", which occurs when someone writes "void foo(int /* x * /)..."
+     * 2) Fix array inames for sorting: "[6]" -> name="[6]",iname="6"
+     * 3) For toplevels: Fix shadowed variables in the order the debugger expects them:
+       \code
+       int x;             // Occurrence (1), should be reported as name="x <shadowed 1>"/iname="x#1"
+       if (true) {
+          int x = 5; (2)  // Occurrence (2), should be reported as name="x"/iname="x"
+       }
+      \endcode */
+    StringVector::iterator nameIt = names->begin();
+    const StringVector::iterator namesEnd = names->end();
+    for (StringVector::iterator iNameIt = inames->begin(); nameIt != namesEnd ; ++nameIt, ++iNameIt) {
+        std::string &name = *nameIt;
+        std::string &iname = *iNameIt;
+        if (name.empty() || name == "__formal") {
+            const std::string number = toString(unnamedId++);
+            name = "<unnamed "  + number + '>';
+            iname = "unnamed#" + number;
+        } else {
+            fixArrayIname(&iname);
+        }
+        if (isTopLevel) {
+            if (const StringVector::size_type shadowCount = std::count(nameIt + 1, namesEnd, name)) {
+                const std::string number = toString(shadowCount);
+                name += " <shadowed ";
+                name += number;
+                name += '>';
+                iname += '#';
+                iname += number;
+            }
+        }
+    }
+}
+
 // Index: Index of symbol, parameterOffset: Looking only at a part of the symbol array, offset
 void SymbolGroupNode::parseParameters(VectorIndexType index,
                                       VectorIndexType parameterOffset,
@@ -279,15 +316,31 @@ void SymbolGroupNode::parseParameters(VectorIndexType index,
 
     const VectorIndexType size = vec.size();
     // Scan the top level elements
+    StringVector names;
+    names.reserve(size);
+    // Pass 1) Determine names. We need the complete set first in order to do some corrections.
     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);
+            if (FAILED(m_symbolGroup->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize)))
+                buf[0] = '\0';
+            names.push_back(std::string(buf));
+        }
+    }
+    // 2) Fix names
+    StringVector inames = names;
+    fixNames(isTopLevel, &names, &inames);
+    // Pass 3): Add nodes with fixed names
+    StringVector::size_type nameIndex = 0;
+    for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) {
+        if (vec.at(pos).ParentSymbol == index) {
+            const VectorIndexType symbolGroupIndex = pos + parameterOffset;
+            SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, names.at(nameIndex),
+                                                         inames.at(nameIndex), this);
             child->parseParameters(symbolGroupIndex, parameterOffset, vec);
             m_children.push_back(child);
+            nameIndex++;
         }
     }
     if (isTopLevel)
@@ -296,7 +349,7 @@ void SymbolGroupNode::parseParameters(VectorIndexType index,
 
 SymbolGroupNode *SymbolGroupNode::create(CIDebugSymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec)
 {
-    SymbolGroupNode *rc = new SymbolGroupNode(sg, name);
+    SymbolGroupNode *rc = new SymbolGroupNode(sg, name, name);
     rc->parseParameters(DEBUG_ANY_ID, 0, vec);
     return rc;
 }
@@ -436,28 +489,40 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
         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 << '"';
+    bool valueEditable = true;
+    bool valueEnabled = true;
+
+    const bool uninitialized = m_flags & Uninitialized;
+    if (uninitialized) {
+        valueEditable = valueEnabled = false;
+        str << ",valueencoded=\"0\",value=\"<not in scope>\"";
+    } else {
+        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;
         }
-        delete [] wbuf;
     }
     // Children: Dump all known or subelements (guess).
-    const VectorIndexType childCountGuess = m_children.empty() ? m_parameters.SubElements : m_children.size();
+    const VectorIndexType childCountGuess = uninitialized ? 0 :
+             (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()) {
+    if (childCountGuess != 0)
+        valueEditable = false;
+    str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"'
+        << ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"'
+        << ",numchild=\"" << childCountGuess << '"';
+    if (!uninitialized && !m_children.empty()) {
         str << ",children=[";
         if (humanReadable)
             str << '\n';
@@ -501,9 +566,14 @@ bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, un
 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 << '"' << m_name << "\" Children=" << m_children.size() << ' ' << m_parameters
+           << " flags=" << m_flags;
+    if (verbosity) {
+        str << " Address=0x" << std::hex << address(index) << std::dec
+            << " Type=\"" << getType(index) << '"';
+        if (!(m_flags & Uninitialized))
+            str << "\" Value=\"" << gdbmiWStringFormat(rawValue(index)) << '"';
+    }
     str << '\n';
 }
 
@@ -531,6 +601,10 @@ bool SymbolGroupNode::expand(ULONG index, std::string *errorMessage)
         *errorMessage = "No subelements to expand in node: " + fullIName();
         return false;
     }
+    if (m_flags & Uninitialized) {
+        *errorMessage = "Refusing to expand uninitialized node: " + fullIName();
+        return false;
+    }
 
     const HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
 
@@ -626,7 +700,7 @@ unsigned SymbolGroup::expandList(const std::vector<std::string> &nodes, std::str
 {
     if (nodes.empty())
         return 0;
-    // Create a set with a key <level, name>. Also required for 1 node.
+    // Create a set with a key <level, name>. Also required for 1 node (see above).
     InamePathEntrySet pathEntries;
     const VectorIndexType nodeCount = nodes.size();
     for (VectorIndexType i= 0; i < nodeCount; i++) {
@@ -670,6 +744,21 @@ bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage)
     return node->expand(index, errorMessage);
 }
 
+// Mark uninitialized (top level only)
+void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
+{
+    if (m_root && !m_root->children().empty() && !uniniNodes.empty()) {
+        const std::vector<std::string>::const_iterator unIniNodesBegin = uniniNodes.begin();
+        const std::vector<std::string>::const_iterator unIniNodesEnd = uniniNodes.end();
+
+        const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end();
+        for (SymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) {
+            if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->fullIName()) != unIniNodesEnd)
+                (*it)->setFlags((*it)->flags() | SymbolGroupNode::Uninitialized);
+        }
+    }
+}
+
 static inline std::string msgAssignError(const std::string &nodeName,
                                          const std::string &value,
                                          const std::string &why)
index de9843e..ae88e6b 100644 (file)
@@ -45,6 +45,9 @@ class SymbolGroupNode {
     SymbolGroupNode(const SymbolGroupNode&);
     SymbolGroupNode& operator=(const SymbolGroupNode&);
 public:
+    enum Flags {
+        Uninitialized = 0x1
+    };
     typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
     typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
     typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
@@ -52,6 +55,7 @@ public:
 
     explicit SymbolGroupNode(CIDebugSymbolGroup *symbolGroup,
                              const std::string &name,
+                             const std::string &iname,
                              SymbolGroupNode *parent = 0);
 
     ~SymbolGroupNode() { removeChildren(); }
@@ -65,7 +69,7 @@ public:
 
     const std::string &name() const { return m_name; }
     std::string fullIName() const;
-    std::string iName() const;
+    const std::string &iName() const { return m_iname; }
 
     const SymbolGroupNodePtrVector &children() const { return m_children; }
     SymbolGroupNode *childAt(unsigned) const;
@@ -91,6 +95,9 @@ public:
 
     ULONG subElements() const { return m_parameters.SubElements; }
 
+    unsigned flags() const     { return m_flags; }
+    void setFlags(unsigned f)  { m_flags = f; }
+
 private:
     // Return allocated wide string array of value
     wchar_t *getValue(ULONG index, ULONG *obtainedSize = 0) const;
@@ -102,6 +109,8 @@ private:
     DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct.
     SymbolGroupNodePtrVector m_children;
     const std::string m_name;
+    const std::string m_iname;
+    unsigned m_flags;
 };
 
 /* Visitor that takes care of iterating over the nodes and the index bookkeeping.
@@ -165,6 +174,8 @@ public:
     // 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);
+    // Mark uninitialized (top level only)
+    void markUninitialized(const std::vector<std::string> &nodes);
 
     // Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible
     // (think mkdir without -p).
index 5c916cd..a1b1d40 100644 (file)
@@ -37,6 +37,12 @@ ByteArrayInputStream::ByteArrayInputStream(QByteArray &ba) :
 {
 }
 
+void ByteArrayInputStream::appendSeparator(char c)
+{
+    if (!m_target.isEmpty() && !m_target.endsWith(c))
+        m_target.append(c);
+}
+
 void hexPrefixOn(ByteArrayInputStream &bs)
 {
     bs.setHexPrefix(true);
@@ -57,6 +63,11 @@ void dec(ByteArrayInputStream &bs)
     bs.setIntegerBase(10);
 }
 
+void blankSeparator(ByteArrayInputStream &bs)
+{
+    bs.appendSeparator();
+}
+
 QByteArray trimFront(QByteArray in)
 {
     if (in.isEmpty())
index a109626..d9b8258 100644 (file)
@@ -61,6 +61,8 @@ public:
     bool hexPrefix() const     { return  m_hexPrefix; }
     void setIntegerBase(int b) { m_integerBase = b; }
     int integerBase() const    { return m_integerBase; }
+    // Append a separator if required (target does not end with it)
+    void appendSeparator(char c = ' ');
 
 private:
     template <class IntType> void appendInt(IntType i);
@@ -93,6 +95,7 @@ void hexPrefixOn(ByteArrayInputStream &bs);
 void hexPrefixOff(ByteArrayInputStream &bs);
 void hex(ByteArrayInputStream &bs);
 void dec(ByteArrayInputStream &bs);
+void blankSeparator(ByteArrayInputStream &bs);
 
 // Bytearray parse helpers
 QByteArray trimFront(QByteArray in);
index f89383b..5452548 100644 (file)
@@ -61,6 +61,7 @@
 #include <QtCore/QDebug>
 #include <QtCore/QTextStream>
 #include <QtCore/QDateTime>
+#include <QtGui/QToolTip>
 
 #ifdef Q_OS_WIN
 #    include <utils/winutils.h>
@@ -73,6 +74,7 @@ Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerViewAgent*)
 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewAgent*)
 
 enum { debug = 0 };
+enum { debugLocals = 0 };
 enum { debugBreakpoints = 0 };
 
 #if 0
@@ -107,6 +109,8 @@ enum { debugBreakpoints = 0 };
 namespace Debugger {
 namespace Cdb {
 
+static const char localsPrefixC[] = "local.";
+
 using namespace Debugger::Internal;
 
 struct MemoryViewCookie {
@@ -346,10 +350,11 @@ void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd
     // No numerical or any other expressions [yet]
     if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
         return;
-    const QByteArray iname = QByteArray("local.") + exp.toAscii();
-    const QModelIndex index = watchHandler()->itemIndex(iname);
-    Q_UNUSED(index)
-    Q_UNUSED(mousePos)
+    const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
+    if (const WatchData *data = watchHandler()->findItem(iname)) {
+        QToolTip::hideText();
+        QToolTip::showText(mousePos, data->toToolTip());
+    }
 }
 
 void CdbEngine::setupEngine()
@@ -903,8 +908,6 @@ void CdbEngine::postExtensionCommand(const QByteArray &cmd,
         showMessage(msg, LogError);
         return;
     }
-    if (!flags & QuietCommand)
-        showMessage(QString::fromLocal8Bit(cmd), LogInput);
 
     const int token = m_nextCommandToken++;
 
@@ -915,6 +918,9 @@ void CdbEngine::postExtensionCommand(const QByteArray &cmd,
     if (!arguments.isEmpty())
         str <<  ' ' << arguments;
 
+    if (!flags & QuietCommand)
+        showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
+
     CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
 
     m_extensionCommandQueue.push_back(pendingCommand);
@@ -934,9 +940,10 @@ void CdbEngine::activateFrame(int index)
     const Debugger::Internal::StackFrames &frames = stackHandler()->frames();
     QTC_ASSERT(index < frames.size(), return; )
 
-    if (debug)
+    const StackFrame frame = frames.at(index);
+    if (debug || debugLocals)
         qDebug("activateFrame idx=%d '%s' %d", index,
-               qPrintable(frames.at(index).file), frames.at(index).line);
+               qPrintable(frame.file), frame.line);
     stackHandler()->setCurrentIndex(index);
     const bool showAssembler = !frames.at(index).isUsable();
     if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
@@ -944,30 +951,46 @@ void CdbEngine::activateFrame(int index)
         watchHandler()->endCycle();
         QAction *assemblerAction = theAssemblerAction();
         if (assemblerAction->isChecked()) {
-            gotoLocation(frames.at(index), true);
+            gotoLocation(frame, true);
         } else {
             assemblerAction->trigger(); // Seems to trigger update
         }
         return;
     }
-    gotoLocation(frames.at(index), true);
-    // Watchers: Initial expand and query
+    gotoLocation(frame, true);
+    // Watchers: Initial expand, get uninitialized and query
+    QByteArray arguments;
+    ByteArrayInputStream str(arguments);
+    // Pre-expand
     const QSet<QByteArray> expanded = watchHandler()->expandedINames();
     if (!expanded.isEmpty()) {
-        QByteArray expandArguments;
-        ByteArrayInputStream expandStr(expandArguments);
-        expandStr << index << ' ';
+        str << blankSeparator << "-e ";
         int i = 0;
         foreach(const QByteArray &e, expanded) {
             if (i++)
-                expandStr << ',';
-            expandStr << e;
+                str << ',';
+            str << e;
         }
-        postExtensionCommand("expandlocals", expandArguments, 0, &CdbEngine::handleExpandLocals);
     }
-
+    // Uninitialized variables if desired
+    if (debuggerCore()->boolSetting(UseCodeModel)) {
+        QStringList uninitializedVariables;
+        getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
+                                  frame.function, frame.file, frame.line, &uninitializedVariables);
+        if (!uninitializedVariables.isEmpty()) {
+            str << blankSeparator << "-u ";
+            int i = 0;
+            foreach(const QString &u, uninitializedVariables) {
+                if (i++)
+                    str << ',';
+                str << localsPrefixC << u;
+            }
+        }
+    }
+    // Required arguments: frame
+    str << blankSeparator << index;
     watchHandler()->beginCycle();
-    postExtensionCommand("locals", QByteArray::number(index), 0, &CdbEngine::handleLocals);
+    postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals);
 }
 
 void CdbEngine::selectThread(int index)
index 90251b2..a21af15 100644 (file)
@@ -1409,9 +1409,16 @@ static bool gdbMiGetBoolValue(bool *target,
 
 struct GdbMiRecursionContext
 {
-    GdbMiRecursionContext(int recursionLevelIn = 0) :
-            recursionLevel(recursionLevelIn), childNumChild(-1), childIndex(0) {}
+    enum Type
+    {
+        Debugger,    // Debugger symbol dump, recursive/symmetrical
+        GdbMacrosCpp // old gdbmacros.cpp format, unsymmetrical
+    };
 
+    GdbMiRecursionContext(Type t, int recursionLevelIn = 0) :
+            type(t), recursionLevel(recursionLevelIn), childNumChild(-1), childIndex(0) {}
+
+    const Type type;
     int recursionLevel;
     int childNumChild;
     int childIndex;
@@ -1429,31 +1436,41 @@ static void gbdMiToWatchData(const GdbMi &root,
     QString v;
     QByteArray b;
     // Check for name/iname and use as expression default
-    if (ctx.recursionLevel == 0) {
-        // parents have only iname, from which name is derived
-        QString iname;
-        if (!gdbMiGetStringValue(&iname, root, "iname"))
-            qWarning("Internal error: iname missing");
-        w.iname = iname.toLatin1();
-        w.name = iname;
-        const int lastDotPos = w.name.lastIndexOf(QLatin1Char('.'));
-        if (lastDotPos != -1)
-            w.name.remove(0, lastDotPos + 1);
-        w.exp = w.name.toLatin1();
+    w.sortId = ctx.childIndex;
+    // Fully symmetrical
+    if (ctx.type == GdbMiRecursionContext::Debugger) {
+        gdbMiGetByteArrayValue(&w.iname, root, "iname");
+        gdbMiGetStringValue(&w.name, root, "name");
+        gdbMiGetByteArrayValue(&w.exp, root, "exp");
     } else {
-        // Children can have a 'name' attribute. If missing, assume array index
-        // For display purposes, it can be overridden by "key"
-        if (!gdbMiGetStringValue(&w.name, root, "name")) {
-            w.name = QString::number(ctx.childIndex);
-        }
-        // Set iname
-        w.iname = ctx.parentIName;
-        w.iname += '.';
-        w.iname += w.name.toLatin1();
-        // Key?
-        QString key;
-        if (gdbMiGetStringValue(&key, root, "key", "keyencoded")) {
-            w.name = key.size() > 13 ? key.mid(0, 13) + QLatin1String("...") : key;
+        // gdbmacros.cpp: iname/name present according to recursion level
+        // Check for name/iname and use as expression default
+        if (ctx.recursionLevel == 0) {
+            // parents have only iname, from which name is derived
+            QString iname;
+            if (!gdbMiGetStringValue(&iname, root, "iname"))
+                qWarning("Internal error: iname missing");
+            w.iname = iname.toLatin1();
+            w.name = iname;
+            const int lastDotPos = w.name.lastIndexOf(QLatin1Char('.'));
+            if (lastDotPos != -1)
+                w.name.remove(0, lastDotPos + 1);
+            w.exp = w.name.toLatin1();
+        } else {
+            // Children can have a 'name' attribute. If missing, assume array index
+            // For display purposes, it can be overridden by "key"
+            if (!gdbMiGetStringValue(&w.name, root, "name")) {
+                w.name = QString::number(ctx.childIndex);
+            }
+            // Set iname
+            w.iname = ctx.parentIName;
+            w.iname += '.';
+            w.iname += w.name.toLatin1();
+            // Key?
+            QString key;
+            if (gdbMiGetStringValue(&key, root, "key", "keyencoded")) {
+                w.name = key.size() > 13 ? key.mid(0, 13) + QLatin1String("...") : key;
+            }
         }
     }
     if (w.name.isEmpty()) {
@@ -1507,7 +1524,7 @@ static void gbdMiToWatchData(const GdbMi &root,
     if (children.empty())
         return;
     wl->back().setChildrenUnneeded();
-    GdbMiRecursionContext nextLevelContext(ctx.recursionLevel + 1);
+    GdbMiRecursionContext nextLevelContext(ctx.type, ctx.recursionLevel + 1);
     nextLevelContext.parentIName = w.iname;
     gdbMiGetStringValue(&nextLevelContext.childType, root, "childtype");
     if (!gdbMiGetIntValue(&nextLevelContext.childNumChild, root, "childnumchild"))
@@ -1528,12 +1545,12 @@ bool QtDumperHelper::parseValue(const char *data, QList<WatchData> *l)
         if (!root.isValid())
             return false;
         foreach(const GdbMi &child, root.children())
-            gbdMiToWatchData(child, GdbMiRecursionContext(), l);
+            gbdMiToWatchData(child, GdbMiRecursionContext(GdbMiRecursionContext::Debugger), l);
     } else {
         root.fromStringMultiple(QByteArray(data));
         if (!root.isValid())
             return false;
-        gbdMiToWatchData(root, GdbMiRecursionContext(), l);
+        gbdMiToWatchData(root, GdbMiRecursionContext(GdbMiRecursionContext::GdbMacrosCpp), l);
     }
     return true;
 }