From caa164a4ab7094fa134031db1a04c1c469542f63 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Mon, 21 Sep 2009 14:55:39 +0200 Subject: [PATCH] CDB: Use GdbMi parser for everything. ..preparing recursive dumpers. Add recursive parser to watchutils.cpp Use insertBulkData() within CDB, add sizeof() information for some Qt containers in order to be able to dump QList<QList<KnownType> > . --- share/qtcreator/gdbmacros/gdbmacros.cpp | 5 + share/qtcreator/gdbmacros/test/main.cpp | 27 + src/plugins/debugger/cdb/cdbdumperhelper.cpp | 10 +- src/plugins/debugger/cdb/cdbdumperhelper.h | 4 +- .../debugger/cdb/cdbstackframecontext.cpp | 31 +- src/plugins/debugger/watchhandler.cpp | 2 + src/plugins/debugger/watchutils.cpp | 533 ++++++------------ src/plugins/debugger/watchutils.h | 53 +- 8 files changed, 226 insertions(+), 439 deletions(-) diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp index 993c4485a3f..c1227b79c0a 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.cpp +++ b/share/qtcreator/gdbmacros/gdbmacros.cpp @@ -37,6 +37,7 @@ #include <QtCore/QHash> #include <QtCore/QLinkedList> #include <QtCore/QList> +#include <QtCore/QQueue> #include <QtCore/QLocale> #include <QtCore/QMap> #include <QtCore/QMetaEnum> @@ -3622,6 +3623,10 @@ static inline void dumpSizes(QDumper &d) sizeMap.insert(sizeof(QStringList), NS"QStringList"); #ifndef QT_BOOTSTRAPPED sizeMap.insert(sizeof(QObject), NS"QObject"); + sizeMap.insert(sizeof(QList<int>), NS"QList<int>"); + sizeMap.insert(sizeof(QLinkedList<int>), NS"QLinkedList<int>"); + sizeMap.insert(sizeof(QVector<int>), NS"QVector<int>"); + sizeMap.insert(sizeof(QQueue<int>), NS"QQueue<int>"); #endif #if USE_QT_GUI sizeMap.insert(sizeof(QWidget), NS"QWidget"); diff --git a/share/qtcreator/gdbmacros/test/main.cpp b/share/qtcreator/gdbmacros/test/main.cpp index 39956dcb0fb..2696b32f12a 100644 --- a/share/qtcreator/gdbmacros/test/main.cpp +++ b/share/qtcreator/gdbmacros/test/main.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include <QtCore/QStringList> +#include <QtCore/QLinkedList> #include <QtCore/QVector> #include <QtCore/QSharedPointer> #include <QtCore/QTimer> @@ -138,6 +139,16 @@ static int dumpQIntList() return 0; } +static int dumpQIntLinkedList() +{ + QLinkedList<int> test = QLinkedList<int>() << 1 << 2; + prepareInBuffer("QLinkedList", "local.qintlinkedlist", "local.qlinkedintlist", "int"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), 0, 0, 0); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + static int dumpQIntVector() { QVector<int> test = QVector<int>() << 42 << 43; @@ -176,6 +187,20 @@ static int dumpQMapIntString() return 0; } +static int dumpQMapQStringString() +{ + QMap<QString,QString> test; + QMapNode<QString,QString> mapNode; + const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode; + test.insert(QLatin1String("42s"), QLatin1String("fortytwo")); + test.insert(QLatin1String("423"), QLatin1String("fortytree")); + prepareInBuffer("QMap", "local.qmapqstringqstring", "local.qmapqstringqstring", "QString@QString"); + qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), sizeof(QString), sizeof(mapNode), valueOffset); + fputs(qDumpOutBuffer, stdout); + fputc('\n', stdout); + return 0; +} + static int dumpQVariant() { QVariant test(QLatin1String("item")); @@ -417,9 +442,11 @@ static TypeDumpFunctionMap registerTypes() rc.insert("QSharedPointer<QString>", dumpQSharedPointerQString); rc.insert("QStringList", dumpQStringList); rc.insert("QList<int>", dumpQIntList); + rc.insert("QLinkedList<int>", dumpQIntLinkedList); rc.insert("QList<std::string>", dumpStdStringQList); rc.insert("QVector<int>", dumpQIntVector); rc.insert("QMap<int,QString>", dumpQMapIntString); + rc.insert("QMap<QString,QString>", dumpQMapQStringString); rc.insert("QMap<int,int>", dumpQMapIntInt); rc.insert("string", dumpStdString); rc.insert("wstring", dumpStdWString); diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 3e66d27d500..de6f3d1cd16 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -554,7 +554,7 @@ static inline QString msgNotHandled(const QString &type) return QString::fromLatin1("The type '%1' is not handled.").arg(type); } -CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, int source, +CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, QList<WatchData> *result, QString *errorMessage) { // Check failure cache and supported types @@ -593,7 +593,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool arg(wd.name, wd.exp, wd.type); m_access->showDebuggerOutput(LogMisc, message); - const DumpExecuteResult der = executeDump(wd, td, dumpChildren, source, result, errorMessage); + const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage); if (der == DumpExecuteOk) return DumpOk; // Cache types that fail due to complicated template size expressions. @@ -610,7 +610,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool CdbDumperHelper::DumpExecuteResult CdbDumperHelper::executeDump(const WatchData &wd, - const QtDumperHelper::TypeData& td, bool dumpChildren, int source, + const QtDumperHelper::TypeData& td, bool dumpChildren, QList<WatchData> *result, QString *errorMessage) { QByteArray inBuffer; @@ -658,12 +658,10 @@ CdbDumperHelper::DumpExecuteResult } if (!callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) return DumpExecuteCallFailed; - QtDumperResult dumpResult; - if (!QtDumperHelper::parseValue(outputData, &dumpResult)) { + if (!QtDumperHelper::parseValue(outputData, result)) { *errorMessage = QLatin1String("Parsing of value query output failed."); return DumpExecuteCallFailed; } - *result = dumpResult.toWatchData(source); return DumpExecuteOk; } diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h index 5c7530a7a7b..c9895e08d96 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.h +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -89,7 +89,7 @@ public: // Dump a WatchData item. enum DumpResult { DumpNotHandled, DumpOk, DumpError }; - DumpResult dumpType(const WatchData &d, bool dumpChildren, int source, + DumpResult dumpType(const WatchData &d, bool dumpChildren, QList<WatchData> *result, QString *errorMessage); inline CdbComInterfaces *comInterfaces() const { return m_cif; } @@ -113,7 +113,7 @@ private: DumpComplexExpressionEncountered, DumpExecuteCallFailed }; DumpExecuteResult executeDump(const WatchData &wd, - const QtDumperHelper::TypeData& td, bool dumpChildren, int source, + const QtDumperHelper::TypeData& td, bool dumpChildren, QList<WatchData> *result, QString *errorMessage); static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage); diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index 8938f93e48a..1b9b18a04f0 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -156,7 +156,7 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt derefedWd.name = QString(QLatin1Char('*')); derefedWd.iname = wd.iname + QLatin1String(".*"); derefedWd.source = OwnerDumper | CdbStackFrameContext::ChildrenKnownBit; - const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(derefedWd, true, OwnerDumper, &m_dumperResult, errorMessage); + const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(derefedWd, true, &m_dumperResult, errorMessage); if (dr != CdbDumperHelper::DumpOk) break; // Insert the pointer item with 1 additional child + its dumper results @@ -166,8 +166,7 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt ptrWd.setHasChildren(true); ptrWd.setChildrenUnneeded(); m_wh->insertData(ptrWd); - foreach(const WatchData &dwd, m_dumperResult) - m_wh->insertData(dwd); + m_wh->insertBulkData(m_dumperResult); handled = true; } while (false); if (debugCDBWatchHandling) @@ -184,7 +183,8 @@ static inline void fixDumperResult(const WatchData &source, const int size = result->size(); if (!size) return; - // debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult"); + if (debugCDBWatchHandling) + debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult"); WatchData &returned = result->front(); if (returned.iname != source.iname) return; @@ -198,6 +198,10 @@ static inline void fixDumperResult(const WatchData &source, returned.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>")); } } + // Indicate owner and known children + returned.source = OwnerDumper; + if (returned.isChildrenKnown() && returned.isHasChildrenKnown() && returned.hasChildren) + returned.source |= CdbStackFrameContext::ChildrenKnownBit; if (size == 1) return; // If the model queries the expanding item by pretending childrenNeeded=1, @@ -208,6 +212,10 @@ static inline void fixDumperResult(const WatchData &source, QList<WatchData>::iterator it = result->begin(); for (++it; it != wend; ++it) { WatchData &wd = *it; + // Indicate owner and known children + it->source = OwnerDumper; + if (it->isChildrenKnown() && it->isHasChildrenKnown() && it->hasChildren) + it->source |= CdbStackFrameContext::ChildrenKnownBit; if (wd.addr.isEmpty() && wd.isSomethingNeeded()) { wd.setHasChildren(false); wd.setAllUnneeded(); @@ -218,7 +226,8 @@ static inline void fixDumperResult(const WatchData &source, wd.setHasChildren(false); } } - // debugWatchDataList(*result, "<fixDumperResult"); + if (debugCDBWatchHandling) + debugWatchDataList(*result, "<fixDumperResult"); } WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) @@ -238,15 +247,14 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) return *this; } // Try library dumpers. - switch (m_dumper->dumpType(wd, true, OwnerDumper, &m_dumperResult, &errorMessage)) { + switch (m_dumper->dumpType(wd, true, &m_dumperResult, &errorMessage)) { case CdbDumperHelper::DumpOk: if (debugCDBWatchHandling) qDebug() << "dumper triggered"; // Dumpers omit types for complicated templates fixDumperResult(wd, &m_dumperResult, false); // Discard the original item and insert the dumper results - foreach(const WatchData &dwd, m_dumperResult) - m_wh->insertData(dwd); + m_wh->insertBulkData(m_dumperResult); // Nasty side effect: Modify owner for the ignore predicate wd.source = OwnerDumper; break; @@ -325,13 +333,12 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal, return true; } QList<WatchData> dumperResult; - const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage); + const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, &dumperResult, errorMessage); if (dr == CdbDumperHelper::DumpOk) { // Hack to stop endless model recursion const bool suppressGrandChildren = !wh->isExpandedIName(incompleteLocal.iname); fixDumperResult(incompleteLocal, &dumperResult, suppressGrandChildren); - foreach(const WatchData &dwd, dumperResult) - wh->insertData(dwd); + wh->insertBulkData(dumperResult); } else { const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage); qWarning("%s", qPrintable(msg)); @@ -372,7 +379,7 @@ bool CdbStackFrameContext::editorToolTip(const QString &iname, // Check dumpers. Should actually be just one item. if (m_useDumpers && m_dumper->state() != CdbDumperHelper::Disabled) { QList<WatchData> result; - if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, OwnerDumper, &result, errorMessage)) { + if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) { foreach (const WatchData &dwd, result) { if (!value->isEmpty()) value->append(QLatin1Char('\n')); diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 8e71dae9610..ac52e47baa3 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -227,6 +227,8 @@ QString WatchData::toString() const str << QLatin1Char('{'); if (!iname.isEmpty()) str << "iname=\"" << iname << doubleQuoteComma; + if (!name.isEmpty() && name != iname) + str << "name=\"" << name << doubleQuoteComma; if (!addr.isEmpty()) str << "addr=\"" << addr << doubleQuoteComma; if (!exp.isEmpty()) diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 27c3ef20746..d09544fcab3 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -492,172 +492,6 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos, return expr; } -// --------------- QtDumperResult - -QtDumperResult::Child::Child() : - keyEncoded(0), - valueEncoded(0), - childCount(-1), - valueEnabled(true), - valueEncountered(false) -{ -} - -QtDumperResult::QtDumperResult() : - valueEncountered(false), - valueEncoded(0), - valueEnabled(true), - childCount(-1), - internal(false), - childChildCount(-1) -{ -} - -void QtDumperResult::clear() -{ - iname.clear(); - value.clear(); - address.clear(); - addressInfo.clear(); - type.clear(); - extra.clear(); - displayedType.clear(); - valueEncoded = 0; - valueEncountered = false; - valueEnabled = false; - childCount = -1; - internal = false; - childType.clear(); - children.clear(); - childChildCount = -1; -} - -QList<WatchData> QtDumperResult::toWatchData(int source) const -{ - QList<WatchData> rc; - rc.push_back(WatchData()); - WatchData &root = rc.front(); - root.iname = iname; - const QChar dot = QLatin1Char('.'); - const int lastDotIndex = root.iname.lastIndexOf(dot); - root.exp = root.name = lastDotIndex == -1 ? iname : iname.mid(lastDotIndex + 1); - if (valueEncountered) { - root.setValue(decodeData(value, valueEncoded)); - root.valueEnabled = valueEnabled; - } - root.setType(type); - if (!displayedType.isEmpty()) - root.displayedType = displayedType; - root.setAddress(address); - root.source = source; - if (childCount >= 0) - root.setHasChildren(childCount > 0); - // Children. Sanity check after parsing sets childcount to list size - // if list is not empty - if (children.empty()) { - if (childCount > 0) - root.setChildrenNeeded(); - } else { - root.setChildrenUnneeded(); - for (int c = 0; c < childCount; c++) { - const Child &dchild = children.at(c); - rc.push_back(WatchData()); - WatchData &wchild = rc.back(); - wchild.source = source; - wchild.iname = iname; - // Name can be empty for array-like things - const QString iname = dchild.name.isEmpty() ? QString::number(c) : dchild.name; - // Use key entry as name (which is used for map nodes) - if (dchild.key.isEmpty()) { - wchild.name = iname; - } else { - // Do not use map keys as iname since they might contain quotes. - wchild.name = decodeData(dchild.key, dchild.keyEncoded); - if (wchild.name.size() > 13) { - wchild.name.truncate(12); - wchild.name += QLatin1String("..."); - } - } - // Append iname to total iname. - wchild.iname += dot; - wchild.iname += iname; - wchild.exp = dchild.exp; - if (dchild.valueEncountered) { - wchild.valueEnabled = dchild.valueEnabled; - wchild.setValue(decodeData(dchild.value, dchild.valueEncoded)); - } - wchild.setAddress(dchild.address); - // The type setter sets hasChildren for known types. - wchild.setType(dchild.type.isEmpty() ? childType : dchild.type); - if (!dchild.displayedType.isEmpty()) - wchild.displayedType = dchild.displayedType; - // Child overrides. - const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount; - switch (effectiveChildChildCount) { - case -1: // In this case, trust WatchData::setType(). - break; - case 0: - wchild.setHasChildren(false); - break; - default: - wchild.setHasChildren(true); - wchild.setChildrenNeeded(); - break; - } - } - } - if (debug) { - QDebug nospace = qDebug().nospace(); - nospace << "QtDumperResult::toWatchData" << *this << '\n'; - foreach(const WatchData &wd, rc) - nospace << " " << wd.toString() << '\n'; - } - - return rc; -} - -QDebug operator<<(QDebug in, const QtDumperResult &d) -{ - QDebug nospace = in.nospace(); - nospace << " iname=" << d.iname << " type=" << d.type - << " displayed=" << d.displayedType - << " address=" << d.address; - if (!d.addressInfo.isEmpty()) - nospace << " addressInfo=" << d.addressInfo; - if (d.valueEncountered) { - nospace << " encoded=" << d.valueEncoded - << " value=" << d.value - << " enabled=" << d.valueEnabled; - } else { - nospace << " <no value>"; - } - nospace << " childnumchild=" << d.childChildCount - << " internal=" << d.internal - << " extra='" << d.extra << "'\n"; - - const int realChildCount = d.children.size(); - if (d.childCount || realChildCount) { - nospace << "childCount=" << d.childCount << '/' << realChildCount - << " childType=" << d.childType << '\n'; - for (int i = 0; i < realChildCount; i++) { - const QtDumperResult::Child &c = d.children.at(i); - nospace << " #" << i << " addr=" << c.address - << " enabled=" << c.valueEnabled - << " type=" << c.type << " exp=" << c.exp - << " name=" << c.name; - if (!c.key.isEmpty()) - nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key; - if (c.valueEncountered) { - nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value; - } else { - nospace << " <no value>"; - } - nospace << "childcount=" << c.childCount << '\n'; - } - } - return in; -} - // ----------------- QtDumperHelper::TypeData QtDumperHelper::TypeData::TypeData() : type(UnknownType), @@ -1049,6 +883,9 @@ void QtDumperHelper::setQClassPrefixes(const QString &qNamespace) m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer"); m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer"); m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer"); + m_qListPrefix = qClassName(qNamespace, "QList"); + m_qLinkedListPrefix = qClassName(qNamespace, "QLinkedList"); + m_qVectorPrefix = qClassName(qNamespace, "QVector"); } static inline double getDumperVersion(const GdbMi &contents) @@ -1205,6 +1042,14 @@ QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &t return QSharedDataPointerSize; if (typeName.startsWith(m_qWeakPointerPrefix)) return QWeakPointerSize; + if (typeName.startsWith(m_qListPrefix)) + return QListSize; + if (typeName.startsWith(m_qLinkedListPrefix)) + return QLinkedListSize; + if (typeName.startsWith(m_qVectorPrefix)) + return QVectorSize; + if (typeName.startsWith(m_qQueuePrefix)) + return QQueueSize; return SpecialSizeCount; } @@ -1393,233 +1238,173 @@ void QtDumperHelper::evaluationParameters(const WatchData &data, qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs; } -/* Parse value: - * "iname="local.sl",addr="0x0012BA84",value="<3 items>",valuedisabled="true", - * numchild="3",childtype="QString",childnumchild="0", - * children=[{name="0",value="<binhex>",valueencoded="2"}, - * {name="1",value="dAB3AG8A",valueencoded="2"}, - * {name="2",value="dABoAHIAZQBlAA==",valueencoded="2"}]" */ +// GdbMi parsing helpers for parsing dumper value results -class ValueDumperParser : public DumperParser -{ -public: - explicit ValueDumperParser(const char *s); - - inline QtDumperResult result() const { return m_result; } - -protected: - virtual bool handleKeyword(const char *k, int size); - virtual bool handleHashStart(); - virtual bool handleValue(const char *k, int size); - -private: - enum Mode { None, ExpectingIName, ExpectingAddress, ExpectingValue, - ExpectingType, ExpectingDisplayedType, ExpectingInternal, - ExpectingValueEnabled, ExpectingValueEncoded, - ExpectingCommonChildType, ExpectingChildCount, - ExpectingChildChildOverrideCount, - ExpectingExtra, - IgnoreNext, - ChildModeStart, - ExpectingChildren,ExpectingChildName, ExpectingChildAddress, - ExpectingChildExpression, ExpectingChildType, - ExpectingChildDisplayedType, - ExpectingChildKey, ExpectingChildKeyEncoded, - ExpectingChildValue, ExpectingChildValueEncoded, - ExpectingChildValueEnabled, ExpectingChildChildCount, - IgnoreNextChildMode - }; - - static inline Mode nextMode(Mode in, const char *keyword, int size); - - Mode m_mode; - QtDumperResult m_result; -}; - -ValueDumperParser::ValueDumperParser(const char *s) : - DumperParser(s), - m_mode(None) +static bool gdbMiGetIntValue(int *target, + const GdbMi &node, + const char *child) { + *target = -1; + const GdbMi childNode = node.findChild(child); + if (!childNode.isValid()) + return false; + bool ok; + *target = childNode.data().toInt(&ok); + return ok; } -// Check key words -ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword, int size) +// Find a string child node and assign value if it exists. +// Optionally decode. +static bool gdbMiGetStringValue(QString *target, + const GdbMi &node, + const char *child, + const char *encodingChild = 0) { - // Careful with same prefix - switch (size) { - case 3: - if (!qstrncmp(keyword, "exp", size)) - return ExpectingChildExpression; - if (!qstrncmp(keyword, "key", size)) - return ExpectingChildKey; - break; - case 4: - if (!qstrncmp(keyword, "addr", size)) - return in > ChildModeStart ? ExpectingChildAddress : ExpectingAddress; - if (!qstrncmp(keyword, "type", size)) - return in > ChildModeStart ? ExpectingChildType : ExpectingType; - if (!qstrncmp(keyword, "name", size)) - return ExpectingChildName; - break; - case 5: - if (!qstrncmp(keyword, "iname", size)) - return ExpectingIName; - if (!qstrncmp(keyword, "value", size)) - return in > ChildModeStart ? ExpectingChildValue : ExpectingValue; - if (!qstrncmp(keyword, "extra", size)) - return ExpectingExtra; - break; - case 8: - if (!qstrncmp(keyword, "children", size)) - return ExpectingChildren; - if (!qstrncmp(keyword, "numchild", size)) - return in > ChildModeStart ? ExpectingChildChildCount : ExpectingChildCount; - if (!qstrncmp(keyword, "internal", size)) - return ExpectingInternal; - break; - case 9: - if (!qstrncmp(keyword, "childtype", size)) - return ExpectingCommonChildType; - break; - case 10: - if (!qstrncmp(keyword, "keyencoded", size)) - return ExpectingChildKeyEncoded; - break; - case 12: - if (!qstrncmp(keyword, "valueencoded", size)) - return in > ChildModeStart ? ExpectingChildValueEncoded : ExpectingValueEncoded; - break; - case 13: - if (!qstrncmp(keyword, "valueenabled", size)) - return in > ChildModeStart ? ExpectingChildValueEnabled : ExpectingValueEnabled; - if (!qstrncmp(keyword, "displayedtype", size)) - return in > ChildModeStart ? ExpectingChildDisplayedType : ExpectingDisplayedType; - if (!qstrncmp(keyword, "childnumchild", size)) - return ExpectingChildChildOverrideCount; - break; + target->clear(); + const GdbMi childNode = node.findChild(child); + if (!childNode.isValid()) + return false; + // Encoded data + if (encodingChild) { + int encoding; + if (!gdbMiGetIntValue(&encoding, node, encodingChild)) + encoding = 0; + *target = decodeData(childNode.data(), encoding); + return true; } - return in > ChildModeStart ? IgnoreNextChildMode : IgnoreNext; -} - -bool ValueDumperParser::handleKeyword(const char *k, int size) -{ - const Mode newMode = nextMode(m_mode, k, size); - if (debug && newMode == IgnoreNext) - qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData()); - m_mode = newMode; + // Plain data + *target = QLatin1String(childNode.data()); return true; } -bool ValueDumperParser::handleHashStart() +static bool gdbMiGetBoolValue(bool *target, + const GdbMi &node, + const char *child) { - m_result.children.push_back(QtDumperResult::Child()); + *target = false; + const GdbMi childNode = node.findChild(child); + if (!childNode.isValid()) + return false; + *target = childNode.data() == "true"; return true; } -bool ValueDumperParser::handleValue(const char *k, int size) +/* Context to store parameters that influence the next level children. + * (next level only, it is not further inherited). For example, the root item + * can provide a "childtype" node that specifies the type of the children. */ + +struct GdbMiRecursionContext { + GdbMiRecursionContext(int recursionLevelIn = 0) : + recursionLevel(recursionLevelIn), childNumChild(-1), childIndex(0) {} + + int recursionLevel; + int childNumChild; + int childIndex; + QString childType; + QString parentIName; +}; + +static void gbdMiToWatchData(const GdbMi &root, + const GdbMiRecursionContext &ctx, + QList<WatchData> *wl) { - const QByteArray valueBA(k, size); - switch (m_mode) { - case None: - case ChildModeStart: - return false; - case ExpectingIName: - m_result.iname = QString::fromLatin1(valueBA); - break; - case ExpectingAddress: { - const QString address = QString::fromLatin1(valueBA); - if (address.startsWith(QLatin1String("0x"))) { - m_result.address = address; - } else { - m_result.addressInfo = address; - } - } - break; - case ExpectingValue: - m_result.valueEncountered = true; - m_result.value = valueBA; - break; - case ExpectingValueEnabled: - m_result.valueEnabled = valueBA == "true"; - break; - case ExpectingValueEncoded: - m_result.valueEncoded = QString::fromLatin1(valueBA).toInt(); - break; - case ExpectingType: - m_result.type = QString::fromLatin1(valueBA); - break; - case ExpectingDisplayedType: - m_result.displayedType = QString::fromLatin1(valueBA); - break; - case ExpectingExtra: - m_result.extra = valueBA; - break; - case ExpectingInternal: - m_result.internal = valueBA == "true"; - break; - case ExpectingCommonChildType: - m_result.childType = QString::fromLatin1(valueBA); - break; - case ExpectingChildCount: - m_result.childCount = QString::fromLatin1(valueBA).toInt(); - break; - case ExpectingChildChildOverrideCount: - m_result.childChildCount = QString::fromLatin1(valueBA).toInt(); - break; - case ExpectingChildren: - case IgnoreNextChildMode: - case IgnoreNext: - break; - case ExpectingChildName: - m_result.children.back().name = QString::fromLatin1(valueBA); - break; - case ExpectingChildAddress: - m_result.children.back().address = QString::fromLatin1(valueBA); - break; - case ExpectingChildKeyEncoded: - m_result.children.back().keyEncoded = QString::fromLatin1(valueBA).toInt(); - break; - case ExpectingChildKey: - m_result.children.back().key = valueBA; - break; - case ExpectingChildValue: - m_result.children.back().valueEncountered = true; - m_result.children.back().value = valueBA; - break; - case ExpectingChildExpression: - m_result.children.back().exp = QString::fromLatin1(valueBA); - break; - case ExpectingChildValueEncoded: - m_result.children.back().valueEncoded = QString::fromLatin1(valueBA).toInt(); - break; - case ExpectingChildValueEnabled: - m_result.children.back().valueEnabled = valueBA == "true"; - break; - case ExpectingChildType: - m_result.children.back().type = QString::fromLatin1(valueBA); - break; - case ExpectingChildDisplayedType: - m_result.children.back().displayedType = QString::fromLatin1(valueBA); - break; - case ExpectingChildChildCount: - m_result.children.back().childCount = QString::fromLatin1(valueBA).toInt(); - break; + if (debug > 1) + qDebug() << Q_FUNC_INFO << '\n' << root.toString(false, 0); + WatchData w; + QString v; + // Check for name/iname and use as expression default + if (ctx.recursionLevel == 0) { + // parents have only iname, from which name is derived + if (!gdbMiGetStringValue(&w.iname, root, "iname")) + qWarning("Internal error: iname missing"); + w.name = w.iname; + const int lastDotPos = w.name.lastIndexOf(QLatin1Char('.')); + if (lastDotPos != -1) + w.name.remove(0, lastDotPos + 1); + w.exp = w.name; + } 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 += QLatin1Char('.'); + w.iname += w.name; + // Key? + QString key; + if (gdbMiGetStringValue(&key, root, "key", "keyencoded")) { + w.name = key.size() > 13 ? key.mid(0, 13) + QLatin1String("...") : key; + } + } + if (w.name.isEmpty()) { + const QString msg = QString::fromLatin1("Internal error: Unable to determine name at level %1/%2 for %3").arg(ctx.recursionLevel).arg(w.iname, QLatin1String(root.toString(true, 2))); + qWarning("%s\n", qPrintable(msg)); + } + gdbMiGetStringValue(&w.displayedType, root, "displayedtype"); + if (gdbMiGetStringValue(&v, root, "editvalue")) + w.editvalue = v.toLatin1(); + if (gdbMiGetStringValue(&v, root, "exp")) + w.exp = v; + gdbMiGetStringValue(&w.addr, root, "addr"); + gdbMiGetStringValue(&w.saddr, root, "saddr"); + gdbMiGetBoolValue(&w.valueEnabled, root, "valueenabled"); + gdbMiGetBoolValue(&w.valueEditable, root, "valueeditable"); + if (gdbMiGetStringValue(&v, root, "valuetooltip", "valuetooltipencoded")) + w.setValue(v); + if (gdbMiGetStringValue(&v, root, "value", "valueencoded")) + w.setValue(v); + // Type from context or self + if (ctx.childType.isEmpty()) { + if (gdbMiGetStringValue(&v, root, "type")) + w.setType(v); + } else { + w.setType(ctx.childType); + } + // child count? + int numChild = -1; + if (ctx.childNumChild >= 0) { + numChild = ctx.childNumChild; + } else { + gdbMiGetIntValue(&numChild, root, "numchild"); + } + if (numChild >= 0) + w.setHasChildren(numChild > 0); + wl->push_back(w); + // Parse children with a new context + if (numChild == 0) + return; + const GdbMi childrenNode = root.findChild("children"); + if (!childrenNode.isValid()) + return; + const QList<GdbMi> children =childrenNode.children(); + if (children.empty()) + return; + wl->back().setChildrenUnneeded(); + GdbMiRecursionContext nextLevelContext(ctx.recursionLevel + 1); + nextLevelContext.parentIName = w.iname; + gdbMiGetStringValue(&nextLevelContext.childType, root, "childtype"); + if (!gdbMiGetIntValue(&nextLevelContext.childNumChild, root, "childnumchild")) + nextLevelContext.childNumChild = -1; + foreach(const GdbMi &child, children) { + gbdMiToWatchData(child, nextLevelContext, wl); + nextLevelContext.childIndex++; } - return true; } -bool QtDumperHelper::parseValue(const char *data, QtDumperResult *r) +bool QtDumperHelper::parseValue(const char *data, + QList<WatchData> *l) { - ValueDumperParser parser(data); - - if (!parser.run()) + l->clear(); + QByteArray fullData = data; + fullData.insert(0, '{'); + fullData.append(data); + fullData.append('}'); + GdbMi root(fullData); + if (!root.isValid()) return false; - *r = parser.result(); - // Sanity - if (!r->children.empty() && r->childCount != r->children.size()) - r->childCount = r->children.size(); - if (debug > 1) - qDebug() << '\n' << data << '\n' << *r; + gbdMiToWatchData(root, GdbMiRecursionContext(), l); return true; } diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index b9a79590b60..9c4280b0dfe 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -90,49 +90,6 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos, // Decode string data as returned by the dumper helpers. QString decodeData(const QByteArray &baIn, int encoding); -// Result of a dumper call. -struct QtDumperResult -{ - struct Child { - Child(); - - int keyEncoded; - int valueEncoded; - int childCount; - bool valueEnabled; - QString name; - QString address; - QString exp; - QString type; - QString displayedType; - QByteArray key; - bool valueEncountered; - QByteArray value; - }; - - QtDumperResult(); - void clear(); - QList<WatchData> toWatchData(int source = 0) const; - - QString iname; - QString address; - QString addressInfo; // "<synthetic>" or such, in the 2nd adress field. - QString type; - QString extra; - QString displayedType; - bool valueEncountered; - QByteArray value; - int valueEncoded; - bool valueEnabled; - int childCount; - bool internal; - QString childType; - int childChildCount; - QList <Child> children; -}; - -QDebug operator<<(QDebug in, const QtDumperResult &d); - /* Attempt to put common code of the dumper handling into a helper * class. * "Custom dumper" is a library compiled against the current @@ -210,7 +167,7 @@ public: // Parse the value response (protocol 2) from debuggee buffer. // 'data' excludes the leading indicator character. - static bool parseValue(const char *data, QtDumperResult *r); + static bool parseValue(const char *data, QList<WatchData> *l); // What kind of debugger expressions are required to dump that type. // A debugger with restricted expression syntax can handle @@ -245,7 +202,9 @@ private: // They are not complete (std::allocator<X>). enum SpecialSizeType { IntSize, PointerSize, StdAllocatorSize, QSharedPointerSize, QSharedDataPointerSize, - QWeakPointerSize, QPointerSize, SpecialSizeCount }; + QWeakPointerSize, QPointerSize, + QListSize, QLinkedListSize, QVectorSize, QQueueSize, + SpecialSizeCount }; // Resolve name to enumeration or SpecialSizeCount (invalid) SpecialSizeType specialSizeType(const QString &t) const; @@ -263,6 +222,10 @@ private: QString m_qSharedPointerPrefix; QString m_qSharedDataPointerPrefix; QString m_qWeakPointerPrefix; + QString m_qListPrefix; + QString m_qLinkedListPrefix; + QString m_qVectorPrefix; + QString m_qQueuePrefix; }; QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d); -- GitLab