Fix up Debugger helpers to be able to dump QObject's with CDB.

- Make container dumper routines set "childnumchild" when known
  in order to avoid roundtrips; avoid repeated invocations of
  container.end().
- Completed dumper information in some places to avoid roundtrips.
- Extended QVariant helpers by dumpers for common GUI types
  (rectangles, points, sizes, fonts, size policies).
- Introduced artificial QObjectChildList/QObjectProperty types to
  be able to dump QObject children and properties without using
  gdb expressions.
- Fixed dumping of Signal/Slot list to pass on correct types. Avoid
  recursions if signal is connected to self.
- Replaced expressions by addresses in the dumpers to it make work
  for CDB.
- Reworked dumper test program to have -a, making it usable for tests,
  add further types.
- Gdb: Clear output buffer before calling dumpers, avoiding mixups
  in case evaluation of expression fails.
- Fix the dumper parser used by CDB, do not be fooled by
  "<synthetic>" addresses, etc.
- Pass on a "dumperVersion" in initial query.
This commit is contained in:
Friedemann Kleint
2009-07-06 17:36:50 +02:00
parent 45448ce5d5
commit 5bd1d2a028
9 changed files with 748 additions and 365 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,6 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT -= gui
TARGET = dumpertest TARGET = dumpertest
CONFIG += console CONFIG += console
CONFIG -= app_bundle CONFIG -= app_bundle

View File

@@ -32,6 +32,8 @@
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QMap> #include <QtCore/QMap>
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <string> #include <string>
#include <list> #include <list>
@@ -174,6 +176,20 @@ static int dumpQMapIntString()
return 0; return 0;
} }
static int dumpQVariant()
{
QVariant test(QLatin1String("item"));
prepareInBuffer("QVariant", "local.qvariant", "local.qvariant", "");
qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0,0 ,0);
fputs(qDumpOutBuffer, stdout);
fputs("\n\n", stdout);
test = QVariant(QStringList(QLatin1String("item1")));
prepareInBuffer("QVariant", "local.qvariant", "local.qvariant", "");
qDumpObjectData440(2, 42, testAddress(&test), 1, 0, 0,0 ,0);
fputs(qDumpOutBuffer, stdout);
return 0;
}
// --------------- std types // --------------- std types
static int dumpStdString() static int dumpStdString()
@@ -309,76 +325,127 @@ static int dumpStdMapIntString()
static int dumpQObject() static int dumpQObject()
{ {
// Requires the childOffset to be know, but that is not critical // Requires the childOffset to be know, but that is not critical
QTimer t; QAction action(0);
QObject x;
QAction *a2= new QAction(&action);
a2->setObjectName(QLatin1String("a2"));
action.setObjectName(QLatin1String("action"));
QObject::connect(&action, SIGNAL(triggered()), &x, SLOT(deleteLater()));
prepareInBuffer("QObject", "local.qobject", "local.qobject", ""); prepareInBuffer("QObject", "local.qobject", "local.qobject", "");
qDumpObjectData440(2, 42, testAddress(&t), 1, 0, 0, 0, 0); qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputs("\n\n", stdout);
// Property list
prepareInBuffer("QObjectPropertyList", "local.qobjectpropertylist", "local.qobjectpropertylist", "");
qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputs("\n\n", stdout);
// Signal list
prepareInBuffer("QObjectSignalList", "local.qobjectsignallist", "local.qobjectsignallist", "");
qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
// Slot list
prepareInBuffer("QObjectSlotList", "local.qobjectslotlist", "local.qobjectslotlist", "");
qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputs("\n\n", stdout);
// Signal list
prepareInBuffer("QObjectChildList", "local.qobjectchildlist", "local.qobjectchildlist", "");
qDumpObjectData440(2, 42, testAddress(&action), 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout); fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0; return 0;
} }
static bool dumpType(const char *arg) static int dumpQObjectList()
{ {
if (!qstrcmp(arg, "QString")) // Requires the childOffset to be know, but that is not critical
{ dumpQString(); return true; } QObject *root = new QObject;
if (!qstrcmp(arg, "QSharedPointer<QString>")) root ->setObjectName("root");
{ dumpQSharedPointerQString(); return true; } QTimer *t1 = new QTimer;
if (!qstrcmp(arg, "QStringList")) t1 ->setObjectName("t1");
{ dumpQStringList(); return true; } QTimer *t2 = new QTimer;
if (!qstrcmp(arg, "QList<int>")) t2 ->setObjectName("t2");
{ dumpQIntList(); return true; } QObjectList test;
if (!qstrcmp(arg, "QList<std::string>")) test << root << t1 << t2;
{ dumpStdStringQList(); return true; } prepareInBuffer("QList", "local.qobjectlist", "local.qobjectlist", "QObject *");
if (!qstrcmp(arg, "QVector<int>")) qDumpObjectData440(2, 42, testAddress(&test), sizeof(QObject*), 0, 0, 0, 0);
{ dumpQIntVector(); return true; } fputs(qDumpOutBuffer, stdout);
if (!qstrcmp(arg, "QMap<int,QString>")) fputc('\n', stdout);
{ dumpQMapIntString(); return true; } delete root;
if (!qstrcmp(arg, "QMap<int,int>")) return 0;
{ dumpQMapIntInt(); return true; } }
if (!qstrcmp(arg, "string"))
{ dumpStdString(); return true; } typedef int (*DumpFunction)();
if (!qstrcmp(arg, "wstring")) typedef QMap<QString, DumpFunction> TypeDumpFunctionMap;
{ dumpStdWString(); return true; }
if (!qstrcmp(arg, "list<int>")) static TypeDumpFunctionMap registerTypes()
{ dumpStdIntList(); return true; } {
if (!qstrcmp(arg, "list<string>")) TypeDumpFunctionMap rc;
{ dumpStdStringList(); return true; } rc.insert("QString", dumpQString);
if (!qstrcmp(arg, "vector<int>")) rc.insert("QSharedPointer<QString>", dumpQSharedPointerQString);
{ dumpStdIntVector(); return true; } rc.insert("QStringList", dumpQStringList);
if (!qstrcmp(arg, "vector<string>")) rc.insert("QList<int>", dumpQIntList);
{ dumpStdStringVector(); return true; } rc.insert("QList<std::string>", dumpStdStringQList);
if (!qstrcmp(arg, "vector<wstring>")) rc.insert("QVector<int>", dumpQIntVector);
{ dumpStdWStringVector(); return true; } rc.insert("QMap<int,QString>", dumpQMapIntString);
if (!qstrcmp(arg, "set<int>")) rc.insert("QMap<int,int>", dumpQMapIntInt);
{ dumpStdIntSet(); return true; } rc.insert("string", dumpStdString);
if (!qstrcmp(arg, "set<string>")) rc.insert("wstring", dumpStdWString);
{ dumpStdStringSet(); return true; } rc.insert("list<int>", dumpStdIntList);
if (!qstrcmp(arg, "map<int,string>")) rc.insert("list<string>", dumpStdStringList);
{ dumpStdMapIntString(); return true; } rc.insert("vector<int>", dumpStdIntVector);
if (!qstrcmp(arg, "QObject")) rc.insert("vector<string>", dumpStdStringVector);
{ dumpQObject(); return true; } rc.insert("vector<wstring>", dumpStdWStringVector);
return false; rc.insert("set<int>", dumpStdIntSet);
rc.insert("set<string>", dumpStdStringSet);
rc.insert("map<int,string>", dumpStdMapIntString);
rc.insert("QObject", dumpQObject);
rc.insert("QObjectList", dumpQObjectList);
rc.insert("QVariant", dumpQVariant);
return rc;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
printf("\nQt Creator Debugging Helper testing tool\n\n");
printf("Running query protocol\n"); printf("Running query protocol\n");
qDumpObjectData440(1, 42, 0, 1, 0, 0, 0, 0); qDumpObjectData440(1, 42, 0, 1, 0, 0, 0, 0);
fputs(qDumpOutBuffer, stdout); fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout); fputc('\n', stdout);
fputc('\n', stdout); fputc('\n', stdout);
if (argc < 2)
const TypeDumpFunctionMap tdm = registerTypes();
const TypeDumpFunctionMap::const_iterator cend = tdm.constEnd();
if (argc < 2) {
printf("Usage: %s [-a]|<type1> <type2..>\n", argv[0]);
printf("Supported types: ");
for (TypeDumpFunctionMap::const_iterator it = tdm.constBegin(); it != cend; ++it) {
fputs(qPrintable(it.key()), stdout);
fputc(' ', stdout);
}
fputc('\n', stdout);
return 0; return 0;
}
int rc = 0;
if (argc == 2 && !qstrcmp(argv[1], "-a")) {
for (TypeDumpFunctionMap::const_iterator it = tdm.constBegin(); it != cend; ++it) {
printf("\nTesting: %s\n", qPrintable(it.key()));
rc += (*it.value())();
}
} else {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
if (!strcmp(arg, "-u")) { printf("\nTesting: %s\n", arg);
optTestUninitialized = true; const TypeDumpFunctionMap::const_iterator it = tdm.constFind(QLatin1String(arg));
printf("\nTesting uninitialized...\n"); if (it == cend) {
continue; rc = -1;
fprintf(stderr, "\nUnhandled type: %s\n", argv[i]);
} else {
rc = (*it.value())();
} }
printf("\nTesting %s\n", arg);
if (!dumpType(arg))
printf("\nUnhandled type: %s\n", arg);
} }
return 0; }
return rc;
} }

View File

@@ -561,6 +561,10 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool
*errorMessage = msgNotHandled(wd.type); *errorMessage = msgNotHandled(wd.type);
return DumpNotHandled; return DumpNotHandled;
} }
if (wd.addr.isEmpty()) {
*errorMessage = QString::fromLatin1("Adress is missing for '%1' (%2).").arg(wd.exp, wd.type);
return DumpNotHandled;
}
// Ensure types are parsed and known. // Ensure types are parsed and known.
if (!ensureInitialized(errorMessage)) { if (!ensureInitialized(errorMessage)) {

View File

@@ -35,6 +35,7 @@
#include "watchhandler.h" #include "watchhandler.h"
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
@@ -164,6 +165,46 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt
return handled; return handled;
} }
// When querying an item, the queried item is sometimes returned in incomplete form.
// Take over values from source.
static inline void fixDumperResult(const WatchData &source,
QList<WatchData> *result,
bool suppressGrandChildren)
{
const int size = result->size();
if (!size)
return;
WatchData &returned = result->front();
if (returned.iname != source.iname)
return;
if (returned.type.isEmpty())
returned.setType(source.type);
if (returned.isValueNeeded()) {
if (source.isValueKnown()) {
returned.setValue(source.value);
} else {
// Should not happen
returned.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>"));
}
}
if (size == 1)
return;
// Fix the children: If the address is missing, we cannot query any further.
const QList<WatchData>::iterator wend = result->end();
QList<WatchData>::iterator it = result->begin();
for (++it; it != wend; ++it) {
WatchData &wd = *it;
if (wd.addr.isEmpty() && wd.isSomethingNeeded()) {
wd.setAllUnneeded();
} else {
// Hack: Suppress endless recursion of the model. To be fixed,
// the model should not query non-visible items.
if (suppressGrandChildren && (wd.isChildrenNeeded() || wd.isHasChildrenNeeded()))
wd.setHasChildren(false);
}
}
}
WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd) WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
{ {
if (debugCDBWatchHandling) if (debugCDBWatchHandling)
@@ -180,9 +221,7 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
if (debugCDBWatchHandling) if (debugCDBWatchHandling)
qDebug() << "dumper triggered"; qDebug() << "dumper triggered";
// Dumpers omit types for complicated templates // Dumpers omit types for complicated templates
if (!m_dumperResult.isEmpty() && m_dumperResult.front().type.isEmpty() fixDumperResult(wd, &m_dumperResult, false);
&& m_dumperResult.front().iname == wd.iname)
m_dumperResult.front().setType(wd.type);
// Discard the original item and insert the dumper results // Discard the original item and insert the dumper results
foreach(const WatchData &dwd, m_dumperResult) foreach(const WatchData &dwd, m_dumperResult)
m_wh->insertData(dwd); m_wh->insertData(dwd);
@@ -254,12 +293,17 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
QList<WatchData> dumperResult; QList<WatchData> dumperResult;
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage); const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage);
if (dr == CdbDumperHelper::DumpOk) { 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) foreach(const WatchData &dwd, dumperResult)
wh->insertData(dwd); wh->insertData(dwd);
} else { } 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); 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)); qWarning("%s", qPrintable(msg));
WatchData wd = incompleteLocal; WatchData wd = incompleteLocal;
if (wd.isValueNeeded())
wd.setValue(QCoreApplication::translate("CdbStackFrameContext", "<Unknown>"));
wd.setAllUnneeded(); wd.setAllUnneeded();
wh->insertData(wd); wh->insertData(wd);
} }

View File

@@ -3192,24 +3192,36 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const
//qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE"; //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
} }
void GdbEngine::sendWatchParameters(const QByteArray &params0) static inline QString arrayFillCommand(const char *array, const QByteArray &params)
{ {
QByteArray params = params0;
params.append('\0');
char buf[50]; char buf[50];
sprintf(buf, "set {char[%d]} &qDumpInBuffer = {", params.size()); sprintf(buf, "set {char[%d]} &%s = {", params.size(), array);
QByteArray encoded; QByteArray encoded;
encoded.append(buf); encoded.append(buf);
for (int i = 0; i != params.size(); ++i) { const int size = params.size();
for (int i = 0; i != size; ++i) {
sprintf(buf, "%d,", int(params[i])); sprintf(buf, "%d,", int(params[i]));
encoded.append(buf); encoded.append(buf);
} }
encoded[encoded.size() - 1] = '}'; encoded[encoded.size() - 1] = '}';
return _(encoded);
}
void GdbEngine::sendWatchParameters(const QByteArray &params0)
{
QByteArray params = params0;
params.append('\0');
const QString inBufferCmd = arrayFillCommand("qDumpInBuffer", params);
params.replace('\0','!'); params.replace('\0','!');
emit gdbInputAvailable(LogMisc, QString::fromUtf8(params)); emit gdbInputAvailable(LogMisc, QString::fromUtf8(params));
postCommand(_(encoded)); params.clear();
params.append('\0');
const QString outBufferCmd = arrayFillCommand("qDumpOutBuffer", params);
postCommand(inBufferCmd);
postCommand(outBufferCmd);
} }
void GdbEngine::handleVarAssign(const GdbResultRecord &, const QVariant &) void GdbEngine::handleVarAssign(const GdbResultRecord &, const QVariant &)

View File

@@ -209,6 +209,8 @@ QString WatchData::toString() const
QTextStream str(&res); QTextStream str(&res);
if (!iname.isEmpty()) if (!iname.isEmpty())
str << "iname=\"" << iname << doubleQuoteComma; str << "iname=\"" << iname << doubleQuoteComma;
if (!addr.isEmpty())
str << "addr=\"" << addr << doubleQuoteComma;
if (!exp.isEmpty()) if (!exp.isEmpty())
str << "exp=\"" << exp << doubleQuoteComma; str << "exp=\"" << exp << doubleQuoteComma;
@@ -780,6 +782,7 @@ static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *i
void WatchModel::insertData(const WatchData &data) void WatchModel::insertData(const WatchData &data)
{ {
// qDebug() << "WMI:" << data.toString();
QTC_ASSERT(!data.iname.isEmpty(), return); QTC_ASSERT(!data.iname.isEmpty(), return);
WatchItem *parent = findItem(parentName(data.iname), m_root); WatchItem *parent = findItem(parentName(data.iname), m_root);
if (!parent) { if (!parent) {

View File

@@ -470,16 +470,19 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
QtDumperResult::Child::Child() : QtDumperResult::Child::Child() :
keyEncoded(0), keyEncoded(0),
valueEncoded(0), valueEncoded(0),
childCount(0), childCount(-1),
valuedisabled(false) valuedisabled(false),
valueEncountered(false)
{ {
} }
QtDumperResult::QtDumperResult() : QtDumperResult::QtDumperResult() :
valueEncountered(false),
valueEncoded(0), valueEncoded(0),
valuedisabled(false), valuedisabled(false),
childCount(0), childCount(-1),
internal(false) internal(false),
childChildCount(-1)
{ {
} }
@@ -488,15 +491,17 @@ void QtDumperResult::clear()
iname.clear(); iname.clear();
value.clear(); value.clear();
address.clear(); address.clear();
addressInfo.clear();
type.clear(); type.clear();
extra.clear(); extra.clear();
displayedType.clear(); displayedType.clear();
valueEncoded = 0; valueEncoded = 0;
valuedisabled = false; valueEncountered = valuedisabled = false;
childCount = 0; childCount = -1;
internal = false; internal = false;
childType.clear(); childType.clear();
children.clear(); children.clear();
childChildCount = -1;
} }
QList<WatchData> QtDumperResult::toWatchData(int source) const QList<WatchData> QtDumperResult::toWatchData(int source) const
@@ -508,15 +513,22 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const
const QChar dot = QLatin1Char('.'); const QChar dot = QLatin1Char('.');
const int lastDotIndex = root.iname.lastIndexOf(dot); const int lastDotIndex = root.iname.lastIndexOf(dot);
root.exp = root.name = lastDotIndex == -1 ? iname : iname.mid(lastDotIndex + 1); root.exp = root.name = lastDotIndex == -1 ? iname : iname.mid(lastDotIndex + 1);
if (valueEncountered) {
root.setValue(decodeData(value, valueEncoded)); root.setValue(decodeData(value, valueEncoded));
root.setType(displayedType.isEmpty() ? type : displayedType);
root.valuedisabled = valuedisabled; root.valuedisabled = valuedisabled;
}
root.setType(displayedType.isEmpty() ? type : displayedType);
root.setAddress(address); root.setAddress(address);
root.source = source; root.source = source;
if (childCount >= 0)
root.setHasChildren(childCount > 0); root.setHasChildren(childCount > 0);
// Children // Children. Sanity check after parsing sets childcount to list size
if (childCount > 0) { // if list is not empty
if (children.size() == childCount) { if (children.empty()) {
if (childCount > 0)
root.setChildrenNeeded();
} else {
root.setChildrenUnneeded();
for (int c = 0; c < childCount; c++) { for (int c = 0; c < childCount; c++) {
const Child &dchild = children.at(c); const Child &dchild = children.at(c);
rc.push_back(WatchData()); rc.push_back(WatchData());
@@ -536,17 +548,35 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const
} }
} }
wchild.exp = dchild.exp; wchild.exp = dchild.exp;
if (dchild.valueEncountered) {
wchild.valuedisabled = dchild.valuedisabled; wchild.valuedisabled = dchild.valuedisabled;
wchild.setValue(decodeData(dchild.value, dchild.valueEncoded));
}
wchild.setType(dchild.type.isEmpty() ? childType : dchild.type); wchild.setType(dchild.type.isEmpty() ? childType : dchild.type);
wchild.setAddress(dchild.address); wchild.setAddress(dchild.address);
wchild.setValue(decodeData(dchild.value, dchild.valueEncoded)); // Child overrides.
const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount;
switch (effectiveChildChildCount) {
case -1:
wchild.setChildrenNeeded();
wchild.setHasChildrenNeeded();
break;
case 0:
wchild.setHasChildren(false); wchild.setHasChildren(false);
} break;
root.setChildrenUnneeded(); default:
} else { wchild.setHasChildren(true);
root.setChildrenNeeded(); break;
} }
} }
}
if (debug) {
QDebug nospace = qDebug().nospace();
nospace << "QtDumperResult::toWatchData" << *this << '\n';
foreach(const WatchData &wd, rc)
nospace << " " << wd.toString() << '\n';
}
return rc; return rc;
} }
@@ -554,11 +584,20 @@ QDebug operator<<(QDebug in, const QtDumperResult &d)
{ {
QDebug nospace = in.nospace(); QDebug nospace = in.nospace();
nospace << " iname=" << d.iname << " type=" << d.type << " displayed=" << d.displayedType nospace << " iname=" << d.iname << " type=" << d.type << " displayed=" << d.displayedType
<< " address=" << d.address << " address=" << d.address;
if (!d.addressInfo.isEmpty())
nospace << " addressInfo=" << d.addressInfo;
if (d.valueEncountered) {
nospace << " encoded=" << d.valueEncoded
<< " value=" << d.value << " value=" << d.value
<< " disabled=" << d.valuedisabled << " disabled=" << d.valuedisabled;
<< " encoded=" << d.valueEncoded << " internal=" << d.internal } else {
nospace << " <no value>";
}
nospace << " childnumchild=" << d.childChildCount
<< " internal=" << d.internal
<< " extra='" << d.extra << "'\n"; << " extra='" << d.extra << "'\n";
const int realChildCount = d.children.size(); const int realChildCount = d.children.size();
if (d.childCount || realChildCount) { if (d.childCount || realChildCount) {
nospace << "childCount=" << d.childCount << '/' << realChildCount nospace << "childCount=" << d.childCount << '/' << realChildCount
@@ -571,8 +610,12 @@ QDebug operator<<(QDebug in, const QtDumperResult &d)
<< " name=" << c.name; << " name=" << c.name;
if (!c.key.isEmpty()) if (!c.key.isEmpty())
nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key; nospace << " keyencoded=" << c.keyEncoded << " key=" << c.key;
nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value if (c.valueEncountered) {
<< "childcount=" << c.childCount << '\n'; nospace << " valueencoded=" << c.valueEncoded << " value=" << c.value;
} else {
nospace << " <no value>";
}
nospace << "childcount=" << c.childCount << '\n';
} }
} }
return in; return in;
@@ -608,6 +651,7 @@ void QtDumperHelper::clear()
m_sizeCache.clear(); m_sizeCache.clear();
qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0); qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
m_expressionCache.clear(); m_expressionCache.clear();
m_dumperVersion.clear();
} }
static inline void formatQtVersion(int v, QTextStream &str) static inline void formatQtVersion(int v, QTextStream &str)
@@ -622,7 +666,7 @@ QString QtDumperHelper::toString(bool debug) const
QTextStream str(&rc); QTextStream str(&rc);
str << "version="; str << "version=";
formatQtVersion(m_qtVersion, str); formatQtVersion(m_qtVersion, str);
str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: "; str << "dumperversion='" << m_dumperVersion << "' namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: ";
const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd(); const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd();
for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) { for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) {
str <<",[" << it.key() << ',' << it.value() << ']'; str <<",[" << it.key() << ',' << it.value() << ']';
@@ -639,9 +683,9 @@ QString QtDumperHelper::toString(bool debug) const
} }
const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace; const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace;
return QCoreApplication::translate("QtDumperHelper", return QCoreApplication::translate("QtDumperHelper",
"%n known types, Qt version: %1, Qt namespace: %2", "%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3",
0, QCoreApplication::CodecForTr, 0, QCoreApplication::CodecForTr,
m_nameTypeMap.size()).arg(qtVersionString(), nameSpace); m_nameTypeMap.size()).arg(qtVersionString(), nameSpace, m_dumperVersion);
} }
QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const
@@ -726,10 +770,9 @@ QtDumperHelper::Type QtDumperHelper::specialType(QString s)
QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t) QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t)
{ {
switch (t) { switch (t) {
case QAbstractItemType: case QAbstractItemType:
case QObjectSlotType:
case QObjectSignalType:
case QVectorType: case QVectorType:
case StdMapType: case StdMapType:
return NeedsComplexExpression; return NeedsComplexExpression;
@@ -738,6 +781,7 @@ QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Typ
case QMapNodeType: case QMapNodeType:
return NeedsCachedExpression; return NeedsCachedExpression;
default: default:
// QObjectSlotType, QObjectSignalType need the signal number, which is numeric
break; break;
} }
return NeedsNoExpression; return NeedsNoExpression;
@@ -968,9 +1012,9 @@ public:
explicit QueryDumperParser(const char *s); explicit QueryDumperParser(const char *s);
struct Data { struct Data {
Data() : qtVersion(0) {}
QString qtNameSpace; QString qtNameSpace;
QString qtVersion; QString qtVersion;
QString dumperVersion;
QStringList types; QStringList types;
QList<SizeEntry> sizes; QList<SizeEntry> sizes;
QMap<QString, QString> expressionCache; QMap<QString, QString> expressionCache;
@@ -986,7 +1030,7 @@ protected:
virtual bool handleValue(const char *k, int size); virtual bool handleValue(const char *k, int size);
private: private:
enum Mode { None, ExpectingDumpers, ExpectingVersion, enum Mode { None, ExpectingDumpers, ExpectingQtVersion, ExpectingDumperVersion,
ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache }; ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache };
Mode m_mode; Mode m_mode;
Data m_data; Data m_data;
@@ -1017,7 +1061,11 @@ bool QueryDumperParser::handleKeyword(const char *k, int size)
return true; return true;
} }
if (!qstrncmp(k, "qtversion", size)) { if (!qstrncmp(k, "qtversion", size)) {
m_mode = ExpectingVersion; m_mode = ExpectingQtVersion;
return true;
}
if (!qstrncmp(k, "dumperversion", size)) {
m_mode = ExpectingDumperVersion;
return true; return true;
} }
if (!qstrncmp(k, "namespace", size)) { if (!qstrncmp(k, "namespace", size)) {
@@ -1038,7 +1086,7 @@ bool QueryDumperParser::handleKeyword(const char *k, int size)
bool QueryDumperParser::handleListStart() bool QueryDumperParser::handleListStart()
{ {
return m_mode == ExpectingDumpers || m_mode == ExpectingVersion; return m_mode == ExpectingDumpers || m_mode == ExpectingQtVersion;
} }
bool QueryDumperParser::handleListEnd() bool QueryDumperParser::handleListEnd()
@@ -1064,7 +1112,10 @@ bool QueryDumperParser::handleValue(const char *k, int size)
case ExpectingNameSpace: case ExpectingNameSpace:
m_data.qtNameSpace = QString::fromLatin1(k, size); m_data.qtNameSpace = QString::fromLatin1(k, size);
break; break;
case ExpectingVersion: // ["4","1","5"] case ExpectingDumperVersion:
m_data.dumperVersion = QString::fromLatin1(k, size);
break;
case ExpectingQtVersion: // ["4","1","5"]
if (!m_data.qtVersion.isEmpty()) if (!m_data.qtVersion.isEmpty())
m_data.qtVersion += QLatin1Char('.'); m_data.qtVersion += QLatin1Char('.');
m_data.qtVersion += QString::fromLatin1(k, size); m_data.qtVersion += QString::fromLatin1(k, size);
@@ -1092,6 +1143,7 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes) foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes)
addSize(se.first, se.second); addSize(se.first, se.second);
m_expressionCache = parser.data().expressionCache; m_expressionCache = parser.data().expressionCache;
m_dumperVersion = parser.data().dumperVersion;
return true; return true;
} }
@@ -1248,17 +1300,6 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
case QAbstractItemType: case QAbstractItemType:
inner = data.addr.mid(1); inner = data.addr.mid(1);
break; break;
case QObjectType:
case QWidgetType:
if (debugger == GdbDebugger) {
extraArgs[0] = QLatin1String("(char*)&((('");
extraArgs[0] += m_qtNamespace;
extraArgs[0] += QLatin1String("QObjectPrivate'*)&");
extraArgs[0] += data.exp;
extraArgs[0] += QLatin1String(")->children)-(char*)&");
extraArgs[0] += data.exp;
}
break;
case QVectorType: case QVectorType:
extraArgs[1] = QLatin1String("(char*)&(("); extraArgs[1] = QLatin1String("(char*)&((");
extraArgs[1] += data.exp; extraArgs[1] += data.exp;
@@ -1355,6 +1396,8 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO); qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO);
break; break;
case SupportedType: case SupportedType:
case QObjectType:
case QWidgetType:
break; break;
} }
@@ -1412,6 +1455,7 @@ private:
ExpectingType, ExpectingDisplayedType, ExpectingInternal, ExpectingType, ExpectingDisplayedType, ExpectingInternal,
ExpectingValueDisabled, ExpectingValueEncoded, ExpectingValueDisabled, ExpectingValueEncoded,
ExpectingCommonChildType, ExpectingChildCount, ExpectingCommonChildType, ExpectingChildCount,
ExpectingChildChildOverrideCount,
ExpectingExtra, ExpectingExtra,
IgnoreNext, IgnoreNext,
ChildModeStart, ChildModeStart,
@@ -1488,7 +1532,7 @@ ValueDumperParser::Mode ValueDumperParser::nextMode(Mode in, const char *keyword
if (!qstrncmp(keyword, "displayedtype", size)) if (!qstrncmp(keyword, "displayedtype", size))
return ExpectingDisplayedType; return ExpectingDisplayedType;
if (!qstrncmp(keyword, "childnumchild", size)) if (!qstrncmp(keyword, "childnumchild", size))
return IgnoreNextChildMode; return ExpectingChildChildOverrideCount;
break; break;
} }
return in > ChildModeStart ? IgnoreNextChildMode : IgnoreNext; return in > ChildModeStart ? IgnoreNextChildMode : IgnoreNext;
@@ -1519,10 +1563,17 @@ bool ValueDumperParser::handleValue(const char *k, int size)
case ExpectingIName: case ExpectingIName:
m_result.iname = QString::fromLatin1(valueBA); m_result.iname = QString::fromLatin1(valueBA);
break; break;
case ExpectingAddress: case ExpectingAddress: {
m_result.address = QString::fromLatin1(valueBA); const QString address = QString::fromLatin1(valueBA);
if (address.startsWith(QLatin1String("0x"))) {
m_result.address = address;
} else {
m_result.addressInfo = address;
}
}
break; break;
case ExpectingValue: case ExpectingValue:
m_result.valueEncountered = true;
m_result.value = valueBA; m_result.value = valueBA;
break; break;
case ExpectingValueDisabled: case ExpectingValueDisabled:
@@ -1549,6 +1600,9 @@ bool ValueDumperParser::handleValue(const char *k, int size)
case ExpectingChildCount: case ExpectingChildCount:
m_result.childCount = QString::fromLatin1(valueBA).toInt(); m_result.childCount = QString::fromLatin1(valueBA).toInt();
break; break;
case ExpectingChildChildOverrideCount:
m_result.childChildCount = QString::fromLatin1(valueBA).toInt();
break;
case ExpectingChildren: case ExpectingChildren:
case IgnoreNextChildMode: case IgnoreNextChildMode:
case IgnoreNext: case IgnoreNext:
@@ -1566,6 +1620,7 @@ bool ValueDumperParser::handleValue(const char *k, int size)
m_result.children.back().key = valueBA; m_result.children.back().key = valueBA;
break; break;
case ExpectingChildValue: case ExpectingChildValue:
m_result.children.back().valueEncountered = true;
m_result.children.back().value = valueBA; m_result.children.back().value = valueBA;
break; break;
case ExpectingChildExpression: case ExpectingChildExpression:
@@ -1590,11 +1645,12 @@ bool ValueDumperParser::handleValue(const char *k, int size)
bool QtDumperHelper::parseValue(const char *data, QtDumperResult *r) bool QtDumperHelper::parseValue(const char *data, QtDumperResult *r)
{ {
ValueDumperParser parser(data); ValueDumperParser parser(data);
if (!parser.run()) if (!parser.run())
return false; return false;
*r = parser.result(); *r = parser.result();
// Sanity // Sanity
if (r->childCount < r->children.size()) if (!r->children.empty() && r->childCount != r->children.size())
r->childCount = r->children.size(); r->childCount = r->children.size();
if (debug) if (debug)
qDebug() << '\n' << data << '\n' << *r; qDebug() << '\n' << data << '\n' << *r;

View File

@@ -99,6 +99,7 @@ struct QtDumperResult
QString exp; QString exp;
QString type; QString type;
QByteArray key; QByteArray key;
bool valueEncountered;
QByteArray value; QByteArray value;
}; };
@@ -108,15 +109,18 @@ struct QtDumperResult
QString iname; QString iname;
QString address; QString address;
QString addressInfo; // "<synthetic>" or such, in the 2nd adress field.
QString type; QString type;
QString extra; QString extra;
QString displayedType; QString displayedType;
bool valueEncountered;
QByteArray value; QByteArray value;
int valueEncoded; int valueEncoded;
bool valuedisabled; bool valuedisabled;
int childCount; int childCount;
bool internal; bool internal;
QString childType; QString childType;
int childChildCount;
QList <Child> children; QList <Child> children;
}; };
@@ -242,6 +246,7 @@ private:
QMap<QString, QString> m_expressionCache; QMap<QString, QString> m_expressionCache;
int m_qtVersion; int m_qtVersion;
QString m_dumperVersion;
QString m_qtNamespace; QString m_qtNamespace;
}; };