CDB: Make use of the improved debugger expression syntax

CDB can now handle sizeof(Class) and even expressions that
determine the offset of map node values (to some extent), may still
fail with templates). Format expressions accordingly, adapt
cached expressions.
Use gdbQuoteType depending on debugger type only.
Most important, do not evaluate each expression separately before
issuing the call, as ".call" can now do it on its own. Check for syntax
errors there and cache failing types as before.
This commit is contained in:
Friedemann Kleint
2009-10-09 14:11:05 +02:00
parent 9ae0575ae2
commit 8266c366a3
5 changed files with 111 additions and 82 deletions

View File

@@ -3561,8 +3561,7 @@ void *watchPoint(int x, int y)
// Helpers to write out common expression values for CDB // Helpers to write out common expression values for CDB
#ifdef Q_CC_MSVC #ifdef Q_CC_MSVC
// Offsets of a map node value which looks like // Offsets of a map node value which looks like
// "(size_t)&(('QMapNode<QString,QString >'*)0)->value")" in gdb syntax // "(size_t)&(((QMapNode<int,int> *)0)->value)-0"
template <class Key, class Value> template <class Key, class Value>
inline QDumper & putQMapNodeOffsetExpression(const char *keyType, inline QDumper & putQMapNodeOffsetExpression(const char *keyType,
const char *valueType, const char *valueType,
@@ -3570,13 +3569,13 @@ template <class Key, class Value>
{ {
QMapNode<Key, Value> *mn = 0; QMapNode<Key, Value> *mn = 0;
const int valueOffset = (char *)&(mn->value) - (char*)mn; const int valueOffset = (char *)&(mn->value) - (char*)mn;
d.put("[\"(size_t)&(('"NS"QMapNode<"); d.put("[\"(size_t)&((("NS"QMapNode<");
d.put(keyType); d.put(keyType);
d.put(','); d.put(',');
d.put(valueType); d.put(valueType);
if (valueType[qstrlen(valueType) - 1] == '>') if (valueType[qstrlen(valueType) - 1] == '>')
d.put(' '); d.put(' ');
d.put(">'*)0)->value\",\""); d.put("> *)0)->value)-0\",\"");
d.put(valueOffset); d.put(valueOffset);
d.put("\"]"); d.put("\"]");
return d; return d;
@@ -3584,7 +3583,7 @@ template <class Key, class Value>
// Helper to write out common expression values for CDB: // Helper to write out common expression values for CDB:
// Offsets of a std::pair for dumping std::map node value which look like // Offsets of a std::pair for dumping std::map node value which look like
// "(size_t)&(('std::pair<int const ,unsigned int>'*)0)->second" // "(size_t)&(((std::pair<int const ,int> *)0)->second)-0"
template <class Key, class Value> template <class Key, class Value>
inline QDumper & putStdPairValueOffsetExpression(const char *keyType, inline QDumper & putStdPairValueOffsetExpression(const char *keyType,
@@ -3593,13 +3592,13 @@ template <class Key, class Value>
{ {
std::pair<Key, Value> *p = 0; std::pair<Key, Value> *p = 0;
const int valueOffset = (char *)&(p->second) - (char*)p; const int valueOffset = (char *)&(p->second) - (char*)p;
d.put("[\"(size_t)&(('std::pair<"); d.put("[\"(size_t)&(((std::pair<");
d.put(keyType); d.put(keyType);
d.put(" const ,"); d.put(" const ,");
d.put(valueType); d.put(valueType);
if (valueType[qstrlen(valueType) - 1] == '>') if (valueType[qstrlen(valueType) - 1] == '>')
d.put(' '); d.put(' ');
d.put(">'*)0)->second\",\""); d.put("> *)0)->second)-0\",\"");
d.put(valueOffset); d.put(valueOffset);
d.put("\"]"); d.put("\"]");
return d; return d;

View File

@@ -457,7 +457,7 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
QString callCmd; QString callCmd;
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)"; QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol << "(1,0,0,0,0,0,0,0)";
const char *outData; const char *outData;
if (!callDumper(callCmd, QByteArray(), &outData, false, errorMessage)) { if (callDumper(callCmd, QByteArray(), &outData, false, errorMessage) != CallOk) {
return false; return false;
} }
if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) { if (!m_helper.parseQuery(outData, QtDumperHelper::CdbDebugger)) {
@@ -490,7 +490,8 @@ bool CdbDumperHelper::writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &b
return true; return true;
} }
bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr, CdbDumperHelper::CallResult
CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuffer, const char **outDataPtr,
bool ignoreAccessViolation, QString *errorMessage) bool ignoreAccessViolation, QString *errorMessage)
{ {
*outDataPtr = 0; *outDataPtr = 0;
@@ -499,10 +500,10 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
// write input buffer // write input buffer
if (!inBuffer.isEmpty()) { if (!inBuffer.isEmpty()) {
if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage)) if (!writeToDebuggee(m_cif->debugDataSpaces, inBuffer, m_inBufferAddress, errorMessage))
return false; return CallFailed;
} }
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, callCmd, errorMessage))
return false; return CallSyntaxError;
// Set up call and a temporary breakpoint after it. // Set up call and a temporary breakpoint after it.
// Try to skip debuggee crash exceptions and dumper exceptions // Try to skip debuggee crash exceptions and dumper exceptions
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block) // by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
@@ -514,11 +515,11 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
if (i) if (i)
goCmd = QLatin1Char('N'); goCmd = QLatin1Char('N');
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
return false; return CallFailed;
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS); HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("WaitForEvent", hr); *errorMessage = msgComFailed("WaitForEvent", hr);
return false; return CallFailed;
} }
const int newExceptionCount = exLogger.exceptionCount(); const int newExceptionCount = exLogger.exceptionCount();
// no new exceptions? -> break // no new exceptions? -> break
@@ -535,13 +536,13 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
if (exLogger.exceptionCount()) { if (exLogger.exceptionCount()) {
const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(','))); const QString exMsgs = exLogger.exceptionMessages().join(QString(QLatin1Char(',')));
*errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs); *errorMessage = QString::fromLatin1("Exceptions occurred during the dumper call: %1").arg(exMsgs);
return false; return CallFailed;
} }
// Read output // Read output
const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0); const HRESULT hr = m_cif->debugDataSpaces->ReadVirtual(m_outBufferAddress, m_buffer, m_outBufferSize, 0);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgComFailed("ReadVirtual", hr); *errorMessage = msgComFailed("ReadVirtual", hr);
return false; return CallFailed;
} }
// see QDumper implementation // see QDumper implementation
const char result = m_buffer[0]; const char result = m_buffer[0];
@@ -550,16 +551,16 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
break; break;
case '+': case '+':
*errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd); *errorMessage = QString::fromLatin1("Dumper call '%1' resulted in output overflow.").arg(callCmd);
return false; return CallFailed;
case 'f': case 'f':
*errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd); *errorMessage = QString::fromLatin1("Dumper call '%1' failed.").arg(callCmd);
return false; return CallFailed;
default: default:
*errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result)); *errorMessage = QString::fromLatin1("Dumper call '%1' failed ('%2').").arg(callCmd).arg(QLatin1Char(result));
return false; return CallFailed;
} }
*outDataPtr = m_buffer + 1; *outDataPtr = m_buffer + 1;
return true; return CallOk;
} }
static inline QString msgDumpFailed(const WatchData &wd, const QString *why) static inline QString msgDumpFailed(const WatchData &wd, const QString *why)
@@ -640,12 +641,11 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool
const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage); const DumpExecuteResult der = executeDump(wd, td, dumpChildren, result, errorMessage);
if (der == DumpExecuteOk) if (der == DumpExecuteOk)
return DumpOk; return DumpOk;
// Cache types that fail due to complicated template size expressions. if (der == CallSyntaxError) {
// Exceptions OTOH might occur when accessing variables that are not
// 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); m_failedTypes.push_back(wd.type);
if (dumpDebug)
qDebug() << "Caching failing type/expression evaluation failed for " << wd.type;
}
// log error // log error
*errorMessage = msgDumpFailed(wd, errorMessage); *errorMessage = msgDumpFailed(wd, errorMessage);
m_manager->showDebuggerOutput(LogWarning, *errorMessage); m_manager->showDebuggerOutput(LogWarning, *errorMessage);
@@ -661,37 +661,13 @@ CdbDumperHelper::DumpExecuteResult
QStringList extraParameters; QStringList extraParameters;
// Build parameter list. // Build parameter list.
m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters); m_helper.evaluationParameters(wd, td, QtDumperHelper::CdbDebugger, &inBuffer, &extraParameters);
// If the parameter list contains sizeof-expressions, execute them separately
// and replace them by the resulting numbers
const QString sizeOfExpr = QLatin1String("sizeof");
const QStringList::iterator eend = extraParameters.end();
for (QStringList::iterator it = extraParameters.begin() ; it != eend; ++it) {
// Strip 'sizeof(X)' to 'X' and query size
QString &ep = *it;
if (ep.startsWith(sizeOfExpr)) {
int size;
ep.truncate(ep.lastIndexOf(QLatin1Char(')')));
ep.remove(0, ep.indexOf(QLatin1Char('(')) + 1);
const bool sizeOk = getTypeSize(ep, &size, errorMessage);
if (loadDebug)
qDebug() << "Size" << sizeOk << size << ep;
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
QString callCmd; QString callCmd;
QTextStream(&callCmd) << ".call " << m_dumpObjectSymbol QTextStream str(&callCmd);
<< "(2,0," << wd.addr << ',' str << ".call " << m_dumpObjectSymbol << "(2,0," << wd.addr << ',' << (dumpChildren ? 1 : 0);
<< (dumpChildren ? 1 : 0) << ',' << extraParameters.join(QString(QLatin1Char(','))) << ')'; foreach(const QString &e, extraParameters)
if (loadDebug) str << ',' << e;
str << ')';
if (dumpDebug)
qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n'; qDebug() << "Query: " << wd.toString() << "\nwith: " << callCmd << '\n';
const char *outputData; const char *outputData;
// Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers. // Completely ignore EXCEPTION_ACCESS_VIOLATION crashes in the dumpers.
@@ -700,8 +676,14 @@ CdbDumperHelper::DumpExecuteResult
*errorMessage = eb.errorString(); *errorMessage = eb.errorString();
return DumpExecuteCallFailed; return DumpExecuteCallFailed;
} }
if (!callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) switch (callDumper(callCmd, inBuffer, &outputData, true, errorMessage)) {
case CallFailed:
return DumpExecuteCallFailed; return DumpExecuteCallFailed;
case CallSyntaxError:
return DumpExpressionFailed;
case CallOk:
break;
}
if (!QtDumperHelper::parseValue(outputData, result)) { if (!QtDumperHelper::parseValue(outputData, result)) {
*errorMessage = QLatin1String("Parsing of value query output failed."); *errorMessage = QLatin1String("Parsing of value query output failed.");
return DumpExecuteCallFailed; return DumpExecuteCallFailed;

View File

@@ -122,12 +122,11 @@ private:
bool getTypeSize(const QString &typeName, int *size, QString *errorMessage); bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage); bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage);
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, enum CallResult { CallOk, CallSyntaxError, CallFailed };
CallResult callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
bool ignoreAccessViolation, QString *errorMessage); bool ignoreAccessViolation, QString *errorMessage);
enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, enum DumpExecuteResult { DumpExecuteOk, DumpExpressionFailed, DumpExecuteCallFailed };
DumpComplexExpressionEncountered,
DumpExecuteCallFailed };
DumpExecuteResult executeDump(const WatchData &wd, DumpExecuteResult executeDump(const WatchData &wd,
const QtDumperHelper::TypeData& td, bool dumpChildren, const QtDumperHelper::TypeData& td, bool dumpChildren,
QList<WatchData> *result, QString *errorMessage); QList<WatchData> *result, QString *errorMessage);

View File

@@ -366,11 +366,11 @@ GuessChildrenResult guessChildren(const QString &type)
return HasPossiblyChildren; return HasPossiblyChildren;
} }
QString sizeofTypeExpression(const QString &type) QString sizeofTypeExpression(const QString &type, QtDumperHelper::Debugger debugger)
{ {
if (type.endsWith(QLatin1Char('*'))) if (type.endsWith(QLatin1Char('*')))
return QLatin1String("sizeof(void*)"); return QLatin1String("sizeof(void*)");
if (type.endsWith(QLatin1Char('>'))) if (debugger != QtDumperHelper::GdbDebugger || type.endsWith(QLatin1Char('>')))
return QLatin1String("sizeof(") + type + QLatin1Char(')'); return QLatin1String("sizeof(") + type + QLatin1Char(')');
return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')'); return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')');
} }
@@ -838,7 +838,7 @@ QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
// Format an expression to have the debugger query the // Format an expression to have the debugger query the
// size. Use size cache if possible // size. Use size cache if possible
QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName, QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
Debugger /* debugger */) const Debugger debugger) const
{ {
// Look up special size types // Look up special size types
const SpecialSizeType st = specialSizeType(typeName); const SpecialSizeType st = specialSizeType(typeName);
@@ -851,7 +851,7 @@ QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
if (sit != m_sizeCache.constEnd()) if (sit != m_sizeCache.constEnd())
return QString::number(sit.value()); return QString::number(sit.value());
// Finally have the debugger evaluate // Finally have the debugger evaluate
return sizeofTypeExpression(typeName); return sizeofTypeExpression(typeName, debugger);
} }
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const
@@ -962,24 +962,17 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0); // << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
extraArgs[2] = evaluationSizeofTypeExpression(nodetype, debugger); extraArgs[2] = evaluationSizeofTypeExpression(nodetype, debugger);
extraArgs[3] = QLatin1String("(size_t)&(('"); extraArgs[3] = qMapNodeValueOffsetExpression(nodetype, data.addr, debugger);
extraArgs[3] += nodetype;
extraArgs[3] += QLatin1String("'*)0)->value");
} }
break; break;
case QMapNodeType: case QMapNodeType:
extraArgs[2] = evaluationSizeofTypeExpression(data.type, debugger); extraArgs[2] = evaluationSizeofTypeExpression(data.type, debugger);
extraArgs[3] = QLatin1String("(size_t)&(('"); extraArgs[3] = qMapNodeValueOffsetExpression(data.type, data.addr, debugger);
extraArgs[3] += data.type;
extraArgs[3] += QLatin1String("'*)0)->value");
break; break;
case StdVectorType: case StdVectorType:
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners; //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
if (inners.at(0) == QLatin1String("bool")) { if (inners.at(0) == QLatin1String("bool")) {
outertype = QLatin1String("std::vector::bool"); outertype = QLatin1String("std::vector::bool");
} else {
//extraArgs[extraArgCount++] = evaluationSizeofTypeExpression(data.type, debugger);
//extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
} }
break; break;
case StdDequeType: case StdDequeType:
@@ -995,27 +988,40 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
extraArgs[2] = zero; extraArgs[2] = zero;
break; break;
case StdMapType: { case StdMapType: {
// We don't want the comparator and the allocator confuse gdb. // We need the offset of the second item in the value pair.
// But we need the offset of the second item in the value pair.
// We read the type of the pair from the allocator argument because // We read the type of the pair from the allocator argument because
// that gets the constness "right" (in the sense that gdb can // that gets the constness "right" (in the sense that gdb/cdb can
// read it back: "std::allocator<std::pair<Key,Value> >" // read it back: "std::allocator<std::pair<Key,Value> >"
// -> "std::pair<Key,Value>". Different debuggers have varying // -> "std::pair<Key,Value>". Different debuggers have varying
// amounts of terminating blanks... // amounts of terminating blanks...
extraArgs[2].clear();
extraArgs[3] = zero;
QString pairType = inners.at(3); QString pairType = inners.at(3);
int bracketPos = pairType.indexOf(QLatin1Char('<')); int bracketPos = pairType.indexOf(QLatin1Char('<'));
if (bracketPos != -1) if (bracketPos != -1)
pairType.remove(0, bracketPos + 1); pairType.remove(0, bracketPos + 1);
// We don't want the comparator and the allocator confuse gdb.
const QChar closingBracket = QLatin1Char('>'); const QChar closingBracket = QLatin1Char('>');
bracketPos = pairType.lastIndexOf(closingBracket); bracketPos = pairType.lastIndexOf(closingBracket);
if (bracketPos != -1) if (bracketPos != -1)
bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1); bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1);
if (bracketPos != -1) if (bracketPos != -1)
pairType.truncate(bracketPos + 1); pairType.truncate(bracketPos + 1);
if (debugger == GdbDebugger) {
extraArgs[2] = QLatin1String("(size_t)&(('"); extraArgs[2] = QLatin1String("(size_t)&(('");
extraArgs[2] += pairType; extraArgs[2] += pairType;
extraArgs[2] += QLatin1String("'*)0)->second"); extraArgs[2] += QLatin1String("'*)0)->second");
extraArgs[3] = zero; } 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; break;
case StdStringType: case StdStringType:
@@ -1068,6 +1074,45 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs; qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs;
} }
// 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 // GdbMi parsing helpers for parsing dumper value results
static bool gdbMiGetIntValue(int *target, static bool gdbMiGetIntValue(int *target,

View File

@@ -79,7 +79,6 @@ bool isSymbianIntType(const QString &type);
enum GuessChildrenResult { HasChildren, HasNoChildren, HasPossiblyChildren }; enum GuessChildrenResult { HasChildren, HasNoChildren, HasPossiblyChildren };
GuessChildrenResult guessChildren(const QString &type); GuessChildrenResult guessChildren(const QString &type);
QString sizeofTypeExpression(const QString &type);
QString quoteUnprintableLatin1(const QByteArray &ba); QString quoteUnprintableLatin1(const QByteArray &ba);
// Editor tooltip support // Editor tooltip support
@@ -193,6 +192,11 @@ private:
static Type specialType(QString s); static Type specialType(QString s);
QString evaluationSizeofTypeExpression(const QString &typeName, Debugger d) const; QString evaluationSizeofTypeExpression(const QString &typeName, Debugger d) const;
void parseQueryTypes(const QStringList &l, Debugger debugger); void parseQueryTypes(const QStringList &l, Debugger debugger);
QString qMapNodeValueOffsetExpression(const QString &type,
const QString &addressIn,
Debugger debugger) const;
inline QString lookupCdbDummyAddressExpression(const QString &expr, const QString &address) const;
NameTypeMap m_nameTypeMap; NameTypeMap m_nameTypeMap;
SizeCache m_sizeCache; SizeCache m_sizeCache;