diff options
Diffstat (limited to 'src/corelib/tools/qdumper.cpp')
-rw-r--r-- | src/corelib/tools/qdumper.cpp | 1157 |
1 files changed, 1157 insertions, 0 deletions
diff --git a/src/corelib/tools/qdumper.cpp b/src/corelib/tools/qdumper.cpp new file mode 100644 index 0000000..c3b8524 --- /dev/null +++ b/src/corelib/tools/qdumper.cpp @@ -0,0 +1,1157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdatetime.h> +#include <qdebug.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <qhash.h> +#include <qmap.h> +#include <qmetaobject.h> +#include <qobject.h> +#include <qstring.h> +#include <qvariant.h> +#include <qvector.h> + +#if !defined(Q_OS_WINCE) && !defined(QT_NO_DUMPER) + +#include <stdlib.h> +#include <stdio.h> + +#ifdef Q_OS_WIN +# include <windows.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace { + +// This is used to abort evaluation of custom data dumpers in a "coordinated" +// way. Abortion will happen anyway when we try to access a non-initialized +// non-trivial object, so there is no way to prevent this from occuring at all +// conceptionally. Gdb will catch SIGSEGV and return to the calling frame. +// This is just fine provided we only _read_ memory in the custom handlers +// below. + +volatile int qProvokeSegFaultHelper; + +static void qCheckAccess(const void *d) +{ + // provoke segfault when address is not readable + qProvokeSegFaultHelper = *(char*)d; +} + +static void qCheckPointer(const void *d) +{ + if (!d) + return; + qProvokeSegFaultHelper = *(char*)d; +} + +static void qProvokeSegFault() +{ + // provoke segfault unconditionally + qCheckAccess(0); +} + +static char qDumpInBuffer[100]; +static char qDumpBuffer[1000]; +#ifdef Q_OS_WIN +static char qDumpBuffer2[sizeof(qDumpBuffer) + 100]; +#endif + +static char toHex(int n) +{ + return n < 10 ? '0' + n : 'a' - 10 + n; +} + + +struct QDumper +{ + explicit QDumper(); + ~QDumper(); + void flush(); + QDumper &operator<<(long c); + QDumper &operator<<(int i); + QDumper &operator<<(unsigned long c); + QDumper &operator<<(unsigned int i); + QDumper &operator<<(const void *p); + void put(char c); + void addCommaIfNeeded(); + void putEncoded(unsigned c); + QDumper &operator<<(const char *str); + QDumper &operator<<(const QString &str); + void disarm(); + + void beginHash(); // start of data hash output + void endHash(); // start of data hash output + + // the dumper arguments + int protocolVersion; // dumper protocol version + int token; // some token to show on success + const char *outertype; // object type + const char *iname; // object name used for display + const char *exp; // object expression + const char *innertype; // 'inner type' for class templates + const void *data; // pointer to raw data + bool dumpChildren; // do we want to see children? + + // handling of nested templates + void setupTemplateParameters(); + enum { maxTemplateParameters = 10 }; + const char *templateParameters[maxTemplateParameters + 1]; + int templateParametersCount; + + // internal state + bool success; // are we finished? + size_t pos; +}; + + +QDumper::QDumper() +{ + success = false; + pos = 0; +} + +QDumper::~QDumper() +{ + flush(); + put(0); // our end marker +#ifdef Q_OS_WIN + sprintf(qDumpBuffer2, "@@CDD/%d/done\n", token); + OutputDebugStringA(qDumpBuffer2); +#else + fprintf(stderr, "%d/done\n", token); +#endif + qDumpInBuffer[0] = 0; +} + +void QDumper::flush() +{ + qDumpBuffer[pos++] = 0; +#ifdef Q_OS_WIN + sprintf(qDumpBuffer2, "@@CDD#%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); + OutputDebugStringA(qDumpBuffer2); +#else + fprintf(stderr, "%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); +#endif + pos = 0; +} + +void QDumper::setupTemplateParameters() +{ + char *s = const_cast<char *>(innertype); + + templateParametersCount = 1; + templateParameters[0] = s; + for (int i = 1; i != maxTemplateParameters + 1; ++i) + templateParameters[i] = 0; + + while (*s) { + while (*s && *s != '@') + ++s; + if (*s) { + *s = '\0'; + ++s; + templateParameters[templateParametersCount++] = s; + } + } +} + +QDumper &QDumper::operator<<(unsigned long c) +{ + static char buf[100]; + sprintf(buf, "%lu", c); + return (*this) << buf; +} + +QDumper &QDumper::operator<<(unsigned int i) +{ + static char buf[100]; + sprintf(buf, "%u", i); + return (*this) << buf; +} + +QDumper &QDumper::operator<<(long c) +{ + static char buf[100]; + sprintf(buf, "%ld", c); + return (*this) << buf; +} + +QDumper &QDumper::operator<<(int i) +{ + static char buf[100]; + sprintf(buf, "%d", i); + return (*this) << buf; +} + +QDumper &QDumper::operator<<(const void *p) +{ + static char buf[100]; + sprintf(buf, "%p", p); + // we get a '0x' prefix only on some implementations. + // if it isn't there, write it out manually. + if (buf[1] != 'x') { + put('0'); + put('x'); + } + return (*this) << buf; +} + +void QDumper::put(char c) +{ + if (pos >= sizeof(qDumpBuffer) - 100) + flush(); + qDumpBuffer[pos++] = c; +} + +void QDumper::addCommaIfNeeded() +{ + if (pos == 0) + return; + if (qDumpBuffer[pos - 1] == '}' || qDumpBuffer[pos - 1] == '"') + put(','); +} + +void QDumper::putEncoded(unsigned c) +{ + if (c >= 32 && c <= 126 && c != '"' && c != '\\') { + put(c); + } else { + put('\\'); + put('u'); + put(toHex((c >> 12) & 0xf)); + put(toHex((c >> 8) & 0xf)); + put(toHex((c >> 4) & 0xf)); + put(toHex( c & 0xf)); + } +} + +QDumper &QDumper::operator<<(const char *str) +{ + while (*str) + put(*(str++)); + return *this; +} + +QDumper &QDumper::operator<<(const QString &str) +{ + int n = str.size(); + if (n < 0) { + qProvokeSegFault(); + } else { + //(*this) << "[" << n << "]"; + if (n > 1000000) + n = 1000000; + //put(' '); + put('\\'); + put('"'); + for (int i = 0; i != n; ++i) + putEncoded(str[i].unicode()); + put('\\'); + put('"'); + if (n < str.size()) + (*this) << "<incomplete string>"; + } + return *this; +} + +void QDumper::disarm() +{ + flush(); + success = true; +} + +void QDumper::beginHash() +{ + addCommaIfNeeded(); + put('{'); +} + +void QDumper::endHash() +{ + put('}'); +} + + +// +// Some helpers to keep the dumper code short +// + +// dump property=value pair +#undef P +#define P(dumper,name,value) \ + do { \ + dumper.addCommaIfNeeded(); \ + dumper << (name) << "=\"" << value << "\""; \ + } while (0) + +// simple string property +#undef S +#define S(dumper, name, value) \ + dumper.beginHash(); \ + P(dumper, "name", name); \ + P(dumper, "value", value); \ + P(dumper, "type", "QString"); \ + P(dumper, "numchild", "0"); \ + dumper.endHash(); + +// simple integer property +#undef I +#define I(dumper, name, value) \ + dumper.beginHash(); \ + P(dumper, "name", name); \ + P(dumper, "value", value); \ + P(dumper, "type", "int"); \ + P(dumper, "numchild", "0"); \ + dumper.endHash(); + +// simple boolean property +#undef BL +#define BL(dumper, name, value) \ + dumper.beginHash(); \ + P(dumper, "name", name); \ + P(dumper, "value", (value ? "true" : "false")); \ + P(dumper, "type", "bool"); \ + P(dumper, "numchild", "0"); \ + dumper.endHash(); + +#undef TT +#define TT(type, value) \ + "<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>" + +static void qDumpUnknown(QDumper &d) +{ + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<internal error>"); + P(d, "type", d.outertype); + P(d, "numchild", "0"); + d.disarm(); +} + +static void qDumpQPropertyList(QDumper &d) +{ + const QObject *ob = (const QObject *)d.data; + const QMetaObject *mo = ob->metaObject(); + P(d, "iname", d.iname); + P(d, "addr", "<synthetic>"); + P(d, "type", "QObject"); + P(d, "numchild", mo->propertyCount()); + if (d.dumpChildren) { + d << ",children=["; + for (int i = mo->propertyCount(); --i >= 0; ) { + const QMetaProperty & prop = mo->property(i); + d.beginHash(); + P(d, "name", prop.name()); + if (QLatin1String(prop.typeName()) == QLatin1String("QString")) { + P(d, "value", prop.read(ob).toString()); + P(d, "numchild", "0"); + } else if (QLatin1String(prop.typeName()) == QLatin1String("bool")) { + P(d, "value", (prop.read(ob).toBool() ? "true" : "false")); + P(d, "numchild", "0"); + } else if (QLatin1String(prop.typeName()) == QLatin1String("int")) { + P(d, "value", prop.read(ob).toInt()); + P(d, "numchild", "0"); + } else { + P(d, "exp", "((" << mo->className() << "*)" << ob + << ")->" << prop.name() << "()"); + } + P(d, "type", prop.typeName()); + P(d, "numchild", "1"); + d.endHash(); + } + d << "]"; + } + d.disarm(); +} + +static void qDumpQObject(QDumper &d) +{ + const QObject *ob = reinterpret_cast<const QObject *>(d.data); + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", (void*)d.data); + P(d, "type", "QObject"); + P(d, "numchild", 4); + if (d.dumpChildren) { + const QMetaObject *mo = ob->metaObject(); + const QObjectList &children = ob->children(); + d << ",children=["; + S(d, "objectName", ob->objectName()); + d.beginHash(); + P(d, "name", "properties"); + // FIXME: Note that when simply using '(QObject*)' + // in the cast below, Gdb/MI _sometimes misparses + // expressions further down in the tree. + P(d, "exp", "*(class QObject*)" << d.data); + P(d, "type", "QPropertyList"); + P(d, "value", "<" << mo->propertyCount() << " items>"); + P(d, "numchild", mo->propertyCount()); + d.endHash(); + d.beginHash(); + P(d, "name", "children"); + P(d, "exp", "((class QObject*)" << d.data << ")->children()"); + P(d, "type", "QList<QObject *>"); + P(d, "value", "<" << children.size() << " items>"); + P(d, "numchild", children.size()); + d.endHash(); + d.beginHash(); + P(d, "name", "parent"); + P(d, "exp", "((class QObject*)" << d.data << ")->parent()"); + P(d, "type", "QObject *"); + P(d, "numchild", (ob->parent() ? "1" : "0")); + d.endHash(); + d << "]"; + } + d.disarm(); +} + +static void qDumpQDir(QDumper &d) +{ + const QDir &dir = *reinterpret_cast<const QDir *>(d.data); + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", dir.path()); + P(d, "type", "QDir"); + P(d, "numchild", "3"); + if (d.dumpChildren) { + d << ",children=["; + S(d, "absolutePath", dir.absolutePath()); + S(d, "canonicalPath", dir.canonicalPath()); + d << "]"; + } + d.disarm(); +} + +static void qDumpQFileInfo(QDumper &d) +{ + const QFileInfo &info = *reinterpret_cast<const QFileInfo *>(d.data); + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", info.filePath()); + P(d, "type", "QDir"); + P(d, "numchild", "3"); + if (d.dumpChildren) { + d << ",children=["; + S(d, "absolutePath", info.absolutePath()); + S(d, "absoluteFilePath", info.absoluteFilePath()); + S(d, "canonicalPath", info.canonicalPath()); + S(d, "canonicalFilePath", info.canonicalFilePath()); + S(d, "completeBaseName", info.completeBaseName()); + S(d, "completeSuffix", info.completeSuffix()); + S(d, "baseName", info.baseName()); +#ifdef Q_OS_MACX + BL(d, "isBundle", info.isBundle()); + S(d, "bundleName", info.bundleName()); +#endif + S(d, "completeSuffix", info.completeSuffix()); + S(d, "fileName", info.fileName()); + S(d, "filePath", info.filePath()); + S(d, "group", info.group()); + S(d, "owner", info.owner()); + S(d, "path", info.path()); + + I(d, "groupid", (long)info.groupId()); + I(d, "ownerid", (long)info.ownerId()); + //QFile::Permissions permissions () const + I(d, "permissions", info.permissions()); + + //QDir absoluteDir () const + //QDir dir () const + + BL(d, "caching", info.caching()); + BL(d, "exists", info.exists()); + BL(d, "isAbsolute", info.isAbsolute()); + BL(d, "isDir", info.isDir()); + BL(d, "isExecutable", info.isExecutable()); + BL(d, "isFile", info.isFile()); + BL(d, "isHidden", info.isHidden()); + BL(d, "isReadable", info.isReadable()); + BL(d, "isRelative", info.isRelative()); + BL(d, "isRoot", info.isRoot()); + BL(d, "isSymLink", info.isSymLink()); + BL(d, "isWritable", info.isWritable()); + +#ifndef QT_NO_DATESTRING + d.beginHash(); + P(d, "name", "created"); + P(d, "value", info.created().toString()); + P(d, "exp", "((QFileInfo*)" << d.data << ")->created()"); + P(d, "type", "QDateTime"); + P(d, "numchild", "1"); + d.endHash(); + + d.beginHash(); + P(d, "name", "lastModified"); + P(d, "value", info.lastModified().toString()); + P(d, "exp", "((QFileInfo*)" << d.data << ")->lastModified()"); + P(d, "type", "QDateTime"); + P(d, "numchild", "1"); + d.endHash(); + + d.beginHash(); + P(d, "name", "lastRead"); + P(d, "value", info.lastRead().toString()); + P(d, "exp", "((QFileInfo*)" << d.data << ")->lastRead()"); + P(d, "type", "QDateTime"); + P(d, "numchild", "1"); + d.endHash(); +#endif + + d << "]"; + } + d.disarm(); +} + +static void qDumpQDateTime(QDumper &d) +{ +#ifdef QT_NO_DATESTRING + qDumpUnknown(d); +#else + const QDateTime &date = *reinterpret_cast<const QDateTime *>(d.data); + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", date.toString()); + P(d, "type", "QDateTime"); + P(d, "numchild", "3"); + if (d.dumpChildren) { + d << ",children=["; + BL(d, "isNull", date.isNull()); + I(d, "toTime_t", (long)date.toTime_t()); + S(d, "toString", date.toString()); + S(d, "toString_(ISO)", date.toString(Qt::ISODate)); + S(d, "toString_(SystemLocale)", date.toString(Qt::SystemLocaleDate)); + S(d, "toString_(Locale)", date.toString(Qt::LocaleDate)); + S(d, "toString", date.toString()); + + d.beginHash(); + P(d, "name", "toUTC"); + P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::UTC)"); + P(d, "type", "QDateTime"); + P(d, "numchild", "1"); + d.endHash(); + + d.beginHash(); + P(d, "name", "toLocalTime"); + P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::LocalTime)"); + P(d, "type", "QDateTime"); + P(d, "numchild", "1"); + d.endHash(); + + d << "]"; + } + d.disarm(); +#endif // ifdef QT_NO_DATESTRING +} + +static void qDumpQString(QDumper &d) +{ + const QString &str = *reinterpret_cast<const QString *>(d.data); + + // Try to provoke segfaults early to prevent the frontend + // from asking for unavailable child details + if (!str.isEmpty()) { + volatile ushort dummy = 0; + dummy += str.at(0).unicode(); + dummy += str.at(str.size() - 1).unicode(); + } + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", str); + P(d, "type", "QString"); + P(d, "numchild", "0"); + d.disarm(); +} + +static void qDumpQStringList(QDumper &d) +{ + const QStringList &list = *reinterpret_cast<const QStringList *>(d.data); + int n = list.size(); + if (n < 0) + qProvokeSegFault(); + if (n > 0) { + qCheckAccess(&list.front()); + qCheckAccess(&list.back()); + } + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<" << n << " items>"); + P(d, "valuedisabled", "true"); + P(d, "numchild", n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + for (int i = 0; i != n; ++i) { + S(d, "[" << i << "]", list[i]); + } + if (n < list.size()) { + d.beginHash(); + P(d, "value", "<incomplete>"); + P(d, "type", " "); + P(d, "numchild", "0"); + d.endHash(); + } + d << "]"; + } + d.disarm(); +} + +static void qDumpQVariantHelper(const void *data, QString *value, + QString *exp, int *numchild) +{ + const QVariant &v = *reinterpret_cast<const QVariant *>(data); + switch (v.type()) { + case QVariant::Invalid: + *value = QLatin1String("<invalid>"); + *numchild = 0; + break; + case QVariant::String: + *value = QLatin1Char('"') + v.toString() + QLatin1Char('"'); + *numchild = 0; + break; + case QVariant::StringList: + *exp = QString(QLatin1String("((QVariant*)%1)->d.data.c")) + .arg((qulonglong)data); + *numchild = v.toStringList().size(); + break; + case QVariant::Int: + *value = QString::number(v.toInt()); + *numchild= 0; + break; + case QVariant::Double: + *value = QString::number(v.toDouble()); + *numchild = 0; + break; + default: + // FIXME + //*exp = QString("qVariantValue<" << v.typeName() << ">" + // << "(*(QVariant*)" << data << ")"); + break; + } +} + +static void qDumpQVariant(QDumper &d) +{ + const QVariant &v = *reinterpret_cast<const QVariant *>(d.data); + QString value; + QString exp; + int numchild = 0; + qDumpQVariantHelper(d.data, &value, &exp, &numchild); + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "(" << v.typeName() << ") " << qPrintable(value)); + P(d, "type", "QVariant"); + P(d, "numchild", 1); + if (d.dumpChildren) { + d << ",children=["; + d.beginHash(); + P(d, "name", "value"); + if (!exp.isEmpty()) + P(d, "exp", qPrintable(exp)); + if (!value.isEmpty()) + P(d, "value", qPrintable(value)); + P(d, "type", v.typeName()); + P(d, "numchild", numchild); + d.endHash(); + d << "]"; + } + d.disarm(); +} + +static void qDumpQList(QDumper &d) +{ + // This uses the knowledge that QList<T> has only a single member + // of type union { QListData p; QListData::Data *d; }; + const QListData &ldata = *reinterpret_cast<const QListData*>(d.data); + const QListData::Data *pdata = *reinterpret_cast<const QListData::Data* const*>(d.data); + int nn = ldata.size(); + if (nn < 0) + qProvokeSegFault(); + if (nn > 0) { + qCheckAccess(ldata.d->array); + //qCheckAccess(ldata.d->array[0]); + //qCheckAccess(ldata.d->array[nn - 1]); + } + + int n = nn; + P(d, "iname", d.iname); + P(d, "value", "<" << n << " items>"); + P(d, "valuedisabled", "true"); + P(d, "numchild", n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + for (int i = 0; i != n; ++i) { + d.beginHash(); + P(d, "name", "[" << i << "]"); + // The exact condition here is: + // QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic + // but this data is not available in the compiled binary. + // So as first approximation only do the 'isLarge' check: + void *p = &(ldata.d->array[i + pdata->begin]); + unsigned long voidpsize = sizeof(void*); + P(d, "exp", "(sizeof(" << d.innertype << ")>" << voidpsize << + "?(**(" << d.innertype << "**)(" << p << "))" + ":(*(" << d.innertype << "*)(" << p << ")))"); + P(d, "type", d.innertype); + d.endHash(); + } + if (n < nn) { + d << ",{"; + P(d, "value", "<incomplete>"); + d.endHash(); + } + d << "]"; + } + d.disarm(); +} + +static void qDumpQVector(QDumper &d) +{ + // Use 'int' as representative value. No way (and no need) + // to deduce proper type here. + const QVector<int> &vec = *reinterpret_cast<const QVector<int> *>(d.data); + const int nn = vec.size(); + + // Try to provoke segfaults early to prevent the frontend + // from asking for unavailable child details + if (nn < 0) + qProvokeSegFault(); + if (nn > 0) { + qCheckAccess(&vec.front()); + qCheckAccess(&vec.back()); + } + + //int innersize = 0; + //scanf(qDumpInBuffer, "%d", &innersize); + + int n = nn; + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<" << n << " items>"); + P(d, "valuedisabled", "true"); + P(d, "numchild", n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + for (int i = 0; i != n; ++i) { + if (i) + d << ","; + d.beginHash(); + P(d, "name", "[" << i << "]"); + P(d, "exp", "(" << d.exp << ".d->array[" << i << "])"); + P(d, "type", d.innertype); + d.endHash(); + } + if (n < nn) { + d << ",{"; + P(d, "value", "<incomplete>"); + d.endHash(); + } + d << "]"; + } + d.disarm(); +} + +static void qDumpQHashNode(QDumper &d) +{ + struct NodeOS { void *next; uint k; uint v; } nodeOS; // int-key optimization, small value + struct NodeOL { void *next; uint k; void *v; } nodeOL; // int-key optimiatzion, large value + struct NodeNS { void *next; uint h; uint k; uint v; } nodeNS; // no optimization, small value + struct NodeNL { void *next; uint h; uint k; void *v; } nodeNL; // no optimization, large value + struct NodeL { void *next; uint h; void *k; void *v; } nodeL; // complex key + + // offsetof(...,...) not yet in Standard C++ + const ulong nodeOSk ( (char *)&nodeOS.k - (char *)&nodeOS ); + const ulong nodeOSv ( (char *)&nodeOS.v - (char *)&nodeOS ); + const ulong nodeOLk ( (char *)&nodeOL.k - (char *)&nodeOL ); + const ulong nodeOLv ( (char *)&nodeOL.v - (char *)&nodeOL ); + const ulong nodeNSk ( (char *)&nodeNS.k - (char *)&nodeNS ); + const ulong nodeNSv ( (char *)&nodeNS.v - (char *)&nodeNS ); + const ulong nodeNLk ( (char *)&nodeNL.k - (char *)&nodeNL ); + const ulong nodeNLv ( (char *)&nodeNL.v - (char *)&nodeNL ); + const ulong nodeLk ( (char *)&nodeL.k - (char *)&nodeL ); + const ulong nodeLv ( (char *)&nodeL.v - (char *)&nodeL ); + + const QHashData *h = reinterpret_cast<const QHashData *>(d.data); + const char *keyType = d.templateParameters[0]; + const char *valueType = d.templateParameters[1]; + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", ""); + P(d, "numchild", 2); + if (d.dumpChildren) { + // there is a hash specialization in cast the key are integers or shorts + bool isOptimizedIntKey = qstrcmp(keyType, "int") == 0 +#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN + || qstrcmp(keyType, "short") == 0 + || qstrcmp(keyType, "ushort") == 0 +#endif + || qstrcmp(keyType, "uint") == 0; + + d << ",children=["; + d.beginHash(); + P(d, "name", "key"); + P(d, "type", keyType); + unsigned long intsize = sizeof(int); + if (isOptimizedIntKey) { + P(d, "exp", "*(" << keyType << "*)" + "(((sizeof(" << valueType << ")>" << intsize << ")?" + << nodeOLk << ":" << nodeOSk << + ")+(char*)" << h << ")"); + } else { + P(d, "exp", "*(" << keyType << "*)" + "(((sizeof(" << keyType << ")>" << intsize << ")?" + << nodeLk << ":" + "((sizeof(" << valueType << ")>" << intsize << ")?" + << nodeNLk << ":" << nodeNSk << "))+(char*)" << h << ")"); + } + d.endHash(); + d.beginHash(); + P(d, "name", "value"); + P(d, "type", valueType); + if (isOptimizedIntKey) { + P(d, "exp", "*(" << valueType << "*)" + "(((sizeof(" << valueType << ")>" << intsize << ")?" + << nodeOLv << ":" << nodeOSv << ")+(char*)" << h << ")"); + } else { + P(d, "exp", "*(" << valueType << "*)" + "(((sizeof(" << keyType << ")>" << intsize << ")?" << nodeLv << ":" + "((sizeof(" << valueType << ")>" << intsize << ")?" + << nodeNLv << ":" << nodeNSv << "))+(char*)" << h << ")"); + } + d.endHash(); + d << "]"; + } + d.disarm(); +} + +static void qDumpQHash(QDumper &d) +{ + QHashData *h = *reinterpret_cast<QHashData *const*>(d.data); + const char *keyType = d.templateParameters[0]; + const char *valueType = d.templateParameters[1]; + + qCheckPointer(h->fakeNext); + qCheckPointer(h->buckets); + + int n = h->size; + + if (n < 0) + qProvokeSegFault(); + if (n > 0) { + qCheckPointer(h->fakeNext); + qCheckPointer(*h->buckets); + } + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<" << n << " items>"); + P(d, "numchild", n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + + QHashData::Node *node = h->firstNode(); + QHashData::Node *end = reinterpret_cast<QHashData::Node *>(h); + int i = 0; + + while (node != end) { + d.beginHash(); + P(d, "name", "[" << i << "]"); + P(d, "type", "QHashNode<" << keyType << "," << valueType << " >"); + P(d, "exp", "*(QHashNode<" << keyType << "," << valueType << " >*)" << node); + d.endHash(); + + ++i; + node = QHashData::nextNode(node); + } + d << "]"; + } + d.disarm(); +} + +static void qDumpQMapNode(QDumper &d) +{ + const QMapData *h = reinterpret_cast<const QMapData *>(d.data); + const char *keyType = d.templateParameters[0]; + const char *valueType = d.templateParameters[1]; + + qCheckAccess(h->backward); + qCheckAccess(h->forward[0]); + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", ""); + P(d, "numchild", 2); + if (d.dumpChildren) { + unsigned long voidpsize = sizeof(void*); + d << ",children=["; + d.beginHash(); + P(d, "name", "key"); + P(d, "type", keyType); + P(d, "exp", "*(" << keyType << "*)" + << "(" + << 2 * voidpsize + << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" + << "+(char*)" << h + << ")"); + d.endHash(); + d.beginHash(); + P(d, "name", "value"); + P(d, "type", valueType); + P(d, "exp", "*(" << valueType << "*)" + << "(" + << "(size_t)&(('QMap<" << keyType << "," << valueType << ">::Node'*)0)->value" + << "+" << 2 * voidpsize + << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" + << "+(char*)" << h + << ")"); + d.endHash(); + d << "]"; + } + + d.disarm(); +} + +static void qDumpQMap(QDumper &d) +{ + QMapData *h = *reinterpret_cast<QMapData *const*>(d.data); + const char *keyType = d.templateParameters[0]; + const char *valueType = d.templateParameters[1]; + + int n = h->size; + + if (n < 0) + qProvokeSegFault(); + if (n > 0) { + qCheckAccess(h->backward); + qCheckAccess(h->forward[0]); + qCheckPointer(h->backward->backward); + qCheckPointer(h->forward[0]->backward); + } + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<" << n << " items>"); + P(d, "numchild", n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + + QMapData::Node *node = reinterpret_cast<QMapData::Node *>(h->forward[0]); + QMapData::Node *end = reinterpret_cast<QMapData::Node *>(h); + int i = 0; + + while (node != end) { + d.beginHash(); + P(d, "name", "[" << i << "]"); + P(d, "type", "QMap<" << keyType << "," << valueType << ">::Node"); + P(d, "exp", "*('QMap<" << keyType << "," << valueType << ">::Node'*)" << node); + d.endHash(); + + ++i; + node = node->forward[0]; + } + d << "]"; + } + + d.disarm(); +} + +static void qDumpQSet(QDumper &d) +{ + // This uses the knowledge that QHash<T> has only a single member + // of union { QHashData *d; QHashNode<Key, T> *e; }; + QHashData *hd = *(QHashData**)d.data; + QHashData::Node *node = hd->firstNode(); + + int n = hd->size; + if (n < 0) + qProvokeSegFault(); + if (n > 0) { + qCheckAccess(node); + qCheckPointer(node->next); + } + + P(d, "iname", d.iname); + P(d, "addr", d.data); + P(d, "value", "<" << n << " items>"); + P(d, "valuedisabled", "true"); + P(d, "numchild", 2 * n); + if (d.dumpChildren) { + if (n > 100) + n = 100; + d << ",children=["; + int i = 0; + for (int bucket = 0; bucket != hd->numBuckets; ++bucket) { + for (node = hd->buckets[bucket]; node->next; node = node->next) { + d.beginHash(); + P(d, "name", "[" << i << "]"); + P(d, "type", d.innertype); + P(d, "exp", "(('QHashNode<" << d.innertype + << ",QHashDummyValue>'*)" + << static_cast<const void*>(node) << ")->key" + ); + d.endHash(); + ++i; + } + } + d << "]"; + } + d.disarm(); +} + +static void handleProtocolVersion2(QDumper & d) +{ + if (!d.outertype[0]) { + qDumpUnknown(d); + return; + } + + d.setupTemplateParameters(); + // d.outertype[0] is usally 'Q', so don't use it + switch (d.outertype[1]) { + case 'D': + if (qstrcmp(d.outertype, "QDateTime") == 0) + qDumpQDateTime(d); + else if (qstrcmp(d.outertype, "QDir") == 0) + qDumpQDir(d); + break; + case 'F': + if (qstrcmp(d.outertype, "QFileInfo") == 0) + qDumpQFileInfo(d); + break; + case 'H': + if (qstrcmp(d.outertype, "QHash") == 0) + qDumpQHash(d); + else if (qstrcmp(d.outertype, "QHashNode") == 0) + qDumpQHashNode(d); + break; + case 'L': + if (qstrcmp(d.outertype, "QList") == 0) + qDumpQList(d); + break; + case 'M': + if (qstrcmp(d.outertype, "QMap") == 0) + qDumpQMap(d); + else if (qstrcmp(d.outertype, "QMap::Node") == 0) + qDumpQMapNode(d); + break; + case 'O': + if (qstrcmp(d.outertype, "QObject") == 0) + qDumpQObject(d); + break; + case 'P': + if (qstrcmp(d.outertype, "QPropertyList") == 0) + qDumpQPropertyList(d); + break; + case 'S': + if (qstrcmp(d.outertype, "QSet") == 0) + qDumpQSet(d); + else if (qstrcmp(d.outertype, "QString") == 0) + qDumpQString(d); + else if (qstrcmp(d.outertype, "QStringList") == 0) + qDumpQStringList(d); + break; + case 'V': + if (qstrcmp(d.outertype, "QVariant") == 0) + qDumpQVariant(d); + else if (qstrcmp(d.outertype, "QVector") == 0) + qDumpQVector(d); + break; + } + + if (!d.success) + qDumpUnknown(d); +} + +} // anonymous namespace + + +extern "C" Q_CORE_EXPORT void qDumpObjectData( + int protocolVersion, + int token, + const char *outertype, + const char *iname, + const char *exp, + const char *innertype, + const void *data, + bool dumpChildren) +{ + if (protocolVersion == 1) { + // used to test whether error output gets through + //fprintf(stderr, "using stderr, qDebug follows: %d\n", token); + //qDebug() << "using qDebug, stderr already used: " << token; + } + + else if (protocolVersion == 2) { + QDumper d; + d.protocolVersion = protocolVersion; + d.token = token; + d.outertype = outertype ? outertype : ""; + d.iname = iname ? iname : ""; + d.exp = exp ? exp : ""; + d.innertype = innertype ? innertype : ""; + d.data = data ? data : ""; + d.dumpChildren = dumpChildren; + handleProtocolVersion2(d); + } + + else { + qDebug() << "Unsupported protocol version" << protocolVersion; + } +} + +QT_END_NAMESPACE + +#endif // !Q_OS_WINCE && !QT_NO_QDUMPER |