Debugger: Fixes around watch editing.

assignValueInDebugger: Pass on WatchData as well, pass
on values as QVariant. Based on that, do more extensive
checks in CDB, preventing assignment of non-PODs.

Locals/Watch editing:
* Disable while running
* Edit pointer values as hex with validation.

CDB: Strip class types off reported pointer values and reformat
the values as short 0x-pointer values, introduce flag to
WatchData::source to do dumper expansion handling.

Windows: recognize int64 as int.

Register handler: Fix accessing uninitialized value.
This commit is contained in:
Friedemann Kleint
2010-09-23 13:22:08 +02:00
parent d81d90a67a
commit 13c97d652e
24 changed files with 197 additions and 136 deletions

View File

@@ -1012,30 +1012,62 @@ void CdbEngine::executeJumpToLine(const QString & /* fileName */, int /*lineNumb
warning(tr("Jump to line is not implemented")); warning(tr("Jump to line is not implemented"));
} }
void CdbEngine::assignValueInDebugger(const QString &expr, const QString &value) void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &valueV)
{ {
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << expr << value; qDebug() << Q_FUNC_INFO << w->iname << expr << valueV;
const int frameIndex = stackHandler()->currentIndex(); const int frameIndex = stackHandler()->currentIndex();
QString errorMessage; QString errorMessage;
bool success = false; bool success = false;
QApplication::setOverrideCursor(Qt::BusyCursor);
const QString iname = QLatin1String(w->iname);
const QString type = QLatin1String(w->type);
const QString newValue = valueV.toString();
showMessage(tr("Assigning '%1' to '%2' (%3)...").arg(newValue, iname, type), LogMisc);
do { do {
QString newValue; // Value must be scalar
const QVariant::Type type = valueV.type();
if (type != QVariant::Double && type != QVariant::Bool
&& type != QVariant::Int && type != QVariant::LongLong
&& type != QVariant::UInt&& type != QVariant::ULongLong) {
errorMessage = tr("Cannot assign only scalar values.");
break;
}
// Check the assigneable type
const bool isInt = isIntType(w->type);
const bool isFloat = !isInt && isFloatType(w->type);
const bool isPointer = !isInt && !isFloat && isPointerType(w->type);
if (!isInt && !isFloat & !isPointer) {
errorMessage = tr("Cannot assign values of type '%1'. Only POD-types can be assigned.").arg(type);
break;
}
CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage); CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage);
if (!sg) if (!sg)
break; break;
if (!sg->assignValue(expr, value, &newValue, &errorMessage)) QString newValueObtained;
if (!sg->assignValue(w->iname, newValue, &newValueObtained, &errorMessage))
break; break;
// Fix the crappy values returned by the symbol group (0n<Decimal>, etc).
// Return pointers as hex
if (isInt || isPointer) {
const QVariant v = CdbCore::SymbolGroupContext::getIntValue(newValueObtained);
if (v.isValid())
newValueObtained = isPointer ?
(QLatin1String("0x") + QString::number(v.toULongLong(), 16)):
v.toString();
}
// Update view // Update view
if (WatchData *fwd = watchHandler()->findItem(expr.toLatin1())) { if (WatchData *fwd = watchHandler()->findItem(w->iname)) {
fwd->setValue(newValue); fwd->setValue(newValueObtained);
watchHandler()->insertData(*fwd); watchHandler()->insertData(*fwd);
watchHandler()->updateWatchers(); watchHandler()->updateWatchers();
} }
success = true; success = true;
} while (false); } while (false);
QApplication::restoreOverrideCursor();
if (!success) { if (!success) {
const QString msg = tr("Unable to assign the value '%1' to '%2': %3").arg(value, expr, errorMessage); const QString msg = tr("Unable to assign the value '%1' to '%2' (%3): %4").
arg(newValue, expr, type, errorMessage);
warning(msg); warning(msg);
} }
} }

View File

@@ -77,7 +77,7 @@ public:
virtual void executeRunToLine(const QString &fileName, int lineNumber); virtual void executeRunToLine(const QString &fileName, int lineNumber);
virtual void executeRunToFunction(const QString &functionName); virtual void executeRunToFunction(const QString &functionName);
virtual void executeJumpToLine(const QString &fileName, int lineNumber); virtual void executeJumpToLine(const QString &fileName, int lineNumber);
virtual void assignValueInDebugger(const QString &expr, const QString &value); virtual void assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value);
virtual void executeDebuggerCommand(const QString &command); virtual void executeDebuggerCommand(const QString &command);
virtual void activateFrame(int index); virtual void activateFrame(int index);

View File

@@ -225,21 +225,17 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt
bool handled = false; bool handled = false;
do { do {
if (wd.error || !isPointerType(wd.type)) // Must be a clas pointer item and a non-null-pointer.
if (wd.error || !(wd.source & CdbSymbolGroupContext::ClassPointerBit)
|| !isPointerType(wd.type))
break; break;
const int classPos = wd.value.indexOf(" class "); quint64 address = 0;
if (classPos == -1) if (!CdbCore::SymbolGroupContext::getUnsignedHexValue(wd.value, &address) || address == 0)
break;
// Fix CDB word separator '0x00000000`0012fe10'.
QString hexAddrS = wd.value.mid(0, classPos);
if (hexAddrS.size() > 11 && hexAddrS.at(10) == QLatin1Char('`'))
hexAddrS.remove(10, 1);
if (m_hexNullPattern.exactMatch(hexAddrS))
break; break;
const QByteArray type = stripPointerType(wd.type); const QByteArray type = stripPointerType(wd.type);
WatchData derefedWd; WatchData derefedWd;
derefedWd.setType(type); derefedWd.setType(type);
derefedWd.setHexAddress(hexAddrS.toAscii()); derefedWd.address = address;
derefedWd.name = QString(QLatin1Char('*')); derefedWd.name = QString(QLatin1Char('*'));
derefedWd.iname = wd.iname + ".*"; derefedWd.iname = wd.iname + ".*";
derefedWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit; derefedWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit;
@@ -348,19 +344,42 @@ CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix,
// Fix display values: Pass through strings, convert unsigned integers // Fix display values: Pass through strings, convert unsigned integers
// to decimal ('0x5454`fedf'), remove inner templates from // to decimal ('0x5454`fedf'), remove inner templates from
// "0x4343 class list<>". // "0x4343 class list<>".
static inline QString fixValue(const QString &value, const QByteArray &type) static inline QString fixValue(const QString &value, const QByteArray &type, bool *isClassPointer)
{ {
// Pass through strings *isClassPointer = false;
if (value.endsWith(QLatin1Char('"'))) // Pass through complete strings. Note that char pointers
// (0x00A "hallo") will be reformatted below.
const QChar doubleQuote = QLatin1Char('"');
if (value.startsWith(doubleQuote) && value.endsWith(doubleQuote))
return value; return value;
const int size = value.size();
// Real Integer numbers Unsigned hex numbers (0x)/decimal numbers (0n) // Real Integer numbers Unsigned hex numbers (0x)/decimal numbers (0n)
if (type != "bool" && isIntType(type)) { if (type != "bool" && isIntType(type)) {
const QVariant intValue = CdbCore::SymbolGroupContext::getIntValue(value); const QVariant intValue = CdbCore::SymbolGroupContext::getIntValue(value);
if (intValue.isValid()) if (intValue.isValid())
return intValue.toString(); return intValue.toString();
} }
return size < 20 ? value : CdbCore::SymbolGroupContext::removeInnerTemplateType(value); /* Pointers: Fix the address and strip off lengthy class type specifications
* "0x00000000`000045AA "hallo" -> "0x45AA "hallo"
* "0x00000000`000045AA class Bla<std::....> *" -> "0x45AA" */
if (isPointerType(type)) {
quint64 ptrValue;
int endPos;
if (CdbCore::SymbolGroupContext::getUnsignedHexValue(value, &ptrValue, &endPos)) {
QString fixedValue = QString::fromAscii("0x%1").arg(ptrValue, 0, 16);
// What is the second token...
if (endPos < value.size() - 1) {
const QString token = value.mid(endPos + 1);
if (token.startsWith(QLatin1String("class")) || token.startsWith(QLatin1String("struct"))) {
*isClassPointer = true;
} else {
fixedValue += QLatin1Char(' ');
fixedValue += token;
}
} // has token
return fixedValue;
} // pointer value conversion ok
}
return value;
} }
unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd) unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd)
@@ -378,8 +397,10 @@ unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd)
if (rc & OutOfScope) { if (rc & OutOfScope) {
wd->setError(WatchData::msgNotInScope()); wd->setError(WatchData::msgNotInScope());
} else { } else {
wd->setValue(fixValue(value, type.toUtf8())); bool isClassPointer;
wd->setValue(fixValue(value, type.toUtf8(), &isClassPointer));
if (isClassPointer)
wd->source |= CdbSymbolGroupContext::ClassPointerBit;
const bool hasChildren = rc & HasChildren; const bool hasChildren = rc & HasChildren;
wd->setHasChildren(hasChildren); wd->setHasChildren(hasChildren);
if (hasChildren) if (hasChildren)

View File

@@ -65,7 +65,11 @@ class CdbSymbolGroupContext : public CdbCore::SymbolGroupContext
public: public:
// Mask bits for the source field of watch data. // Mask bits for the source field of watch data.
enum { SourceMask = 0xFF, ChildrenKnownBit = 0x0100 }; enum { SourceMask = 0xFF,
// We know the children although the WatchModel does not believe us.
ChildrenKnownBit = 0x0100,
// Is a pointer to a potentially dumpeable class.
ClassPointerBit = 0x0200 };
static CdbSymbolGroupContext *create(const QString &prefix, static CdbSymbolGroupContext *create(const QString &prefix,
CIDebugSymbolGroup *symbolGroup, CIDebugSymbolGroup *symbolGroup,

View File

@@ -440,21 +440,25 @@ QString SymbolGroupContext::symbolINameAt(unsigned long index) const
// Return hexadecimal pointer value from a CDB pointer value // Return hexadecimal pointer value from a CDB pointer value
// which look like "0x000032a" or "0x00000000`0250124a" or // which look like "0x000032a" or "0x00000000`0250124a" or
// "0x1`0250124a" on 64-bit systems. // "0x1`0250124a" on 64-bit systems.
bool SymbolGroupContext::getUnsignedHexValue(QString stringValue, quint64 *value) bool SymbolGroupContext::getUnsignedHexValue(QString stringValue, quint64 *value,
int *endPos /* = 0 */)
{ {
if (endPos)
*endPos = -1;
*value = 0; *value = 0;
if (!stringValue.startsWith(QLatin1String("0x"))) if (!stringValue.startsWith(QLatin1String("0x")))
return false; return false;
stringValue.remove(0, 2); // Chop off character values (0x76 'a') and return right end position
// Chop off character values (0x76 'a')
const int blankPos = stringValue.indexOf(QLatin1Char(' ')); const int blankPos = stringValue.indexOf(QLatin1Char(' '));
if (endPos)
*endPos = blankPos != -1 ? blankPos : stringValue.size();
if (blankPos != -1) if (blankPos != -1)
stringValue.truncate(blankPos); stringValue.truncate(blankPos);
stringValue.remove(0, 2); // Remove '0x', as checked above
// Remove 64bit separator // Remove 64bit separator
if (stringValue.size() > 9) { if (stringValue.size() > 9) {
const int sepPos = stringValue.size() - 9; if (stringValue.at(8) == QLatin1Char('`'))
if (stringValue.at(sepPos) == QLatin1Char('`')) stringValue.remove(8, 1);
stringValue.remove(sepPos, 1);
} }
bool ok; bool ok;
*value = stringValue.toULongLong(&ok, 16); *value = stringValue.toULongLong(&ok, 16);

View File

@@ -115,7 +115,7 @@ public:
// For 64bit values (returned as dec), potentially '0n..'. // For 64bit values (returned as dec), potentially '0n..'.
static bool getDecimalIntValue(QString stringValue, qint64 *value); static bool getDecimalIntValue(QString stringValue, qint64 *value);
// For pointers and 64bit values (returned as hex) // For pointers and 64bit values (returned as hex)
static bool getUnsignedHexValue(QString stringValue, quint64 *value); static bool getUnsignedHexValue(QString stringValue, quint64 *value, int *endPos = 0);
// Convenience to return an integer (hex/decimal) as matching variant (signed/unsigned). // Convenience to return an integer (hex/decimal) as matching variant (signed/unsigned).
static QVariant getIntValue(const QString &stringValue); static QVariant getIntValue(const QString &stringValue);

View File

@@ -1671,7 +1671,7 @@ void DebuggerEngine::selectThread(int)
{ {
} }
void DebuggerEngine::assignValueInDebugger(const QString &, const QString &) void DebuggerEngine::assignValueInDebugger(const Internal::WatchData *, const QString &, const QVariant &)
{ {
} }

View File

@@ -184,7 +184,7 @@ public:
virtual void attemptBreakpointSynchronization(); virtual void attemptBreakpointSynchronization();
virtual void selectThread(int index); virtual void selectThread(int index);
virtual void assignValueInDebugger(const QString &expr, const QString &value); virtual void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
virtual void removeTooltip(); virtual void removeTooltip();
// Convenience // Convenience

View File

@@ -3647,11 +3647,11 @@ void GdbEngine::insertData(const WatchData &data0)
watchHandler()->insertData(data); watchHandler()->insertData(data);
} }
void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value) void GdbEngine::assignValueInDebugger(const Internal::WatchData *, const QString &expression, const QVariant &value)
{ {
postCommand("-var-delete assign"); postCommand("-var-delete assign");
postCommand("-var-create assign * " + expression.toLatin1()); postCommand("-var-create assign * " + expression.toLatin1());
postCommand("-var-assign assign " + GdbMi::escapeCString(value.toLatin1()), postCommand("-var-assign assign " + GdbMi::escapeCString(value.toString().toLatin1()),
Discardable, CB(handleVarAssign)); Discardable, CB(handleVarAssign));
} }

View File

@@ -450,7 +450,7 @@ private: ////////// View & Data Stuff //////////
virtual void setToolTipExpression(const QPoint &mousePos, virtual void setToolTipExpression(const QPoint &mousePos,
TextEditor::ITextEditor *editor, int cursorPos); TextEditor::ITextEditor *editor, int cursorPos);
virtual void assignValueInDebugger(const QString &expr, const QString &value); virtual void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
virtual void fetchMemory(MemoryViewAgent *agent, QObject *token, virtual void fetchMemory(MemoryViewAgent *agent, QObject *token,
quint64 addr, quint64 length); quint64 addr, quint64 length);

View File

@@ -543,14 +543,13 @@ void PdbEngine::setToolTipExpression(const QPoint &mousePos,
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void PdbEngine::assignValueInDebugger(const QString &expression, void PdbEngine::assignValueInDebugger(const Internal::WatchData *, const QString &expression, const QVariant &value)
const QString &value)
{ {
Q_UNUSED(expression); Q_UNUSED(expression);
Q_UNUSED(value); Q_UNUSED(value);
SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value)); SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value.toString()));
#if 0 #if 0
m_scriptEngine->evaluate(expression + QLatin1Char('=') + value); m_scriptEngine->evaluate(expression + QLatin1Char('=') + value.toString());
updateLocals(); updateLocals();
#endif #endif
} }

View File

@@ -90,7 +90,7 @@ private:
void attemptBreakpointSynchronization(); void attemptBreakpointSynchronization();
void assignValueInDebugger(const QString &expr, const QString &value); void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
void executeDebuggerCommand(const QString &command); void executeDebuggerCommand(const QString &command);
void loadSymbols(const QString &moduleName); void loadSymbols(const QString &moduleName);

View File

@@ -227,9 +227,9 @@ void QmlCppEngine::selectThread(int index)
d->m_cppEngine->selectThread(index); d->m_cppEngine->selectThread(index);
} }
void QmlCppEngine::assignValueInDebugger(const QString &expr, const QString &value) void QmlCppEngine::assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value)
{ {
d->m_activeEngine->assignValueInDebugger(expr, value); d->m_activeEngine->assignValueInDebugger(w, expr, value);
} }
QAbstractItemModel *QmlCppEngine::commandModel() const QAbstractItemModel *QmlCppEngine::commandModel() const

View File

@@ -54,7 +54,7 @@ public:
virtual void attemptBreakpointSynchronization(); virtual void attemptBreakpointSynchronization();
virtual void selectThread(int index); virtual void selectThread(int index);
virtual void assignValueInDebugger(const QString &expr, const QString &value); virtual void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
QAbstractItemModel *commandModel() const; QAbstractItemModel *commandModel() const;
QAbstractItemModel *modulesModel() const; QAbstractItemModel *modulesModel() const;

View File

@@ -488,8 +488,8 @@ void QmlEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void QmlEngine::assignValueInDebugger(const QString &expression, void QmlEngine::assignValueInDebugger(const Internal::WatchData *,
const QString &value) const QString &expression, const QVariant &valueV)
{ {
QRegExp inObject("@([0-9a-fA-F]+)->(.+)"); QRegExp inObject("@([0-9a-fA-F]+)->(.+)");
if (inObject.exactMatch(expression)) { if (inObject.exactMatch(expression)) {
@@ -500,7 +500,7 @@ void QmlEngine::assignValueInDebugger(const QString &expression,
QByteArray reply; QByteArray reply;
QDataStream rs(&reply, QIODevice::WriteOnly); QDataStream rs(&reply, QIODevice::WriteOnly);
rs << QByteArray("SET_PROPERTY"); rs << QByteArray("SET_PROPERTY");
rs << expression.toUtf8() << objectId << property << value; rs << expression.toUtf8() << objectId << property << valueV.toString();
sendMessage(reply); sendMessage(reply);
} }
} }

View File

@@ -94,7 +94,7 @@ private:
void attemptBreakpointSynchronization(); void attemptBreakpointSynchronization();
void assignValueInDebugger(const QString &expr, const QString &value); void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
void loadSymbols(const QString &moduleName); void loadSymbols(const QString &moduleName);
void loadAllSymbols(); void loadAllSymbols();
void requestModuleSymbols(const QString &moduleName); void requestModuleSymbols(const QString &moduleName);

View File

@@ -54,7 +54,7 @@ using namespace Debugger::Constants;
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
RegisterHandler::RegisterHandler(DebuggerEngine *engine) RegisterHandler::RegisterHandler(DebuggerEngine *engine)
: m_engine(engine) : m_engine(engine), m_base(15)
{ {
setNumberBase(16); setNumberBase(16);
} }

View File

@@ -581,11 +581,11 @@ void ScriptEngine::setToolTipExpression(const QPoint &mousePos,
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void ScriptEngine::assignValueInDebugger(const QString &expression, void ScriptEngine::assignValueInDebugger(const Internal::WatchData *,
const QString &value) const QString &expression, const QVariant &value)
{ {
SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value)); SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value.toString()));
m_scriptEngine->evaluate(expression + QLatin1Char('=') + value); m_scriptEngine->evaluate(expression + QLatin1Char('=') + value.toString());
updateLocals(); updateLocals();
} }

View File

@@ -88,7 +88,7 @@ private:
void attemptBreakpointSynchronization(); void attemptBreakpointSynchronization();
void assignValueInDebugger(const QString &expr, const QString &value); void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
void executeDebuggerCommand(const QString &command); void executeDebuggerCommand(const QString &command);
void loadSymbols(const QString &moduleName); void loadSymbols(const QString &moduleName);

View File

@@ -543,10 +543,9 @@ void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void TcfEngine::assignValueInDebugger(const QString &expression, void TcfEngine::assignValueInDebugger(const Internal::WatchData *, const QString &expression, const QVariant &value)
const QString &value)
{ {
XSDEBUG("ASSIGNING: " << expression + '=' + value); XSDEBUG("ASSIGNING: " << expression + '=' + value.toString());
updateLocals(); updateLocals();
} }

View File

@@ -93,7 +93,7 @@ private:
void attemptBreakpointSynchronization(); void attemptBreakpointSynchronization();
void assignValueInDebugger(const QString &expr, const QString &value); void assignValueInDebugger(const Internal::WatchData *w, const QString &expr, const QVariant &value);
void executeDebuggerCommand(const QString &command); void executeDebuggerCommand(const QString &command);
void loadSymbols(const QString &moduleName); void loadSymbols(const QString &moduleName);

View File

@@ -59,7 +59,7 @@
#include <QtGui/QTextEdit> #include <QtGui/QTextEdit>
#include <ctype.h> #include <ctype.h>
#include <utils/qtcassert.h>
// creates debug output for accesses to the model // creates debug output for accesses to the model
//#define DEBUG_MODEL 1 //#define DEBUG_MODEL 1
@@ -487,6 +487,23 @@ static inline QString formattedValue(const WatchData &data, int format)
return result; return result;
} }
// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
// "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
if (data.isEmpty() || !data.startsWith(QLatin1String("0x")))
return 0;
data.remove(0, 2);
const int blankPos = data.indexOf(QLatin1Char(' '));
if (blankPos != -1)
data.truncate(blankPos);
data.remove(QLatin1Char('`'));
bool ok;
const quint64 address = data.toULongLong(&ok, 16);
return ok ? address : quint64(0);
}
// Return the type used for editing // Return the type used for editing
static inline int editType(const WatchData &d) static inline int editType(const WatchData &d)
{ {
@@ -496,6 +513,9 @@ static inline int editType(const WatchData &d)
return d.type.contains('u') ? QVariant::ULongLong : QVariant::LongLong; return d.type.contains('u') ? QVariant::ULongLong : QVariant::LongLong;
if (isFloatType(d.type)) if (isFloatType(d.type))
return QVariant::Double; return QVariant::Double;
// Check for pointers using hex values (0xAD00 "Hallo")
if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
return QVariant::ULongLong;
return QVariant::String; return QVariant::String;
} }
@@ -506,11 +526,11 @@ static inline QVariant editValue(const WatchData &d)
case QVariant::Bool: case QVariant::Bool:
return d.value != QLatin1String("0") && d.value != QLatin1String("false"); return d.value != QLatin1String("0") && d.value != QLatin1String("false");
case QVariant::ULongLong: case QVariant::ULongLong:
if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
return QVariant(pointerValue(d.value));
return QVariant(d.value.toULongLong()); return QVariant(d.value.toULongLong());
break;
case QVariant::LongLong: case QVariant::LongLong:
return QVariant(d.value.toLongLong()); return QVariant(d.value.toLongLong());
break;
case QVariant::Double: case QVariant::Double:
return QVariant(d.value.toDouble()); return QVariant(d.value.toDouble());
default: default:
@@ -647,23 +667,6 @@ static inline QString truncateValue(QString v)
return v; return v;
} }
// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *",
// check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
if (data.isEmpty() || !data.startsWith(QLatin1String("0x")))
return 0;
data.remove(0, 2);
const int blankPos = data.indexOf(QLatin1Char(' '));
if (blankPos != -1)
data.truncate(blankPos);
data.remove(QLatin1Char('`'));
bool ok;
const quint64 address = data.toULongLong(&ok, 16);
return ok ? address : quint64(0);
}
int WatchModel::itemFormat(const WatchData &data) const int WatchModel::itemFormat(const WatchData &data) const
{ {
const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1); const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1);
@@ -672,6 +675,22 @@ int WatchModel::itemFormat(const WatchData &data) const
return m_handler->m_typeFormats.value(data.type, -1); return m_handler->m_typeFormats.value(data.type, -1);
} }
static inline QString expression(const WatchItem *item)
{
if (!item->exp.isEmpty())
return QString::fromAscii(item->exp);
if (item->address && !item->type.isEmpty()) {
return QString::fromAscii("*(%1*)%2").
arg(QLatin1String(item->type), QLatin1String(item->hexAddress()));
}
if (const WatchItem *parent = item->parent) {
if (!parent->exp.isEmpty())
return QString::fromAscii("(%1).%2")
.arg(QString::fromLatin1(parent->exp), item->name);
}
return QString();
}
QVariant WatchModel::data(const QModelIndex &idx, int role) const QVariant WatchModel::data(const QModelIndex &idx, int role) const
{ {
switch (role) { switch (role) {
@@ -689,6 +708,8 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
case LocalsEditTypeRole: case LocalsEditTypeRole:
return QVariant(editType(data)); return QVariant(editType(data));
case LocalsIntegerBaseRole: case LocalsIntegerBaseRole:
if (isPointerType(data.type)) // Pointers using 0x-convention
return QVariant(16);
return QVariant(formatToIntegerBase(itemFormat(data))); return QVariant(formatToIntegerBase(itemFormat(data)));
case Qt::EditRole: case Qt::EditRole:
case Qt::DisplayRole: { case Qt::DisplayRole: {
@@ -728,20 +749,8 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
break; break;
} }
case LocalsExpressionRole: { case LocalsExpressionRole:
if (!data.exp.isEmpty()) return QVariant(expression(item));
return data.exp;
if (data.address && !data.type.isEmpty()) {
return QString::fromAscii("*(%1*)%2").
arg(QLatin1String(data.type), QLatin1String(data.hexAddress()));
}
WatchItem *parent = item->parent;
if (parent && !parent->exp.isEmpty())
return QString::fromAscii("(%1).%2")
.arg(QString::fromLatin1(parent->exp), data.name);
return QVariant();
}
case LocalsINameRole: case LocalsINameRole:
return data.iname; return data.iname;
@@ -806,22 +815,6 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role) bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
switch (role) { switch (role) {
case RequestAssignValueRole: {
QString str = value.toString();
int i = str.indexOf('=');
if (i != -1)
engine()->assignValueInDebugger(str.left(i), str.mid(i + 1));
return true;
}
case RequestAssignTypeRole: {
QString str = value.toString();
int i = str.indexOf('=');
if (i != -1)
engine()->assignValueInDebugger(str.left(i), str.mid(i + 1));
return true;
}
case RequestShowInEditorRole: { case RequestShowInEditorRole: {
m_handler->showInEditor(); m_handler->showInEditor();
return true; return true;
@@ -895,33 +888,36 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
case RequestWatchExpressionRole: case RequestWatchExpressionRole:
m_handler->watchExpression(value.toString()); m_handler->watchExpression(value.toString());
break; break;
case RequestAssignValueRole:
engine()->assignValueInDebugger(&data, expression(&data), value);
return true;
case RequestAssignTypeRole: // TODO: Implement.
engine()->assignValueInDebugger(&data, expression(&data), value);
return true;
} }
emit dataChanged(index, index); emit dataChanged(index, index);
return true; return true;
} }
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
{ {
using namespace Qt;
if (!idx.isValid()) if (!idx.isValid())
return ItemFlags(); return Qt::ItemFlags();
// enabled, editable, selectable, checkable, and can be used both as the // enabled, editable, selectable, checkable, and can be used both as the
// source of a drag and drop operation and as a drop target. // source of a drag and drop operation and as a drop target.
static const ItemFlags notEditable = static const Qt::ItemFlags notEditable = Qt::ItemIsSelectable| Qt::ItemIsEnabled;
ItemIsSelectable static const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
// | ItemIsDragEnabled
// | ItemIsDropEnabled
// | ItemIsUserCheckable
// | ItemIsTristate
| ItemIsEnabled;
static const ItemFlags editable = notEditable | ItemIsEditable; // Disable editing if debuggee is positively running.
const bool isRunning = engine() && engine()->state() == InferiorRunOk;
if (isRunning)
return notEditable;
const WatchData &data = *watchItem(idx); const WatchData &data = *watchItem(idx);
if (data.isWatcher()) { if (data.isWatcher()) {
if (idx.column() == 0 && data.iname.count('.') == 1) if (idx.column() == 0 && data.iname.count('.') == 1)
return editable; // Watcher names are editable. return editable; // Watcher names are editable.
@@ -1341,6 +1337,7 @@ QByteArray WatchHandler::watcherName(const QByteArray &exp)
void WatchHandler::watchExpression(const QString &exp) void WatchHandler::watchExpression(const QString &exp)
{ {
QTC_ASSERT(m_engine && (m_engine->debuggerCapabilities() & AddWatcherCapability), return ; );
// Do not insert multiple placeholders. // Do not insert multiple placeholders.
if (exp.isEmpty() && m_watcherNames.contains(QByteArray())) if (exp.isEmpty() && m_watcherNames.contains(QByteArray()))
return; return;
@@ -1727,6 +1724,5 @@ void WatchHandler::removeTooltip()
m_tooltips->reinitialize(); m_tooltips->reinitialize();
m_tooltips->emitAllChanged(); m_tooltips->emitAllChanged();
} }
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -550,7 +550,7 @@ bool isIntType(const QByteArray &type)
case 'c': case 'c':
return type == "char"; return type == "char";
case 'i': case 'i':
return type == "int"; return type == "int" || type == "int64";
case 'l': case 'l':
return type == "long" return type == "long"
|| type == "long long"; || type == "long long";

View File

@@ -93,36 +93,42 @@ public:
{ {
if (index.column() == 1) { if (index.column() == 1) {
editor->setProperty("modelData", index.data(Qt::EditRole)); editor->setProperty("modelData", index.data(Qt::EditRole));
} else { return;
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor); }
QTC_ASSERT(lineEdit, return); QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
if (index.column() == 0) {
// Watch window: Edit expression in name column.
lineEdit->setText(index.data(LocalsExpressionRole).toString()); lineEdit->setText(index.data(LocalsExpressionRole).toString());
} else {
// To be implemented: Edit type (of a pointer, say).
lineEdit->setText(index.data(Qt::EditRole).toString());
} }
} }
void setModelData(QWidget *editor, QAbstractItemModel *model, void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const const QModelIndex &index) const
{ {
const QString exp = index.data(LocalsExpressionRole).toString();
if (index.column() == 1) { // The value column. if (index.column() == 1) { // The value column.
const QVariant value = editor->property("modelData"); const QVariant value = editor->property("modelData");
QTC_ASSERT(value.isValid(), return); QTC_ASSERT(value.isValid(), return);
const QString command = exp + QLatin1Char('=') + value.toString(); model->setData(index, value, RequestAssignValueRole);
model->setData(index, QVariant(command), RequestAssignValueRole);
return; return;
} }
//qDebug() << "SET MODEL DATA"; //qDebug() << "SET MODEL DATA";
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor); const QLineEdit *lineEdit = qobject_cast<const QLineEdit*>(editor);
QTC_ASSERT(lineEdit, return); QTC_ASSERT(lineEdit, return);
const QString value = lineEdit->text(); const QString value = lineEdit->text();
model->setData(index, value, Qt::EditRole);
if (index.column() == 2) { if (index.column() == 2) {
// The type column. // The type column.
model->setData(index, QString(exp + '=' + value), RequestAssignTypeRole); model->setData(index, QVariant(value), RequestAssignTypeRole);
} else if (index.column() == 0) { } else if (index.column() == 0) {
// The watcher name column. // The watcher name column: Change the expression.
model->setData(index, exp, RequestRemoveWatchExpressionRole); const QString exp = index.data(LocalsExpressionRole).toString();
model->setData(index, value, RequestWatchExpressionRole); if (exp != value ) {
model->setData(index, exp, RequestRemoveWatchExpressionRole);
model->setData(index, value, RequestWatchExpressionRole);
}
} }
} }