forked from qt-creator/qt-creator
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:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -93,38 +93,44 @@ 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);
|
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
|
||||||
QTC_ASSERT(lineEdit, return);
|
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.
|
||||||
|
const QString exp = index.data(LocalsExpressionRole).toString();
|
||||||
|
if (exp != value ) {
|
||||||
model->setData(index, exp, RequestRemoveWatchExpressionRole);
|
model->setData(index, exp, RequestRemoveWatchExpressionRole);
|
||||||
model->setData(index, value, RequestWatchExpressionRole);
|
model->setData(index, value, RequestWatchExpressionRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||||
const QModelIndex &) const
|
const QModelIndex &) const
|
||||||
|
|||||||
Reference in New Issue
Block a user