Commit 6e93f454 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Display Q<>Pointers/Work towards displaying maps/QMaps in CDB.

Make dumpers pass on more size information initially, namely the
various Q<>Pointers. Introduce enum for those special template types
whose size does not vary with the arguments (Q<>Pointer,
std::allocators...) to make it more easily extensible.
Pass on some common QMapNode<> sizes as well.
Introduce an expression cache to the common QtDumperHelper
class and make dumpers pass some common expression values
(value offsets of common QMapNode<> incarnations).
Make CDBDumperHelper use the expression cache.
Extend dumper tester.
parent 7196c94c
......@@ -50,6 +50,7 @@
#if QT_VERSION >= 0x040500
#include <QtCore/QSharedPointer>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QSharedData>
#include <QtCore/QWeakPointer>
#endif
......@@ -1345,6 +1346,11 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val
}
}
#ifdef Q_CC_MSVC
# define MAP_NODE_TYPE_END ">"
#else
# define MAP_NODE_TYPE_END " >"
#endif
static void qDumpQHash(QDumper &d)
{
......@@ -1407,7 +1413,7 @@ static void qDumpQHash(QDumper &d)
d.endItem();
d.beginItem("type");
d.put("'"NS"QHashNode<").put(keyType).put(",")
.put(valueType).put(" >'");
.put(valueType).put(MAP_NODE_TYPE_END"'");
d.endItem();
}
d.endHash();
......@@ -1763,7 +1769,7 @@ static void qDumpQMap(QDumper &d)
// actually, any type (even 'char') will do...
d.beginItem("type");
d.put(NS"QMapNode<").put(keyType).put(",");
d.put(valueType).put(" >");
d.put(valueType).put(MAP_NODE_TYPE_END);
d.endItem();
d.beginItem("exp");
d.put("*('"NS"QMapNode<").put(keyType).put(",");
......@@ -1776,7 +1782,7 @@ static void qDumpQMap(QDumper &d)
#else
d.beginItem("type");
d.put(NS"QMapData::Node<").put(keyType).put(",");
d.put(valueType).put(" >");
d.put(valueType).put(MAP_NODE_TYPE_END);
d.endItem();
d.beginItem("exp");
d.put("*('"NS"QMapData::Node<").put(keyType).put(",");
......@@ -3036,6 +3042,27 @@ void *watchPoint(int x, int y)
}
#endif
// Helper to write out common expression values for CDB:
// Offsets of a map node value which looks like
// "(size_t)&(('QMapNode<QString,QString >'*)0)->value")" in gdb syntax
template <class Key, class Value>
inline QDumper & putQMapNodeOffsetExpression(const char *keyType,
const char *valueType,
QDumper &d)
{
QMapNode<Key, Value> *mn = 0;
const int valueOffset = (char *)&(mn->value) - (char*)mn;
d.put("(size_t)&(('"NS"QMapNode<");
d.put(keyType);
d.put(',');
d.put(valueType);
d.put(">'*)0)->value=\"");
d.put(valueOffset);
d.put('"');
return d;
}
extern "C" Q_DECL_EXPORT
void *qDumpObjectData440(
int protocolVersion,
......@@ -3136,8 +3163,30 @@ void *qDumpObjectData440(
#endif
.put("std::string=\"").put(sizeof(std::string)).put("\",")
.put("std::wstring=\"").put(sizeof(std::wstring)).put("\",")
.put("std::allocator=\"").put(sizeof(std::allocator<int>))
.put("std::allocator=\"").put(sizeof(std::allocator<int>)).put("\",")
#if QT_VERSION >= 0x040500
.put(NS"QSharedPointer=\"").put(sizeof(QSharedPointer<int>)).put("\",")
.put(NS"QSharedDataPointer=\"").put(sizeof(QSharedDataPointer<QSharedData>)).put("\",")
.put(NS"QWeakPointer=\"").put(sizeof(QWeakPointer<int>)).put("\",")
#endif
.put("QPointer=\"").put(sizeof(QPointer<QObject>)).put("\",")
// Common map node types
.put(NS"QMapNode<int,int>=\"").put(sizeof(QMapNode<int,int >)).put("\",")
.put(NS"QMapNode<int,"NS"QString>=\"").put(sizeof(QMapNode<int, QString>)).put("\",")
.put(NS"QMapNode<int,"NS"QVariant>=\"").put(sizeof(QMapNode<int, QVariant>)).put("\",")
.put(NS"QMapNode<"NS"QString,int>=\"").put(sizeof(QMapNode<QString, int>)).put("\",")
.put(NS"QMapNode<"NS"QString,"NS"QString>=\"").put(sizeof(QMapNode<QString, QString>)).put("\",")
.put(NS"QMapNode<"NS"QString,"NS"QVariant>=\"").put(sizeof(QMapNode<QString, QVariant>))
.put("\"}");
// Write out common expression values for CDB
d.put(",expressions={");
putQMapNodeOffsetExpression<int,int>("int", "int", d).put(',');
putQMapNodeOffsetExpression<int,QString>("int", NS"QString", d).put(',');
putQMapNodeOffsetExpression<int,QVariant>("int", NS"QVariant", d).put(',');
putQMapNodeOffsetExpression<QString,int>(NS"QString", "int", d).put(',');
putQMapNodeOffsetExpression<QString,QString>(NS"QString", NS"QString", d).put(',');
putQMapNodeOffsetExpression<QString,QVariant>(NS"QString", NS"QVariant", d);
d.put('}');
d.disarm();
}
......
......@@ -29,12 +29,15 @@
#include <QtCore/QStringList>
#include <QtCore/QVector>
#include <QtCore/QSharedPointer>
#include <QtCore/QTimer>
#include <QtCore/QMap>
#include <string>
#include <list>
#include <vector>
#include <set>
#include <map>
#include <stdio.h>
#include <string.h>
......@@ -102,6 +105,17 @@ static int dumpQString()
return 0;
}
static int dumpQSharedPointerQString()
{
QSharedPointer<QString> test(new QString(QLatin1String("hallo")));
prepareInBuffer("QSharedPointer", "local.sharedpointerqstring", "local.local.sharedpointerqstring", "QString");
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), 0, 0, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
QString uninitialized;
return 0;
}
static int dumpQStringList()
{
QStringList test = QStringList() << QLatin1String("item1") << QLatin1String("item2");
......@@ -132,6 +146,34 @@ static int dumpQIntVector()
return 0;
}
static int dumpQMapIntInt()
{
QMap<int,int> test;
QMapNode<int,int> mapNode;
const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode;
test.insert(42, 43);
test.insert(43, 44);
prepareInBuffer("QMap", "local.qmapintint", "local.qmapintint", "int@int");
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(int), sizeof(mapNode), valueOffset);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
}
static int dumpQMapIntString()
{
QMap<int,QString> test;
QMapNode<int,QString> mapNode;
const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode;
test.insert(42, QLatin1String("fortytwo"));
test.insert(43, QLatin1String("fortytree"));
prepareInBuffer("QMap", "local.qmapintqstring", "local.qmapintqstring", "int@QString");
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(QString), sizeof(mapNode), valueOffset);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
}
// --------------- std types
static int dumpStdString()
......@@ -238,6 +280,20 @@ static int dumpStdStringSet()
return 0;
}
static int dumpStdMapIntString()
{
std::map<int,std::string> test;
std::map<int,std::string>::value_type entry(42, std::string("fortytwo"));
test.insert(entry);
const int valueOffset = (char*)&(entry.second) - (char*)&entry;
prepareInBuffer("std::map", "local.stdmapintstring", "local.stdmapintstring",
"int@std::basic_string<char,std::char_traits<char>,std::allocator<char> >@std::less<int>@std::allocator<std::pair<const int,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >");
qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::string), valueOffset, 0);
fputs(qDumpOutBuffer, stdout);
fputc('\n', stdout);
return 0;
}
static int dumpQObject()
{
// Requires the childOffset to be know, but that is not critical
......@@ -249,6 +305,47 @@ static int dumpQObject()
return 0;
}
static bool dumpType(const char *arg)
{
if (!qstrcmp(arg, "QString"))
{ dumpQString(); return true; }
if (!qstrcmp(arg, "QSharedPointer<QString>"))
{ dumpQSharedPointerQString(); return true; }
if (!qstrcmp(arg, "QStringList"))
{ dumpQStringList(); return true; }
if (!qstrcmp(arg, "QList<int>"))
{ dumpQIntList(); return true; }
if (!qstrcmp(arg, "QList<std::string>"))
{ dumpStdStringQList(); return true; }
if (!qstrcmp(arg, "QVector<int>"))
{ dumpQIntVector(); return true; }
if (!qstrcmp(arg, "QMap<int,QString>"))
{ dumpQMapIntString(); return true; }
if (!qstrcmp(arg, "QMap<int,int>"))
{ dumpQMapIntInt(); return true; }
if (!qstrcmp(arg, "string"))
{ dumpStdString(); return true; }
if (!qstrcmp(arg, "wstring"))
{ dumpStdWString(); return true; }
if (!qstrcmp(arg, "list<int>"))
{ dumpStdIntList(); return true; }
if (!qstrcmp(arg, "list<string>"))
{ dumpStdStringList(); return true; }
if (!qstrcmp(arg, "vector<int>"))
{ dumpStdIntVector(); return true; }
if (!qstrcmp(arg, "vector<string>"))
{ dumpStdStringVector(); return true; }
if (!qstrcmp(arg, "set<int>"))
{ dumpStdIntSet(); return true; }
if (!qstrcmp(arg, "set<string>"))
{ dumpStdStringSet(); return true; }
if (!qstrcmp(arg, "map<int,string>"))
{ dumpStdMapIntString(); return true; }
if (!qstrcmp(arg, "QObject"))
{ dumpQObject(); return true; }
return false;
}
int main(int argc, char *argv[])
{
printf("Running query protocol\n");
......@@ -266,34 +363,8 @@ int main(int argc, char *argv[])
continue;
}
printf("\nTesting %s\n", arg);
if (!qstrcmp(arg, "QString"))
dumpQString();
if (!qstrcmp(arg, "QStringList"))
dumpQStringList();
if (!qstrcmp(arg, "QList<int>"))
dumpQIntList();
if (!qstrcmp(arg, "QList<std::string>"))
dumpStdStringQList();
if (!qstrcmp(arg, "QVector<int>"))
dumpQIntVector();
if (!qstrcmp(arg, "string"))
dumpStdString();
if (!qstrcmp(arg, "wstring"))
dumpStdWString();
if (!qstrcmp(arg, "list<int>"))
dumpStdIntList();
if (!qstrcmp(arg, "list<string>"))
dumpStdStringList();
if (!qstrcmp(arg, "vector<int>"))
dumpStdIntVector();
if (!qstrcmp(arg, "vector<string>"))
dumpStdStringVector();
if (!qstrcmp(arg, "set<int>"))
dumpStdIntSet();
if (!qstrcmp(arg, "set<string>"))
dumpStdStringSet();
if (!qstrcmp(arg, "QObject"))
dumpQObject();
if (!dumpType(arg))
printf("\nUnhandled type: %s\n", arg);
}
return 0;
}
......@@ -576,8 +576,9 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool
return DumpOk;
// Cache types that fail due to complicated template size expressions.
// Exceptions OTOH might occur when accessing variables that are not
// yet initialized in a particular breakpoint. That should be ignored
if (der == DumpExecuteSizeFailed)
// yet initialized in a particular breakpoint. That should be ignored.
// Also fail for complex expression that were not cached/replaced by the helper.
if (der == DumpExecuteSizeFailed || der == DumpComplexExpressionEncountered)
m_failedTypes.push_back(wd.type);
// log error
*errorMessage = msgDumpFailed(wd, errorMessage);
......@@ -611,6 +612,12 @@ CdbDumperHelper::DumpExecuteResult
if (!sizeOk)
return DumpExecuteSizeFailed;
ep = QString::number(size);
continue;
}
// We cannot evaluate any other expressions than 'sizeof()' ;-(
if (!ep.isEmpty() && !ep.at(0).isDigit()) {
*errorMessage = QString::fromLatin1("Unable to evaluate: '%1'").arg(ep);
return DumpComplexExpressionEncountered;
}
}
// Execute call
......
......@@ -109,7 +109,9 @@ private:
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
bool ignoreAccessViolation, QString *errorMessage);
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed };
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed,
DumpComplexExpressionEncountered,
DumpExecuteCallFailed };
DumpExecuteResult executeDump(const WatchData &wd,
const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
QList<WatchData> *result, QString *errorMessage);
......
......@@ -112,7 +112,7 @@ WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
const SharedPointerCdbDumperHelper &dumper) :
m_hexNullPattern(QLatin1String("0x0+")),
m_wh(wh),
m_dumper(dumper)
m_dumper(dumper)
{
Q_ASSERT(m_hexNullPattern.isValid());
}
......@@ -179,6 +179,10 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
case CdbDumperHelper::DumpOk:
if (debugCDBWatchHandling)
qDebug() << "dumper triggered";
// Dumpers omit types for complicated templates
if (!m_dumperResult.isEmpty() && m_dumperResult.front().type.isEmpty()
&& m_dumperResult.front().iname == wd.iname)
m_dumperResult.front().setType(wd.type);
// Discard the original item and insert the dumper results
foreach(const WatchData &dwd, m_dumperResult)
m_wh->insertData(dwd);
......@@ -245,13 +249,20 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
errorMessage);
}
// Expand dumper items (not implemented)
// Expand artifical dumper items
if (incompleteLocal.source == OwnerDumper) {
if (debugCDBWatchHandling)
qDebug() << "ignored dumper item";
WatchData wd = incompleteLocal;
wd.setAllUnneeded();
wh->insertData(wd);
QList<WatchData> dumperResult;
const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage);
if (dr == CdbDumperHelper::DumpOk) {
foreach(const WatchData &dwd, dumperResult)
wh->insertData(dwd);
} else {
const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: %1 (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage);
qWarning("%s", qPrintable(msg));
WatchData wd = incompleteLocal;
wd.setAllUnneeded();
wh->insertData(wd);
}
return true;
}
......
......@@ -560,14 +560,10 @@ void QtDumperHelper::TypeData::clear()
}
// ----------------- QtDumperHelper
const QString stdAllocatorPrefix = QLatin1String("std::allocator");
QtDumperHelper::QtDumperHelper() :
m_intSize(0),
m_pointerSize(0),
m_stdAllocatorSize(0),
m_qtVersion(0)
{
qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
}
void QtDumperHelper::clear()
......@@ -576,9 +572,8 @@ void QtDumperHelper::clear()
m_qtVersion = 0;
m_qtNamespace.clear();
m_sizeCache.clear();
m_intSize = 0;
m_pointerSize = 0;
m_stdAllocatorSize = 0;
qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
m_expressionCache.clear();
}
static inline void formatQtVersion(int v, QTextStream &str)
......@@ -593,13 +588,15 @@ QString QtDumperHelper::toString(bool debug) const
QTextStream str(&rc);
str << "version=";
formatQtVersion(m_qtVersion, str);
str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types: ";
str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: ";
const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd();
for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) {
str <<",[" << it.key() << ',' << it.value() << ']';
}
str << "Sizes: intsize=" << m_intSize << " pointer size=" << m_pointerSize
<< " allocatorsize=" << m_stdAllocatorSize;
str << "\nSpecial size: ";
for (int i = 0; i < SpecialSizeCount; i++)
str << ' ' << m_specialSizes[i];
str << "\nSize cache: ";
const SizeCache::const_iterator scend = m_sizeCache.constEnd();
for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) {
str << ' ' << it.key() << '=' << it.value();
......@@ -693,22 +690,23 @@ QtDumperHelper::Type QtDumperHelper::specialType(QString s)
return UnknownType;
}
bool QtDumperHelper::needsExpressionSyntax(Type t)
QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t)
{
switch (t) {
case QAbstractItemType:
case QObjectSlotType:
case QObjectSignalType:
case QMapType:
case QVectorType:
case QMultiMapType:
case QMapNodeType:
case StdMapType:
return true;
default:
break;
case QAbstractItemType:
case QObjectSlotType:
case QObjectSignalType:
case QVectorType:
case StdMapType:
return NeedsComplexExpression;
case QMapType:
case QMultiMapType:
case QMapNodeType:
return NeedsCachedExpression;
default:
break;
}
return false;
return NeedsNoExpression;
}
QString QtDumperHelper::qtVersionString() const
......@@ -745,7 +743,7 @@ void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger debugger)
const Type t = specialType(l.at(i));
if (t != UnknownType) {
// Exclude types that require expression syntax for CDB
if (debugger == GdbDebugger || !needsExpressionSyntax(t))
if (debugger == GdbDebugger || expressionRequirements(t) != NeedsComplexExpression)
m_nameTypeMap.insert(l.at(i), t);
} else {
m_nameTypeMap.insert(l.at(i), SupportedType);
......@@ -940,6 +938,7 @@ public:
QString qtVersion;
QStringList types;
QList<SizeEntry> sizes;
QMap<QString, QString> expressionCache;
};
inline Data data() const { return m_data; }
......@@ -952,10 +951,12 @@ protected:
virtual bool handleValue(const char *k, int size);
private:
enum Mode { None, ExpectingDumpers, ExpectingVersion, ExpectingNameSpace, ExpectingSizes };
enum Mode { None, ExpectingDumpers, ExpectingVersion,
ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache };
Mode m_mode;
Data m_data;
QString m_lastSizeType;
QString m_lastExpression;
};
QueryDumperParser::QueryDumperParser(const char *s) :
......@@ -965,10 +966,16 @@ QueryDumperParser::QueryDumperParser(const char *s) :
}
bool QueryDumperParser::handleKeyword(const char *k, int size)
{
if (m_mode == ExpectingSizes) {
{
switch (m_mode) {
case ExpectingSizes:
m_lastSizeType = QString::fromLatin1(k, size);
return true;
case ExpectingExpressionCache:
m_lastExpression = QString::fromLatin1(k, size);
return true;
default:
break;
}
if (!qstrncmp(k, "dumpers", size)) {
m_mode = ExpectingDumpers;
......@@ -986,6 +993,10 @@ bool QueryDumperParser::handleKeyword(const char *k, int size)
m_mode = ExpectingSizes;
return true;
}
if (!qstrncmp(k, "expressions", size)) {
m_mode = ExpectingExpressionCache;
return true;
}
qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData());
return false;
}
......@@ -1026,6 +1037,9 @@ bool QueryDumperParser::handleValue(const char *k, int size)
case ExpectingSizes:
m_data.sizes.push_back(SizeEntry(m_lastSizeType, QString::fromLatin1(k, size).toInt()));
break;
case ExpectingExpressionCache:
m_data.expressionCache.insert(m_lastExpression, QString::fromLatin1(k, size));
break;
}
return true;
}
......@@ -1042,25 +1056,24 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
parseQueryTypes(parser.data().types, debugger);
foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes)
addSize(se.first, se.second);
m_expressionCache = parser.data().expressionCache;
qDebug() << m_expressionCache;
return true;
}
void QtDumperHelper::addSize(const QString &name, int size)
{
// Special interest cases
if (name == QLatin1String("char*")) {
m_specialSizes[PointerSize] = size;
return;
}
const SpecialSizeType st = specialSizeType(name);
if (st != SpecialSizeCount) {
m_specialSizes[st] = size;
return;
}
do {
if (name == QLatin1String("char*")) {
m_pointerSize = size;
break;
}
if (name == QLatin1String("int")) {
m_intSize = size;
break;
}
if (name.startsWith(stdAllocatorPrefix)) {
m_stdAllocatorSize = size;
break;
}
if (name == QLatin1String("std::string")) {
m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"), size);
break;
......@@ -1104,11 +1117,13 @@ QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
Debugger /* debugger */) const
{
// Look up fixed types
if (m_pointerSize && isPointerType(typeName))
return QString::number(m_pointerSize);
if (m_stdAllocatorSize && typeName.startsWith(stdAllocatorPrefix))
return QString::number(m_stdAllocatorSize);
// Look up special size types
const SpecialSizeType st = specialSizeType(typeName);
if (st != SpecialSizeCount) {
if (const int size = m_specialSizes[st])
return QString::number(size);
}
// Look up size cache
const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName);
if (sit != m_sizeCache.constEnd())
return QString::number(sit.value());
......@@ -1116,6 +1131,42 @@ QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
return sizeofTypeExpression(typeName);
}
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName)
{
if (isPointerType(typeName))
return PointerSize;
static const QString intType = QLatin1String("int");
static const QString stdAllocatorPrefix = QLatin1String("std::allocator");
static const QString qPointerPrefix = QLatin1String("QPointer");
static const QString qSharedPointerPrefix = QLatin1String("QSharedPointer");
static const QString qSharedDataPointerPrefix = QLatin1String("QSharedDataPointer");
static const QString qWeakPointerPrefix = QLatin1String("QWeakPointer");
if (typeName == intType)
return IntSize;
if (typeName.startsWith(stdAllocatorPrefix))
return StdAllocatorSize;
if (typeName.startsWith(qPointerPrefix))
return QPointerSize;
if (typeName.startsWith(qSharedPointerPrefix))
return QSharedPointerSize;
if (typeName.startsWith(qSharedDataPointerPrefix))
return QSharedDataPointerSize;
if (typeName.startsWith(qWeakPointerPrefix))
return QWeakPointerSize;