diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index 3c9f4558fe1bed2018c55100417350af8fbbe303..580e49c6fb465f66c3b2842e1ba9fcad85cfd51e 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -27,7 +27,6 @@ HEADERS += breakhandler.h \ debuggerrunner.h \ debuggertooltip.h \ debuggerstringutils.h \ - watchutils.h \ idebuggerengine.h \ moduleshandler.h \ moduleswindow.h \ @@ -42,6 +41,7 @@ HEADERS += breakhandler.h \ sourcefileswindow.h \ threadswindow.h \ watchhandler.h \ + watchutils.h \ watchwindow.h \ name_demangler.h \ debuggeruiswitcher.h \ @@ -57,7 +57,6 @@ SOURCES += breakhandler.cpp \ debuggerplugin.cpp \ debuggerrunner.cpp \ debuggertooltip.cpp \ - watchutils.cpp \ moduleshandler.cpp \ moduleswindow.cpp \ outputcollector.cpp \ @@ -70,8 +69,10 @@ SOURCES += breakhandler.cpp \ stackwindow.cpp \ sourcefileswindow.cpp \ threadswindow.cpp \ + watchdata.cpp \ watchhandler.cpp \ watchwindow.cpp \ + watchutils.cpp \ name_demangler.cpp \ debuggeruiswitcher.cpp \ debuggermainwindow.cpp diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp index 9baabf0923e7f7a7db32ede95802edb7896aa302..67f3a0d6f36ae44841c02eb046dbedf028e82b56 100644 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ b/src/plugins/debugger/gdb/classicgdbengine.cpp @@ -418,7 +418,8 @@ void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response) setWatchDataType(data, response.data.findChild("type")); setWatchDataDisplayedType(data, response.data.findChild("displaytype")); QList<WatchData> list; - handleChildren(data, contents, &list); + parseWatchData(manager()->watchHandler()->expandedINames(), + data, contents, &list); //for (int i = 0; i != list.size(); ++i) // qDebug() << "READ: " << list.at(i).toString(); manager()->watchHandler()->insertBulkData(list); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 0f8f645404e2b378ee86501bdd1ccbad3d090ea8..7432c4601f1ce7f5dab58f898a414542cf6b3f2c 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -251,7 +251,6 @@ void GdbEngine::initializeVariables() m_fullToShortName.clear(); m_shortToFullName.clear(); - m_varToType.clear(); invalidateSourcesList(); m_sourcesListUpdating = false; @@ -3236,65 +3235,6 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, // ////////////////////////////////////////////////////////////////////// -void GdbEngine::setWatchDataValue(WatchData &data, const GdbMi &item) -{ - GdbMi value = item.findChild("value"); - if (value.isValid()) { - int encoding = item.findChild("valueencoded").data().toInt(); - data.setValue(decodeData(value.data(), encoding)); - } else { - data.setValueNeeded(); - } -} - -void GdbEngine::setWatchDataValueToolTip(WatchData &data, const GdbMi &mi, - int encoding) -{ - if (mi.isValid()) - data.setValueToolTip(decodeData(mi.data(), encoding)); -} - -void GdbEngine::setWatchDataChildCount(WatchData &data, const GdbMi &mi) -{ - if (mi.isValid()) - data.setHasChildren(mi.data().toInt() > 0); -} - -void GdbEngine::setWatchDataValueEnabled(WatchData &data, const GdbMi &mi) -{ - if (mi.data() == "true") - data.valueEnabled = true; - else if (mi.data() == "false") - data.valueEnabled = false; -} - -void GdbEngine::setWatchDataValueEditable(WatchData &data, const GdbMi &mi) -{ - if (mi.data() == "true") - data.valueEditable = true; - else if (mi.data() == "false") - data.valueEditable = false; -} - -void GdbEngine::setWatchDataExpression(WatchData &data, const GdbMi &mi) -{ - if (mi.isValid()) - data.exp = mi.data(); -} - -void GdbEngine::setWatchDataAddress(WatchData &data, const GdbMi &mi) -{ - if (mi.isValid()) - setWatchDataAddressHelper(data, mi.data()); -} - -void GdbEngine::setWatchDataAddressHelper(WatchData &data, const QByteArray &addr) -{ - data.addr = addr; - if (data.exp.isEmpty() && !data.addr.startsWith("$")) - data.exp = "*(" + gdbQuoteTypes(data.type).toLatin1() + "*)" + data.addr; -} - void GdbEngine::setAutoDerefPointers(const QVariant &on) { Q_UNUSED(on) @@ -3443,25 +3383,6 @@ void GdbEngine::handleVarAssign(const GdbResponse &) updateLocals(); } -// Find the "type" and "displayedtype" children of root and set up type. -void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &item) -{ - if (item.isValid()) { - const QString miData = _(item.data()); - if (!data.framekey.isEmpty()) - m_varToType[data.framekey] = miData; - data.setType(miData); - } else if (data.type.isEmpty()) { - data.setTypeNeeded(); - } -} - -void GdbEngine::setWatchDataDisplayedType(WatchData &data, const GdbMi &item) -{ - if (item.isValid()) - data.displayedType = _(item.data()); -} - void GdbEngine::handleVarCreate(const GdbResponse &response) { WatchData data = response.cookie.value<WatchData>(); @@ -3502,84 +3423,6 @@ void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response) } } -void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item, - QList<WatchData> *list) -{ - //qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString(); - WatchData data = data0; - if (!manager()->watchHandler()->isExpandedIName(data.iname)) - data.setChildrenUnneeded(); - - GdbMi children = item.findChild("children"); - if (children.isValid() || !manager()->watchHandler()->isExpandedIName(data.iname)) - data.setChildrenUnneeded(); - - setWatchDataType(data, item.findChild("type")); - GdbMi mi = item.findChild("editvalue"); - if (mi.isValid()) - data.editvalue = mi.data(); - mi = item.findChild("editformat"); - if (mi.isValid()) - data.editformat = mi.data().toInt(); - mi = item.findChild("typeformats"); - if (mi.isValid()) - data.typeFormats = QString::fromUtf8(mi.data()); - - setWatchDataValue(data, item); - setWatchDataAddress(data, item.findChild("addr")); - setWatchDataExpression(data, item.findChild("exp")); - setWatchDataValueEnabled(data, item.findChild("valueenabled")); - setWatchDataValueEditable(data, item.findChild("valueeditable")); - setWatchDataChildCount(data, item.findChild("numchild")); - //qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n"; - list->append(data); - - bool ok = false; - qulonglong addressBase = item.findChild("addrbase").data().toULongLong(&ok, 0); - qulonglong addressStep = item.findChild("addrstep").data().toULongLong(); - - // Try not to repeat data too often. - WatchData childtemplate; - setWatchDataType(childtemplate, item.findChild("childtype")); - setWatchDataChildCount(childtemplate, item.findChild("childnumchild")); - //qDebug() << "CHILD TEMPLATE:" << childtemplate.toString(); - - int i = 0; - foreach (const GdbMi &child, children.children()) { - WatchData data1 = childtemplate; - GdbMi name = child.findChild("name"); - if (name.isValid()) - data1.name = _(name.data()); - else - data1.name = QString::number(i); - GdbMi iname = child.findChild("iname"); - if (iname.isValid()) - data1.iname = iname.data(); - else - data1.iname = data.iname + '.' + data1.name.toLatin1(); - if (!data1.name.isEmpty() && data1.name.at(0).isDigit()) - data1.name = _c('[') + data1.name + _c(']'); - if (addressStep) { - const QByteArray addr = "0x" + QByteArray::number(addressBase, 16); - setWatchDataAddressHelper(data1, addr); - addressBase += addressStep; - } - QByteArray key = child.findChild("key").data(); - if (!key.isEmpty()) { - int encoding = child.findChild("keyencoded").data().toInt(); - QString skey = decodeData(key, encoding); - if (skey.size() > 13) { - skey = skey.left(12); - skey += _("..."); - } - //data1.name += " (" + skey + ")"; - data1.name = skey; - } - handleChildren(data1, child, list); - ++i; - } -} - void GdbEngine::updateLocals(const QVariant &cookie) { m_pendingWatchRequests = 0; diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 345e1c23cd67282bc2cd3ee487c6837b2321f145..c1bc7b57f0e5fe147fdef0a27d45a54ce1aa36d9 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -459,8 +459,6 @@ private: ////////// View & Data Stuff ////////// // FIXME: BaseClass. called to improve situation for a watch item void updateSubItemClassic(const WatchData &data); - void handleChildren(const WatchData &parent, const GdbMi &child, - QList<WatchData> *insertions); void virtual updateWatchData(const WatchData &data); Q_SLOT void updateWatchDataHelper(const WatchData &data); @@ -503,11 +501,8 @@ private: ////////// View & Data Stuff ////////// QMap<QByteArray, int> *seen); void setLocals(const QList<GdbMi> &locals); void handleStackListArgumentsClassic(const GdbResponse &response); - void setWatchDataType(WatchData &data, const GdbMi &mi); - void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi); QSet<QByteArray> m_processedNames; - QMap<QString, QString> m_varToType; private: ////////// Dumper Management ////////// QString qtDumperLibraryName() const; @@ -538,16 +533,6 @@ private: ////////// Convenience Functions ////////// static QString m_toolTipExpression; static QPoint m_toolTipPos; static QByteArray tooltipINameForExpression(const QByteArray &exp); - - static void setWatchDataValue(WatchData &data, const GdbMi &item); - static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi, - int encoding = 0); - static void setWatchDataChildCount(WatchData &data, const GdbMi &mi); - static void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi); - static void setWatchDataValueEditable(WatchData &data, const GdbMi &mi); - static void setWatchDataExpression(WatchData &data, const GdbMi &mi); - static void setWatchDataAddress(WatchData &data, const GdbMi &mi); - static void setWatchDataAddressHelper(WatchData &data, const QByteArray &addr); }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp index 3bb164365bd99573e24ea6a749df9d2cb2b80fe3..112b172d73f888b5108facee359367f3e53fea52 100644 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ b/src/plugins/debugger/gdb/pythongdbengine.cpp @@ -125,14 +125,15 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) GdbMi data = all.findChild("data"); QList<WatchData> list; + WatchHandler *watchHandler = manager()->watchHandler(); foreach (const GdbMi &child, data.children()) { WatchData dummy; dummy.iname = child.findChild("iname").data(); dummy.name = _(child.findChild("name").data()); //qDebug() << "CHILD: " << child.toString(); - handleChildren(dummy, child, &list); + parseWatchData(watchHandler->expandedINames(), dummy, child, &list); } - manager()->watchHandler()->insertBulkData(list); + watchHandler->insertBulkData(list); //for (int i = 0; i != list.size(); ++i) // qDebug() << "LOCAL: " << list.at(i).toString(); diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d2f8248a977f6b15010fcf0536d15cf51f45073 --- /dev/null +++ b/src/plugins/debugger/watchdata.cpp @@ -0,0 +1,265 @@ + +#include "watchdata.h" +#include "watchutils.h" + +#include <QtCore/QTextStream> +#include <QtCore/QDebug> + +#include <QtGui/QApplication> +#include <QtGui/QTextDocument> // Qt::escape + +//////////////////////////////////////////////////////////////////// +// +// WatchData +// +//////////////////////////////////////////////////////////////////// + +namespace Debugger { +namespace Internal { + +WatchData::WatchData() : + editformat(0), + hasChildren(false), + generation(-1), + valueEnabled(true), + valueEditable(true), + error(false), + source(0), + state(InitialState), + changed(false) +{ +} + +bool WatchData::isEqual(const WatchData &other) const +{ + return iname == other.iname + && exp == other.exp + && name == other.name + && value == other.value + && editvalue == other.editvalue + && valuetooltip == other.valuetooltip + && type == other.type + && displayedType == other.displayedType + && variable == other.variable + && addr == other.addr + && framekey == other.framekey + && hasChildren == other.hasChildren + && valueEnabled == other.valueEnabled + && valueEditable == other.valueEditable + && error == other.error; +} + +void WatchData::setError(const QString &msg) +{ + setAllUnneeded(); + value = msg; + setHasChildren(false); + valueEnabled = false; + valueEditable = false; + error = true; +} + +void WatchData::setValue(const QString &value0) +{ + value = value0; + if (value == "{...}") { + value.clear(); + hasChildren = true; // at least one... + } + // strip off quoted characters for chars. + if (value.endsWith(QLatin1Char('\'')) && type.endsWith(QLatin1String("char"))) { + const int blankPos = value.indexOf(QLatin1Char(' ')); + if (blankPos != -1) + value.truncate(blankPos); + } + + // avoid duplicated information + if (value.startsWith(QLatin1Char('(')) && value.contains(") 0x")) + value = value.mid(value.lastIndexOf(") 0x") + 2); + + // doubles are sometimes displayed as "@0x6141378: 1.2". + // I don't want that. + if (/*isIntOrFloatType(type) && */ value.startsWith("@0x") + && value.contains(':')) { + value = value.mid(value.indexOf(':') + 2); + setHasChildren(false); + } + + // "numchild" is sometimes lying + //MODEL_DEBUG("\n\n\nPOINTER: " << type << value); + if (isPointerType(type)) + setHasChildren(value != "0x0" && value != "<null>" + && !isCharPointerType(type)); + + // pointer type information is available in the 'type' + // column. No need to duplicate it here. + if (value.startsWith(QLatin1Char('(') + type + ") 0x")) + value = value.section(QLatin1Char(' '), -1, -1); + + setValueUnneeded(); +} + +void WatchData::setValueToolTip(const QString &tooltip) +{ + valuetooltip = tooltip; +} + +void WatchData::setType(const QString &str, bool guessChildrenFromType) +{ + type = str.trimmed(); + bool changed = true; + while (changed) { + if (type.endsWith(QLatin1String("const"))) + type.chop(5); + else if (type.endsWith(QLatin1Char(' '))) + type.chop(1); + else if (type.endsWith(QLatin1Char('&'))) + type.chop(1); + else if (type.startsWith(QLatin1String("const "))) + type = type.mid(6); + else if (type.startsWith(QLatin1String("volatile "))) + type = type.mid(9); + else if (type.startsWith(QLatin1String("class "))) + type = type.mid(6); + else if (type.startsWith(QLatin1String("struct "))) + type = type.mid(6); + else if (type.startsWith(QLatin1Char(' '))) + type = type.mid(1); + else + changed = false; + } + setTypeUnneeded(); + if (guessChildrenFromType) { + switch (guessChildren(type)) { + case HasChildren: + setHasChildren(true); + break; + case HasNoChildren: + setHasChildren(false); + break; + case HasPossiblyChildren: + setHasChildren(true); // FIXME: bold assumption + break; + } + } +} + +void WatchData::setAddress(const QByteArray &a) +{ + addr = a; +} + +QString WatchData::toString() const +{ + const char *doubleQuoteComma = "\","; + QString res; + QTextStream str(&res); + str << QLatin1Char('{'); + if (!iname.isEmpty()) + str << "iname=\"" << iname << doubleQuoteComma; + if (!name.isEmpty() && name != iname) + str << "name=\"" << name << doubleQuoteComma; + if (error) + str << "error,"; + if (!addr.isEmpty()) + str << "addr=\"" << addr << doubleQuoteComma; + if (!exp.isEmpty()) + str << "exp=\"" << exp << doubleQuoteComma; + + if (!variable.isEmpty()) + str << "variable=\"" << variable << doubleQuoteComma; + + if (isValueNeeded()) + str << "value=<needed>,"; + if (isValueKnown() && !value.isEmpty()) + str << "value=\"" << value << doubleQuoteComma; + + if (!editvalue.isEmpty()) + str << "editvalue=\"<...>\","; + // str << "editvalue=\"" << editvalue << doubleQuoteComma; + + if (isTypeNeeded()) + str << "type=<needed>,"; + if (isTypeKnown() && !type.isEmpty()) + str << "type=\"" << type << doubleQuoteComma; + + if (isHasChildrenNeeded()) + str << "hasChildren=<needed>,"; + if (isHasChildrenKnown()) + str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma; + + if (isChildrenNeeded()) + str << "children=<needed>,"; + if (source) + str << "source=" << source; + str.flush(); + if (res.endsWith(QLatin1Char(','))) + res.truncate(res.size() - 1); + return res + QLatin1Char('}'); +} + +// Format a tooltip fow with aligned colon. +static void formatToolTipRow(QTextStream &str, + const QString &category, const QString &value) +{ + str << "<tr><td>" << category << "</td><td> : </td><td>" + << Qt::escape(value) << "</td></tr>"; +} + +static QString typeToolTip(const WatchData &wd) +{ + if (wd.displayedType.isEmpty()) + return wd.type; + QString rc = wd.displayedType; + rc += QLatin1String(" ("); + rc += wd.type; + rc += QLatin1Char(')'); + return rc; +} + +QString WatchData::toToolTip() const +{ + if (!valuetooltip.isEmpty()) + return QString::number(valuetooltip.size()); + QString res; + QTextStream str(&res); + str << "<html><body><table>"; + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Name"), name); + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Expression"), exp); + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Type"), typeToolTip(*this)); + QString val = value; + if (value.size() > 1000) { + val.truncate(1000); + val += QCoreApplication::translate("Debugger::Internal::WatchHandler", " ... <cut off>"); + } + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Value"), val); + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Object Address"), addr); + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Internal ID"), iname); + formatToolTipRow(str, QCoreApplication::translate("Debugger::Internal::WatchHandler", "Generation"), + QString::number(generation)); + str << "</table></body></html>"; + return res; +} + +QString WatchData::msgNotInScope() +{ + static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>"); + return rc; +} + +const QString &WatchData::shadowedNameFormat() +{ + static const QString format = QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>"); + return format; +} + +QString WatchData::shadowedName(const QString &name, int seen) +{ + if (seen <= 0) + return name; + return shadowedNameFormat().arg(name, seen); +} + +} // namespace Internal +} // namespace Debugger + diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h new file mode 100644 index 0000000000000000000000000000000000000000..f8062324ef25eaf4ae2385582dedadcf069b9bac --- /dev/null +++ b/src/plugins/debugger/watchdata.h @@ -0,0 +1,142 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGGER_WATCHDATA_H +#define DEBUGGER_WATCHDATA_H + +#include <QtCore/QMetaType> +#include <QtCore/QtGlobal> +#include <QtCore/QObject> +#include <QtScript/QScriptValue> + +namespace Debugger { +namespace Internal { + +class WatchData +{ +public: + WatchData(); + + enum State + { + Complete = 0, + HasChildrenNeeded = 1, + ValueNeeded = 2, + TypeNeeded = 4, + ChildrenNeeded = 8, + + NeededMask = ValueNeeded + | TypeNeeded + | ChildrenNeeded + | HasChildrenNeeded, + + InitialState = ValueNeeded + | TypeNeeded + | ChildrenNeeded + | HasChildrenNeeded + }; + + void setValue(const QString &); + void setType(const QString &, bool guessChildrenFromType = true); + void setValueToolTip(const QString &); + void setError(const QString &); + void setAddress(const QByteArray &); + + bool isSomethingNeeded() const { return state & NeededMask; } + void setAllNeeded() { state = NeededMask; } + void setAllUnneeded() { state = State(0); } + + bool isTypeNeeded() const { return state & TypeNeeded; } + bool isTypeKnown() const { return !(state & TypeNeeded); } + void setTypeNeeded() { state = State(state | TypeNeeded); } + void setTypeUnneeded() { state = State(state & ~TypeNeeded); } + + bool isValueNeeded() const { return state & ValueNeeded; } + bool isValueKnown() const { return !(state & ValueNeeded); } + void setValueNeeded() { state = State(state | ValueNeeded); } + void setValueUnneeded() { state = State(state & ~ValueNeeded); } + + bool isChildrenNeeded() const { return state & ChildrenNeeded; } + bool isChildrenKnown() const { return !(state & ChildrenNeeded); } + void setChildrenNeeded() { state = State(state | ChildrenNeeded); } + void setChildrenUnneeded() { state = State(state & ~ChildrenNeeded); } + + bool isHasChildrenNeeded() const { return state & HasChildrenNeeded; } + bool isHasChildrenKnown() const { return !(state & HasChildrenNeeded); } + void setHasChildrenNeeded() { state = State(state | HasChildrenNeeded); } + void setHasChildrenUnneeded() { state = State(state & ~HasChildrenNeeded); } + void setHasChildren(bool c) { hasChildren = c; setHasChildrenUnneeded(); + if (!c) setChildrenUnneeded(); } + + QString toString() const; + QString toToolTip() const; + bool isLocal() const { return iname.startsWith("local."); } + bool isWatcher() const { return iname.startsWith("watch."); } + bool isValid() const { return !iname.isEmpty(); } + + bool isEqual(const WatchData &other) const; + + static QString msgNotInScope(); + static QString shadowedName(const QString &name, int seen); + static const QString &shadowedNameFormat(); + +public: + QByteArray iname; // Internal name sth like 'local.baz.public.a' + QByteArray exp; // The expression + QString name; // Displayed name + QString value; // Displayed value + QByteArray editvalue; // Displayed value + int editformat; // Format of displayed value + QString valuetooltip; // Tooltip in value column + QString typeFormats; // Selection of formats of displayed value + QString type; // Type for further processing + QString displayedType;// Displayed type (optional) + QByteArray variable; // Name of internal Gdb variable if created + QByteArray addr; // Displayed address + QString framekey; // Key for type cache + QScriptValue scriptValue; // If needed... + bool hasChildren; + int generation; // When updated? + bool valueEnabled; // Value will be greyed out or not + bool valueEditable; // Value will be editable + bool error; + +public: + int source; // Originated from dumper or symbol evaluation? (CDB only) + int state; + bool changed; +}; + +} // namespace Internal +} // namespace Debugger + +Q_DECLARE_METATYPE(Debugger::Internal::WatchData); + + +#endif // DEBUGGER_WATCHDATA_H diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 8aab91508bb3bcc704487a05e566261c040ce92b..e4a3bb61ab699dd394492e2c3ade4feabcac3ad8 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -103,254 +103,6 @@ public: QList<WatchItem *> children; // fetched children }; -//////////////////////////////////////////////////////////////////// -// -// WatchData -// -//////////////////////////////////////////////////////////////////// - -WatchData::WatchData() : - editformat(0), - hasChildren(false), - generation(-1), - valueEnabled(true), - valueEditable(true), - error(false), - source(0), - state(InitialState), - changed(false) -{ -} - -bool WatchData::isEqual(const WatchData &other) const -{ - return iname == other.iname - && exp == other.exp - && name == other.name - && value == other.value - && editvalue == other.editvalue - && valuetooltip == other.valuetooltip - && type == other.type - && displayedType == other.displayedType - && variable == other.variable - && addr == other.addr - && framekey == other.framekey - && hasChildren == other.hasChildren - && valueEnabled == other.valueEnabled - && valueEditable == other.valueEditable - && error == other.error; -} - -void WatchData::setError(const QString &msg) -{ - setAllUnneeded(); - value = msg; - setHasChildren(false); - valueEnabled = false; - valueEditable = false; - error = true; -} - -void WatchData::setValue(const QString &value0) -{ - value = value0; - if (value == "{...}") { - value.clear(); - hasChildren = true; // at least one... - } - // strip off quoted characters for chars. - if (value.endsWith(QLatin1Char('\'')) && type.endsWith(QLatin1String("char"))) { - const int blankPos = value.indexOf(QLatin1Char(' ')); - if (blankPos != -1) - value.truncate(blankPos); - } - - // avoid duplicated information - if (value.startsWith(QLatin1Char('(')) && value.contains(") 0x")) - value = value.mid(value.lastIndexOf(") 0x") + 2); - - // doubles are sometimes displayed as "@0x6141378: 1.2". - // I don't want that. - if (/*isIntOrFloatType(type) && */ value.startsWith("@0x") - && value.contains(':')) { - value = value.mid(value.indexOf(':') + 2); - setHasChildren(false); - } - - // "numchild" is sometimes lying - //MODEL_DEBUG("\n\n\nPOINTER: " << type << value); - if (isPointerType(type)) - setHasChildren(value != "0x0" && value != "<null>" - && !isCharPointerType(type)); - - // pointer type information is available in the 'type' - // column. No need to duplicate it here. - if (value.startsWith(QLatin1Char('(') + type + ") 0x")) - value = value.section(QLatin1Char(' '), -1, -1); - - setValueUnneeded(); -} - -void WatchData::setValueToolTip(const QString &tooltip) -{ - valuetooltip = tooltip; -} - -void WatchData::setType(const QString &str, bool guessChildrenFromType) -{ - type = str.trimmed(); - bool changed = true; - while (changed) { - if (type.endsWith(QLatin1String("const"))) - type.chop(5); - else if (type.endsWith(QLatin1Char(' '))) - type.chop(1); - else if (type.endsWith(QLatin1Char('&'))) - type.chop(1); - else if (type.startsWith(QLatin1String("const "))) - type = type.mid(6); - else if (type.startsWith(QLatin1String("volatile "))) - type = type.mid(9); - else if (type.startsWith(QLatin1String("class "))) - type = type.mid(6); - else if (type.startsWith(QLatin1String("struct "))) - type = type.mid(6); - else if (type.startsWith(QLatin1Char(' '))) - type = type.mid(1); - else - changed = false; - } - setTypeUnneeded(); - if (guessChildrenFromType) { - switch (guessChildren(type)) { - case HasChildren: - setHasChildren(true); - break; - case HasNoChildren: - setHasChildren(false); - break; - case HasPossiblyChildren: - setHasChildren(true); // FIXME: bold assumption - break; - } - } -} - -void WatchData::setAddress(const QByteArray &a) -{ - addr = a; -} - -QString WatchData::toString() const -{ - const char *doubleQuoteComma = "\","; - QString res; - QTextStream str(&res); - str << QLatin1Char('{'); - if (!iname.isEmpty()) - str << "iname=\"" << iname << doubleQuoteComma; - if (!name.isEmpty() && name != iname) - str << "name=\"" << name << doubleQuoteComma; - if (error) - str << "error,"; - if (!addr.isEmpty()) - str << "addr=\"" << addr << doubleQuoteComma; - if (!exp.isEmpty()) - str << "exp=\"" << exp << doubleQuoteComma; - - if (!variable.isEmpty()) - str << "variable=\"" << variable << doubleQuoteComma; - - if (isValueNeeded()) - str << "value=<needed>,"; - if (isValueKnown() && !value.isEmpty()) - str << "value=\"" << value << doubleQuoteComma; - - if (!editvalue.isEmpty()) - str << "editvalue=\"<...>\","; - // str << "editvalue=\"" << editvalue << doubleQuoteComma; - - if (isTypeNeeded()) - str << "type=<needed>,"; - if (isTypeKnown() && !type.isEmpty()) - str << "type=\"" << type << doubleQuoteComma; - - if (isHasChildrenNeeded()) - str << "hasChildren=<needed>,"; - if (isHasChildrenKnown()) - str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma; - - if (isChildrenNeeded()) - str << "children=<needed>,"; - if (source) - str << "source=" << source; - str.flush(); - if (res.endsWith(QLatin1Char(','))) - res.truncate(res.size() - 1); - return res + QLatin1Char('}'); -} - -// Format a tooltip fow with aligned colon. -static void formatToolTipRow(QTextStream &str, - const QString &category, const QString &value) -{ - str << "<tr><td>" << category << "</td><td> : </td><td>" - << Qt::escape(value) << "</td></tr>"; -} - -static inline QString typeToolTip(const WatchData &wd) -{ - if (wd.displayedType.isEmpty()) - return wd.type; - QString rc = wd.displayedType; - rc += QLatin1String(" ("); - rc += wd.type; - rc += QLatin1Char(')'); - return rc; -} - -QString WatchData::toToolTip() const -{ - if (!valuetooltip.isEmpty()) - return QString::number(valuetooltip.size()); - QString res; - QTextStream str(&res); - str << "<html><body><table>"; - formatToolTipRow(str, WatchHandler::tr("Name"), name); - formatToolTipRow(str, WatchHandler::tr("Expression"), exp); - formatToolTipRow(str, WatchHandler::tr("Type"), typeToolTip(*this)); - QString val = value; - if (value.size() > 1000) { - val.truncate(1000); - val += WatchHandler::tr(" ... <cut off>"); - } - formatToolTipRow(str, WatchHandler::tr("Value"), val); - formatToolTipRow(str, WatchHandler::tr("Object Address"), addr); - formatToolTipRow(str, WatchHandler::tr("Internal ID"), iname); - formatToolTipRow(str, WatchHandler::tr("Generation"), - QString::number(generation)); - str << "</table></body></html>"; - return res; -} - -QString WatchData::msgNotInScope() -{ - static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>"); - return rc; -} - -const QString &WatchData::shadowedNameFormat() -{ - static const QString format = QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>"); - return format; -} - -QString WatchData::shadowedName(const QString &name, int seen) -{ - if (seen <= 0) - return name; - return shadowedNameFormat().arg(name, seen); -} /////////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 2fd672fb43e95ef19cbe80a285aa57f306589243..38bb85c87d43e06f6991aeb0cf1ea2200da9d57c 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -30,6 +30,8 @@ #ifndef DEBUGGER_WATCHHANDLER_H #define DEBUGGER_WATCHHANDLER_H +#include "watchdata.h" + #include <QtCore/QPointer> #include <QtCore/QObject> #include <QtCore/QHash> @@ -51,101 +53,6 @@ class WatchItem; class WatchHandler; enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch }; -class WatchData -{ -public: - WatchData(); - - enum State - { - Complete = 0, - HasChildrenNeeded = 1, - ValueNeeded = 2, - TypeNeeded = 4, - ChildrenNeeded = 8, - - NeededMask = ValueNeeded - | TypeNeeded - | ChildrenNeeded - | HasChildrenNeeded, - - InitialState = ValueNeeded - | TypeNeeded - | ChildrenNeeded - | HasChildrenNeeded - }; - - void setValue(const QString &); - void setType(const QString &, bool guessChildrenFromType = true); - void setValueToolTip(const QString &); - void setError(const QString &); - void setAddress(const QByteArray &); - - bool isSomethingNeeded() const { return state & NeededMask; } - void setAllNeeded() { state = NeededMask; } - void setAllUnneeded() { state = State(0); } - - bool isTypeNeeded() const { return state & TypeNeeded; } - bool isTypeKnown() const { return !(state & TypeNeeded); } - void setTypeNeeded() { state = State(state | TypeNeeded); } - void setTypeUnneeded() { state = State(state & ~TypeNeeded); } - - bool isValueNeeded() const { return state & ValueNeeded; } - bool isValueKnown() const { return !(state & ValueNeeded); } - void setValueNeeded() { state = State(state | ValueNeeded); } - void setValueUnneeded() { state = State(state & ~ValueNeeded); } - - bool isChildrenNeeded() const { return state & ChildrenNeeded; } - bool isChildrenKnown() const { return !(state & ChildrenNeeded); } - void setChildrenNeeded() { state = State(state | ChildrenNeeded); } - void setChildrenUnneeded() { state = State(state & ~ChildrenNeeded); } - - bool isHasChildrenNeeded() const { return state & HasChildrenNeeded; } - bool isHasChildrenKnown() const { return !(state & HasChildrenNeeded); } - void setHasChildrenNeeded() { state = State(state | HasChildrenNeeded); } - void setHasChildrenUnneeded() { state = State(state & ~HasChildrenNeeded); } - void setHasChildren(bool c) { hasChildren = c; setHasChildrenUnneeded(); - if (!c) setChildrenUnneeded(); } - - QString toString() const; - QString toToolTip() const; - bool isLocal() const { return iname.startsWith("local."); } - bool isWatcher() const { return iname.startsWith("watch."); } - bool isValid() const { return !iname.isEmpty(); } - - bool isEqual(const WatchData &other) const; - - static QString msgNotInScope(); - static QString shadowedName(const QString &name, int seen); - static const QString &shadowedNameFormat(); - -public: - QByteArray iname; // Internal name sth like 'local.baz.public.a' - QByteArray exp; // The expression - QString name; // Displayed name - QString value; // Displayed value - QByteArray editvalue; // Displayed value - int editformat; // Format of displayed value - QString valuetooltip; // Tooltip in value column - QString typeFormats; // Selection of formats of displayed value - QString type; // Type for further processing - QString displayedType;// Displayed type (optional) - QByteArray variable; // Name of internal Gdb variable if created - QByteArray addr; // Displayed address - QString framekey; // Key for type cache - QScriptValue scriptValue; // If needed... - bool hasChildren; - int generation; // When updated? - bool valueEnabled; // Value will be greyed out or not - bool valueEditable; // Value will be editable - bool error; - -public: - int source; // Originated from dumper or symbol evaluation? (CDB only) - int state; - bool changed; -}; - enum WatchRoles { INameRole = Qt::UserRole, @@ -305,6 +212,4 @@ private: } // namespace Internal } // namespace Debugger -Q_DECLARE_METATYPE(Debugger::Internal::WatchData); - #endif // DEBUGGER_WATCHHANDLER_H diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index a25568dcec07cd100b7cfdd314dbdccdff7b41f6..46a698952062b209506b193c8799a8f264db6c8b 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -28,8 +28,10 @@ **************************************************************************/ #include "watchutils.h" -#include "watchhandler.h" +#include "watchdata.h" +#include "debuggerstringutils.h" #include "gdb/gdbmi.h" + #include <utils/qtcassert.h> #include <coreplugin/ifile.h> @@ -1543,5 +1545,166 @@ QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d) return in; } + +////////////////////////////////////////////////////////////////////// +// +// GdbMi interaction +// +////////////////////////////////////////////////////////////////////// + +void setWatchDataValue(WatchData &data, const GdbMi &item) +{ + GdbMi value = item.findChild("value"); + if (value.isValid()) { + int encoding = item.findChild("valueencoded").data().toInt(); + data.setValue(decodeData(value.data(), encoding)); + } else { + data.setValueNeeded(); + } +} + +void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi, + int encoding) +{ + if (mi.isValid()) + data.setValueToolTip(decodeData(mi.data(), encoding)); +} + +void setWatchDataChildCount(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + data.setHasChildren(mi.data().toInt() > 0); +} + +void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi) +{ + if (mi.data() == "true") + data.valueEnabled = true; + else if (mi.data() == "false") + data.valueEnabled = false; +} + +void setWatchDataValueEditable(WatchData &data, const GdbMi &mi) +{ + if (mi.data() == "true") + data.valueEditable = true; + else if (mi.data() == "false") + data.valueEditable = false; +} + +void setWatchDataExpression(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + data.exp = mi.data(); +} + +void setWatchDataAddress(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + setWatchDataAddressHelper(data, mi.data()); +} + +void setWatchDataAddressHelper(WatchData &data, const QByteArray &addr) +{ + data.addr = addr; + if (data.exp.isEmpty() && !data.addr.startsWith("$")) + data.exp = "*(" + gdbQuoteTypes(data.type).toLatin1() + "*)" + data.addr; +} + +// Find the "type" and "displayedtype" children of root and set up type. +void setWatchDataType(WatchData &data, const GdbMi &item) +{ + if (item.isValid()) + data.setType(_(item.data())); + else if (data.type.isEmpty()) + data.setTypeNeeded(); +} + +void setWatchDataDisplayedType(WatchData &data, const GdbMi &item) +{ + if (item.isValid()) + data.displayedType = _(item.data()); +} + +void parseWatchData(const QSet<QByteArray> &expandedINames, + const WatchData &data0, const GdbMi &item, QList<WatchData> *list) +{ + //qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString(); + WatchData data = data0; + bool isExpanded = expandedINames.contains(data.iname); + if (!isExpanded) + data.setChildrenUnneeded(); + + GdbMi children = item.findChild("children"); + if (children.isValid() || !isExpanded) + data.setChildrenUnneeded(); + + setWatchDataType(data, item.findChild("type")); + GdbMi mi = item.findChild("editvalue"); + if (mi.isValid()) + data.editvalue = mi.data(); + mi = item.findChild("editformat"); + if (mi.isValid()) + data.editformat = mi.data().toInt(); + mi = item.findChild("typeformats"); + if (mi.isValid()) + data.typeFormats = QString::fromUtf8(mi.data()); + + setWatchDataValue(data, item); + setWatchDataAddress(data, item.findChild("addr")); + setWatchDataExpression(data, item.findChild("exp")); + setWatchDataValueEnabled(data, item.findChild("valueenabled")); + setWatchDataValueEditable(data, item.findChild("valueeditable")); + setWatchDataChildCount(data, item.findChild("numchild")); + //qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n"; + list->append(data); + + bool ok = false; + qulonglong addressBase = item.findChild("addrbase").data().toULongLong(&ok, 0); + qulonglong addressStep = item.findChild("addrstep").data().toULongLong(); + + // Try not to repeat data too often. + WatchData childtemplate; + setWatchDataType(childtemplate, item.findChild("childtype")); + setWatchDataChildCount(childtemplate, item.findChild("childnumchild")); + //qDebug() << "CHILD TEMPLATE:" << childtemplate.toString(); + + int i = 0; + foreach (const GdbMi &child, children.children()) { + WatchData data1 = childtemplate; + GdbMi name = child.findChild("name"); + if (name.isValid()) + data1.name = _(name.data()); + else + data1.name = QString::number(i); + GdbMi iname = child.findChild("iname"); + if (iname.isValid()) + data1.iname = iname.data(); + else + data1.iname = data.iname + '.' + data1.name.toLatin1(); + if (!data1.name.isEmpty() && data1.name.at(0).isDigit()) + data1.name = _c('[') + data1.name + _c(']'); + if (addressStep) { + const QByteArray addr = "0x" + QByteArray::number(addressBase, 16); + setWatchDataAddressHelper(data1, addr); + addressBase += addressStep; + } + QByteArray key = child.findChild("key").data(); + if (!key.isEmpty()) { + int encoding = child.findChild("keyencoded").data().toInt(); + QString skey = decodeData(key, encoding); + if (skey.size() > 13) { + skey = skey.left(12); + skey += _("..."); + } + //data1.name += " (" + skey + ")"; + data1.name = skey; + } + parseWatchData(expandedINames, data1, child, list); + ++i; + } +} + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index 3941fbf2473733c0af27a0d28474d8f951e21d87..67d0d732a88fdb15814ce196022fa671c9574956 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -30,6 +30,7 @@ #ifndef WATCHUTILS_H #define WATCHUTILS_H +#include <QtCore/QSet> #include <QtCore/QString> #include <QtCore/QMap> @@ -238,6 +239,27 @@ QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d); // remove the default template argument in std:: containers QString removeDefaultTemplateArguments(QString type); + +// +// GdbMi interaction +// + +void setWatchDataValue(WatchData &data, const GdbMi &item); +void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi, + int encoding); +void setWatchDataChildCount(WatchData &data, const GdbMi &mi); +void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi); +void setWatchDataValueEditable(WatchData &data, const GdbMi &mi); +void setWatchDataExpression(WatchData &data, const GdbMi &mi); +void setWatchDataAddress(WatchData &data, const GdbMi &mi); +void setWatchDataAddressHelper(WatchData &data, const QByteArray &addr); +void setWatchDataType(WatchData &data, const GdbMi &mi); +void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi); + +void parseWatchData(const QSet<QByteArray> &expandedINames, + const WatchData &parent, const GdbMi &child, + QList<WatchData> *insertions); + } // namespace Internal } // namespace Debugger