Newer
Older
break;
}
if (name == QLatin1String("std::wstring")) {
m_sizeCache.insert(QLatin1String("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >"), size);
m_sizeCache.insert(QLatin1String("std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >"), size);
break;
}
} while (false);
m_sizeCache.insert(name, size);
}
QtDumperHelper::Type QtDumperHelper::type(const QString &typeName) const
{
const QtDumperHelper::TypeData td = typeData(typeName);
return td.type;
}
QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
{
TypeData td;
td.type = UnknownType;
const Type st = simpleType(typeName);
if (st != UnknownType) {
td.isTemplate = false;
return td;
}
// Try template
td.isTemplate = extractTemplate(typeName, &td.tmplate, &td.inner);
if (!td.isTemplate)
return td;
// Check the template type QMap<X,Y> -> 'QMap'
td.type = simpleType(td.tmplate);
return td;
}
// Format an expression to have the debugger query the
// size. Use size cache if possible
QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
Debugger debugger) const
// 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());
// Finally have the debugger evaluate
return sizeofTypeExpression(typeName, debugger);
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const
{
if (isPointerType(typeName))
return PointerSize;
static const QString intType = QLatin1String("int");
static const QString stdAllocatorPrefix = QLatin1String("std::allocator");
if (typeName == intType)
return IntSize;
if (typeName.startsWith(stdAllocatorPrefix))
return StdAllocatorSize;
if (typeName.startsWith(m_qPointerPrefix))
return QPointerSize;
if (typeName.startsWith(m_qSharedPointerPrefix))
return QSharedPointerSize;
if (typeName.startsWith(m_qSharedDataPointerPrefix))
return QSharedDataPointerSize;
if (typeName.startsWith(m_qWeakPointerPrefix))
return QWeakPointerSize;
if (typeName.startsWith(m_qListPrefix))
return QListSize;
if (typeName.startsWith(m_qLinkedListPrefix))
return QLinkedListSize;
if (typeName.startsWith(m_qVectorPrefix))
return QVectorSize;
if (typeName.startsWith(m_qQueuePrefix))
return QQueueSize;
return SpecialSizeCount;
}
static inline bool isInteger(const QString &n)
{
const int size = n.size();
if (!size)
return false;
for (int i = 0; i < size; i++)
if (!n.at(i).isDigit())
return false;
return true;
}
void QtDumperHelper::evaluationParameters(const WatchData &data,
const TypeData &td,
QByteArray *inBuffer,
QStringList *extraArgsIn) const
{
enum { maxExtraArgCount = 4 };
QStringList &extraArgs = *extraArgsIn;
// See extractTemplate for parameters
QStringList inners = td.inner.split(QLatin1Char('@'));
if (inners.at(0).isEmpty())
inners.clear();
for (int i = 0; i != inners.size(); ++i)
inners[i] = inners[i].simplified();
QString outertype = td.isTemplate ? td.tmplate : data.type;
// adjust the data extract
if (outertype == m_qtNamespace + QLatin1String("QWidget"))
outertype = m_qtNamespace + QLatin1String("QObject");
QString inner = td.inner;
extraArgs.clear();
if (!inners.empty()) {
// "generic" template dumpers: passing sizeof(argument)
// gives already most information the dumpers need
const int count = qMin(int(maxExtraArgCount), inners.size());
for (int i = 0; i < count; i++)
extraArgs.push_back(evaluationSizeofTypeExpression(inners.at(i), debugger));
}
int extraArgCount = extraArgs.size();
// Pad with zeros
const QString zero = QString(QLatin1Char('0'));
const int extraPad = maxExtraArgCount - extraArgCount;
for (int i = 0; i < extraPad; i++)
extraArgs.push_back(zero);
// in rare cases we need more or less:
switch (td.type) {
case QAbstractItemType:
inner = data.addr.mid(1);
break;
case QObjectSlotType:
case QObjectSignalType: {
// we need the number out of something like
// iname="local.ob.slots.2" // ".deleteLater()"?
const int pos = data.iname.lastIndexOf('.');
const QString slotNumber = data.iname.mid(pos + 1);
QTC_ASSERT(slotNumber.toInt() != -1, /**/);
extraArgs[0] = slotNumber;
}
break;
case QMapType:
case QMultiMapType: {
QString nodetype;
if (m_qtVersion >= 0x040500) {
nodetype = m_qtNamespace + QLatin1String("QMapNode");
nodetype += data.type.mid(outertype.size());
// FIXME: doesn't work for QMultiMap
nodetype = data.type + QLatin1String("::Node");
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
extraArgs[2] = evaluationSizeofTypeExpression(nodetype, debugger);
extraArgs[3] = qMapNodeValueOffsetExpression(nodetype, data.addr, debugger);
case QMapNodeType:
extraArgs[2] = evaluationSizeofTypeExpression(data.type, debugger);
extraArgs[3] = qMapNodeValueOffsetExpression(data.type, data.addr, debugger);
break;
case StdVectorType:
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
if (inners.at(0) == QLatin1String("bool")) {
outertype = QLatin1String("std::vector::bool");
}
break;
case StdDequeType:
extraArgs[1] = zero;
case StdStackType:
// remove 'std::allocator<...>':
extraArgs[1] = zero;
break;
case StdSetType:
// remove 'std::less<...>':
extraArgs[1] = zero;
// remove 'std::allocator<...>':
extraArgs[2] = zero;
break;
case StdMapType: {
// We need the offset of the second item in the value pair.
// We read the type of the pair from the allocator argument because
// that gets the constness "right" (in the sense that gdb/cdb can
// read it back: "std::allocator<std::pair<Key,Value> >"
// -> "std::pair<Key,Value>". Different debuggers have varying
// amounts of terminating blanks...
extraArgs[2].clear();
extraArgs[3] = zero;
int bracketPos = pairType.indexOf(QLatin1Char('<'));
if (bracketPos != -1)
pairType.remove(0, bracketPos + 1);
// We don't want the comparator and the allocator confuse gdb.
const QChar closingBracket = QLatin1Char('>');
bracketPos = pairType.lastIndexOf(closingBracket);
if (bracketPos != -1)
bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1);
if (bracketPos != -1)
pairType.truncate(bracketPos + 1);
if (debugger == GdbDebugger) {
extraArgs[2] = QLatin1String("(size_t)&(('");
extraArgs[2] += pairType;
extraArgs[2] += QLatin1String("'*)0)->second");
} else {
// Cdb: The std::pair is usually in scope. Still, this expression
// occasionally fails for complex types (std::string).
// We need an address as CDB cannot do the 0-trick.
// Use data address or try at least cache if missing.
const QString address = data.addr.isEmpty() ? QString::fromLatin1("DUMMY_ADDRESS") : data.addr;
QString offsetExpr;
QTextStream str(&offsetExpr);
str << "(size_t)&(((" << pairType << " *)" << address << ")->second)" << '-' << address;
extraArgs[2] = lookupCdbDummyAddressExpression(offsetExpr, address);
}
}
break;
case StdStringType:
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
if (inners.at(0) == QLatin1String("char")) {
outertype = QLatin1String("std::string");
} else if (inners.at(0) == QLatin1String("wchar_t")) {
outertype = QLatin1String("std::wstring");
}
qFill(extraArgs, zero);
break;
case UnknownType:
qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO);
break;
case SupportedType:
case QVectorType:
case QObjectType:
case QWidgetType:
// Look up expressions in the cache
if (!m_expressionCache.empty()) {
const QMap<QString, QString>::const_iterator excCend = m_expressionCache.constEnd();
const QStringList::iterator eend = extraArgs.end();
for (QStringList::iterator it = extraArgs.begin(); it != eend; ++it) {
QString &e = *it;
if (!e.isEmpty() && e != zero && !isInteger(e)) {
const QMap<QString, QString>::const_iterator eit = m_expressionCache.constFind(e);
if (eit != excCend)
e = eit.value();
}
}
}
inBuffer->clear();
inBuffer->append(outertype.toUtf8());
inBuffer->append('\0');
inBuffer->append(data.iname);
inBuffer->append(data.exp);
inBuffer->append(inner.toUtf8());
inBuffer->append(data.iname);
inBuffer->append('\0');
if (debug)
qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs;
}
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
// Return debugger expression to get the offset of a map node.
QString QtDumperHelper::qMapNodeValueOffsetExpression(const QString &type,
const QString &addressIn,
Debugger debugger) const
{
switch (debugger) {
case GdbDebugger:
return QLatin1String("(size_t)&(('") + type + QLatin1String("'*)0)->value");
case CdbDebugger: {
// Cdb: This will only work if a QMapNode is in scope.
// We need an address as CDB cannot do the 0-trick.
// Use data address or try at least cache if missing.
const QString address = addressIn.isEmpty() ? QString::fromLatin1("DUMMY_ADDRESS") : addressIn;
QString offsetExpression;
QTextStream(&offsetExpression) << "(size_t)&(((" << type
<< " *)" << address << ")->value)-" << address;
return lookupCdbDummyAddressExpression(offsetExpression, address);
}
}
return QString();
}
/* Cdb cannot do tricks like ( "&(std::pair<int,int>*)(0)->second)",
* that is, use a null pointer to determine the offset of a member.
* It tries to dereference the address at some point and fails with
* "memory access error". As a trick, use the address of the watch item
* to do this. However, in the expression cache, 0 is still used, so,
* for cache lookups, use '0' as address. */
QString QtDumperHelper::lookupCdbDummyAddressExpression(const QString &expr,
const QString &address) const
{
QString nullExpr = expr;
nullExpr.replace(address, QString(QLatin1Char('0')));
const QString rc = m_expressionCache.value(nullExpr, expr);
if (debug)
qDebug() << "lookupCdbDummyAddressExpression" << expr << rc;
return rc;
}
// GdbMi parsing helpers for parsing dumper value results
static bool gdbMiGetIntValue(int *target,
const GdbMi &node,
const char *child)
*target = -1;
const GdbMi childNode = node.findChild(child);
if (!childNode.isValid())
return false;
bool ok;
*target = childNode.data().toInt(&ok);
return ok;
// Find a string child node and assign value if it exists.
// Optionally decode.
static bool gdbMiGetStringValue(QString *target,
const GdbMi &node,
const char *child,
const char *encodingChild = 0)
target->clear();
const GdbMi childNode = node.findChild(child);
if (!childNode.isValid())
return false;
// Encoded data
if (encodingChild) {
int encoding;
if (!gdbMiGetIntValue(&encoding, node, encodingChild))
encoding = 0;
*target = decodeData(childNode.data(), encoding);
return true;
// Plain data
*target = QLatin1String(childNode.data());
static bool gdbMiGetByteArrayValue(QByteArray *target,
const GdbMi &node,
const char *child,
const char *encodingChild = 0)
{
QString str;
const bool success = gdbMiGetStringValue(&str, node, child, encodingChild);
*target = str.toLatin1();
return success;
}
static bool gdbMiGetBoolValue(bool *target,
const GdbMi &node,
const char *child)
*target = false;
const GdbMi childNode = node.findChild(child);
if (!childNode.isValid())
return false;
*target = childNode.data() == "true";
/* Context to store parameters that influence the next level children.
* (next level only, it is not further inherited). For example, the root item
* can provide a "childtype" node that specifies the type of the children. */
struct GdbMiRecursionContext
{
GdbMiRecursionContext(int recursionLevelIn = 0) :
recursionLevel(recursionLevelIn), childNumChild(-1), childIndex(0) {}
int recursionLevel;
int childNumChild;
int childIndex;
QString childType;
QByteArray parentIName;
};
static void gbdMiToWatchData(const GdbMi &root,
const GdbMiRecursionContext &ctx,
QList<WatchData> *wl)
if (debug > 1)
qDebug() << Q_FUNC_INFO << '\n' << root.toString(false, 0);
WatchData w;
QString v;
QByteArray b;
// Check for name/iname and use as expression default
if (ctx.recursionLevel == 0) {
// parents have only iname, from which name is derived
QString iname;
if (!gdbMiGetStringValue(&iname, root, "iname"))
qWarning("Internal error: iname missing");
w.iname = iname.toLatin1();
w.name = iname;
const int lastDotPos = w.name.lastIndexOf(QLatin1Char('.'));
if (lastDotPos != -1)
w.name.remove(0, lastDotPos + 1);
w.exp = w.name.toLatin1();
} else {
// Children can have a 'name' attribute. If missing, assume array index
// For display purposes, it can be overridden by "key"
if (!gdbMiGetStringValue(&w.name, root, "name")) {
w.name = QString::number(ctx.childIndex);
}
// Set iname
w.iname = ctx.parentIName;
w.iname += '.';
w.iname += w.name.toLatin1();
// Key?
QString key;
if (gdbMiGetStringValue(&key, root, "key", "keyencoded")) {
w.name = key.size() > 13 ? key.mid(0, 13) + QLatin1String("...") : key;
}
}
if (w.name.isEmpty()) {
const QString msg = QString::fromLatin1("Internal error: Unable to determine name at level %1/%2 for %3").arg(ctx.recursionLevel).arg(w.iname, QLatin1String(root.toString(true, 2)));
qWarning("%s\n", qPrintable(msg));
}
gdbMiGetStringValue(&w.displayedType, root, "displayedtype");
if (gdbMiGetByteArrayValue(&b, root, "editvalue"))
w.editvalue = b;
if (gdbMiGetByteArrayValue(&b, root, "exp"))
w.exp = b;
gdbMiGetByteArrayValue(&w.addr, root, "addr");
gdbMiGetByteArrayValue(&w.saddr, root, "saddr");
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
gdbMiGetBoolValue(&w.valueEnabled, root, "valueenabled");
gdbMiGetBoolValue(&w.valueEditable, root, "valueeditable");
if (gdbMiGetStringValue(&v, root, "valuetooltip", "valuetooltipencoded"))
w.setValue(v);
if (gdbMiGetStringValue(&v, root, "value", "valueencoded"))
w.setValue(v);
// Type from context or self
if (ctx.childType.isEmpty()) {
if (gdbMiGetStringValue(&v, root, "type"))
w.setType(v);
} else {
w.setType(ctx.childType);
}
// child count?
int numChild = -1;
if (ctx.childNumChild >= 0) {
numChild = ctx.childNumChild;
} else {
gdbMiGetIntValue(&numChild, root, "numchild");
}
if (numChild >= 0)
w.setHasChildren(numChild > 0);
wl->push_back(w);
// Parse children with a new context
if (numChild == 0)
return;
const GdbMi childrenNode = root.findChild("children");
if (!childrenNode.isValid())
return;
const QList<GdbMi> children =childrenNode.children();
if (children.empty())
return;
wl->back().setChildrenUnneeded();
GdbMiRecursionContext nextLevelContext(ctx.recursionLevel + 1);
nextLevelContext.parentIName = w.iname;
gdbMiGetStringValue(&nextLevelContext.childType, root, "childtype");
if (!gdbMiGetIntValue(&nextLevelContext.childNumChild, root, "childnumchild"))
nextLevelContext.childNumChild = -1;
foreach(const GdbMi &child, children) {
gbdMiToWatchData(child, nextLevelContext, wl);
nextLevelContext.childIndex++;
bool QtDumperHelper::parseValue(const char *data,
QList<WatchData> *l)
GdbMi root;
root.fromStringMultiple(QByteArray(data));
gbdMiToWatchData(root, GdbMiRecursionContext(), l);
QDebug operator<<(QDebug in, const QtDumperHelper::TypeData &d)
{
QDebug nsp = in.nospace();
nsp << " type=" << d.type << " tpl=" << d.isTemplate;
if (d.isTemplate)
nsp << d.tmplate << '<' << d.inner << '>';
return in;
}