aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/debugger/watchutils.cpp
diff options
context:
space:
mode:
authorFriedemann Kleint <[email protected]>2009-09-21 14:55:39 +0200
committerFriedemann Kleint <[email protected]>2009-09-21 14:55:39 +0200
commitcaa164a4ab7094fa134031db1a04c1c469542f63 (patch)
tree24c5c661dd842febd3c3100c196c2f12f2042144 /src/plugins/debugger/watchutils.cpp
parentde73a6d6dd7bad5ffeae224f41bc8dd6bfaa96d3 (diff)
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> > .
Diffstat (limited to 'src/plugins/debugger/watchutils.cpp')
-rw-r--r--src/plugins/debugger/watchutils.cpp533
1 files changed, 159 insertions, 374 deletions
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;
}