diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 387d386c6a1..a46ecd075bb 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -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] [iname])."; + str << "Invalid parameter: '" << args + << "'\nUsage: locals [-t token] [-h] [-d] [-e expandset] [-u uninitializedset] [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(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); diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index ba33d804d83..f08a6292769 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -40,6 +40,7 @@ enum { debug = 0 }; typedef std::vector::size_type VectorIndexType; +typedef std::vector 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 "/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 = "'; + 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 += " 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(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=\"\""; + } 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(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 &nodes, std::str { if (nodes.empty()) return 0; - // Create a set with a key . Also required for 1 node. + // Create a set with a key . 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 &uniniNodes) +{ + if (m_root && !m_root->children().empty() && !uniniNodes.empty()) { + const std::vector::const_iterator unIniNodesBegin = uniniNodes.begin(); + const std::vector::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) diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index de9843ea3df..ae88e6b034a 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -45,6 +45,9 @@ class SymbolGroupNode { SymbolGroupNode(const SymbolGroupNode&); SymbolGroupNode& operator=(const SymbolGroupNode&); public: + enum Flags { + Uninitialized = 0x1 + }; typedef std::vector SymbolParameterVector; typedef std::vector 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 &nodes, std::string *errorMessage); + // Mark uninitialized (top level only) + void markUninitialized(const std::vector &nodes); // Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible // (think mkdir without -p). diff --git a/src/plugins/debugger/cdb2/bytearrayinputstream.cpp b/src/plugins/debugger/cdb2/bytearrayinputstream.cpp index 5c916cdd902..a1b1d40a6c1 100644 --- a/src/plugins/debugger/cdb2/bytearrayinputstream.cpp +++ b/src/plugins/debugger/cdb2/bytearrayinputstream.cpp @@ -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()) diff --git a/src/plugins/debugger/cdb2/bytearrayinputstream.h b/src/plugins/debugger/cdb2/bytearrayinputstream.h index a10962698c8..d9b8258adce 100644 --- a/src/plugins/debugger/cdb2/bytearrayinputstream.h +++ b/src/plugins/debugger/cdb2/bytearrayinputstream.h @@ -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 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); diff --git a/src/plugins/debugger/cdb2/cdbengine2.cpp b/src/plugins/debugger/cdb2/cdbengine2.cpp index f89383bf366..5452548d056 100644 --- a/src/plugins/debugger/cdb2/cdbengine2.cpp +++ b/src/plugins/debugger/cdb2/cdbengine2.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #ifdef Q_OS_WIN # include @@ -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 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) diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 90251b2a790..a21af15c460 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -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 *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; }