forked from qt-creator/qt-creator
Debugger: Work on WatchModel performance
Don't instantiate repeating boilerplate item data in some cases (such as large arrays). This makes it necessary to access parent WatchItems in a lot more cases than before and needs another separation of WatchItem/WatchModel code to keep the dumper autotests in a functional state. For a plain std::vector<int> with 1 mio items this reduces extraction time from more than 2 minutes to about 3 seconds. Change-Id: I175c5f6ee90434a6e85342d8bb71bd10a04dd271 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com> Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
@@ -37,12 +37,6 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchData
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
@@ -118,8 +112,8 @@ bool isIntOrFloatType(const QByteArray &type)
|
||||
return isIntType(type) || isFloatType(type);
|
||||
}
|
||||
|
||||
WatchData::WatchData() :
|
||||
id(WatchData::InvalidId),
|
||||
WatchItem::WatchItem() :
|
||||
id(WatchItem::InvalidId),
|
||||
state(InitialState),
|
||||
editformat(StopDisplay),
|
||||
address(0),
|
||||
@@ -128,6 +122,7 @@ WatchData::WatchData() :
|
||||
bitpos(0),
|
||||
bitsize(0),
|
||||
elided(0),
|
||||
arrayIndex(-1),
|
||||
wantsChildren(false),
|
||||
valueEnabled(true),
|
||||
valueEditable(true),
|
||||
@@ -135,16 +130,7 @@ WatchData::WatchData() :
|
||||
{
|
||||
}
|
||||
|
||||
bool WatchData::isAncestorOf(const QByteArray &childIName) const
|
||||
{
|
||||
if (iname.size() >= childIName.size())
|
||||
return false;
|
||||
if (!childIName.startsWith(iname))
|
||||
return false;
|
||||
return childIName.at(iname.size()) == '.';
|
||||
}
|
||||
|
||||
bool WatchData::isVTablePointer() const
|
||||
bool WatchItem::isVTablePointer() const
|
||||
{
|
||||
// First case: Cdb only. No user type can be named like this, this is safe.
|
||||
// Second case: Python dumper only.
|
||||
@@ -152,7 +138,7 @@ bool WatchData::isVTablePointer() const
|
||||
|| (type.isEmpty() && name == QLatin1String("[vptr]"));
|
||||
}
|
||||
|
||||
void WatchData::setError(const QString &msg)
|
||||
void WatchItem::setError(const QString &msg)
|
||||
{
|
||||
setAllUnneeded();
|
||||
value = msg;
|
||||
@@ -161,7 +147,7 @@ void WatchData::setError(const QString &msg)
|
||||
valueEditable = false;
|
||||
}
|
||||
|
||||
void WatchData::setValue(const QString &value0)
|
||||
void WatchItem::setValue(const QString &value0)
|
||||
{
|
||||
value = value0;
|
||||
if (value == QLatin1String("{...}")) {
|
||||
@@ -216,7 +202,7 @@ static GuessChildrenResult guessChildren(const QByteArray &type)
|
||||
return HasPossiblyChildren;
|
||||
}
|
||||
|
||||
void WatchData::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
void WatchItem::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
{
|
||||
type = str.trimmed();
|
||||
bool changed = true;
|
||||
@@ -253,7 +239,7 @@ void WatchData::setType(const QByteArray &str, bool guessChildrenFromType)
|
||||
}
|
||||
}
|
||||
|
||||
QString WatchData::toString() const
|
||||
QString WatchItem::toString() const
|
||||
{
|
||||
const char *doubleQuoteComma = "\",";
|
||||
QString res;
|
||||
@@ -299,6 +285,254 @@ QString WatchData::toString() const
|
||||
return res + QLatin1Char('}');
|
||||
}
|
||||
|
||||
QString WatchItem::msgNotInScope()
|
||||
{
|
||||
//: Value of variable in Debugger Locals display for variables out
|
||||
//: of scope (stopped above initialization).
|
||||
static const QString rc =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchItem", "<not in scope>");
|
||||
return rc;
|
||||
}
|
||||
|
||||
const QString &WatchItem::shadowedNameFormat()
|
||||
{
|
||||
//: Display of variables shadowed by variables of the same name
|
||||
//: in nested scopes: Variable %1 is the variable name, %2 is a
|
||||
//: simple count.
|
||||
static const QString format =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchItem", "%1 <shadowed %2>");
|
||||
return format;
|
||||
}
|
||||
|
||||
QString WatchItem::shadowedName(const QString &name, int seen)
|
||||
{
|
||||
if (seen <= 0)
|
||||
return name;
|
||||
return shadowedNameFormat().arg(name).arg(seen);
|
||||
}
|
||||
|
||||
QByteArray WatchItem::hexAddress() const
|
||||
{
|
||||
if (address)
|
||||
return QByteArray("0x") + QByteArray::number(address, 16);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QString decodeItemHelper(const T &t)
|
||||
{
|
||||
return QString::number(t);
|
||||
}
|
||||
|
||||
QString decodeItemHelper(const double &t)
|
||||
{
|
||||
return QString::number(t, 'g', 16);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void decodeArrayHelper(WatchItem *item, const QByteArray &rawData, int size, const QByteArray &childType)
|
||||
{
|
||||
const QByteArray ba = QByteArray::fromHex(rawData);
|
||||
const T *p = (const T *) ba.data();
|
||||
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
||||
WatchItem *child = new WatchItem;
|
||||
child->arrayIndex = i;
|
||||
child->value = decodeItemHelper(p[i]);
|
||||
child->size = size;
|
||||
child->type = childType;
|
||||
child->setAllUnneeded();
|
||||
item->appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeArrayData(WatchItem *item, const QByteArray &rawData,
|
||||
const DebuggerEncoding &encoding, const QByteArray &childType)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<signed char>(item, rawData, encoding.size, childType);
|
||||
case 2:
|
||||
return decodeArrayHelper<short>(item, rawData, encoding.size, childType);
|
||||
case 4:
|
||||
return decodeArrayHelper<int>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<qint64>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<uchar>(item, rawData, encoding.size, childType);
|
||||
case 2:
|
||||
return decodeArrayHelper<ushort>(item, rawData, encoding.size, childType);
|
||||
case 4:
|
||||
return decodeArrayHelper<uint>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<quint64>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
return decodeArrayHelper<float>(item, rawData, encoding.size, childType);
|
||||
case 8:
|
||||
return decodeArrayHelper<double>(item, rawData, encoding.size, childType);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
}
|
||||
|
||||
void WatchItem::parseHelper(const GdbMi &input)
|
||||
{
|
||||
setChildrenUnneeded();
|
||||
|
||||
GdbMi mi = input["type"];
|
||||
if (mi.isValid())
|
||||
setType(mi.data());
|
||||
|
||||
editvalue = input["editvalue"].data();
|
||||
editformat = DebuggerDisplay(input["editformat"].toInt());
|
||||
editencoding = DebuggerEncoding(input["editencoding"].data());
|
||||
|
||||
mi = input["valueelided"];
|
||||
if (mi.isValid())
|
||||
elided = mi.toInt();
|
||||
|
||||
mi = input["bitpos"];
|
||||
if (mi.isValid())
|
||||
bitpos = mi.toInt();
|
||||
|
||||
mi = input["bitsize"];
|
||||
if (mi.isValid())
|
||||
bitsize = mi.toInt();
|
||||
|
||||
mi = input["origaddr"];
|
||||
if (mi.isValid())
|
||||
origaddr = mi.toAddress();
|
||||
|
||||
mi = input["address"];
|
||||
if (mi.isValid()) {
|
||||
address = mi.toAddress();
|
||||
if (exp.isEmpty()) {
|
||||
if (iname.startsWith("local.") && iname.count('.') == 1)
|
||||
// Solve one common case of adding 'class' in
|
||||
// *(class X*)0xdeadbeef for gdb.
|
||||
exp = name.toLatin1();
|
||||
else
|
||||
exp = "*(" + gdbQuoteTypes(type) + "*)" + hexAddress();
|
||||
}
|
||||
}
|
||||
|
||||
mi = input["value"];
|
||||
QByteArray enc = input["valueencoded"].data();
|
||||
if (mi.isValid() || !enc.isEmpty()) {
|
||||
setValue(decodeData(mi.data(), enc));
|
||||
} else {
|
||||
setValueNeeded();
|
||||
}
|
||||
|
||||
mi = input["size"];
|
||||
if (mi.isValid())
|
||||
size = mi.toInt();
|
||||
|
||||
mi = input["exp"];
|
||||
if (mi.isValid())
|
||||
exp = mi.data();
|
||||
|
||||
mi = input["valueenabled"];
|
||||
if (mi.data() == "true")
|
||||
valueEnabled = true;
|
||||
else if (mi.data() == "false")
|
||||
valueEnabled = false;
|
||||
|
||||
mi = input["valueeditable"];
|
||||
if (mi.data() == "true")
|
||||
valueEditable = true;
|
||||
else if (mi.data() == "false")
|
||||
valueEditable = false;
|
||||
|
||||
mi = input["numchild"]; // GDB/MI
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
mi = input["haschild"]; // native-mixed
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
|
||||
mi = input["arraydata"];
|
||||
if (mi.isValid()) {
|
||||
DebuggerEncoding encoding(input["arrayencoding"].data());
|
||||
QByteArray childType = input["childtype"].data();
|
||||
decodeArrayData(this, mi.data(), encoding, childType);
|
||||
} else {
|
||||
const GdbMi children = input["children"];
|
||||
if (children.isValid()) {
|
||||
bool ok = false;
|
||||
// Try not to repeat data too often.
|
||||
const GdbMi childType = input["childtype"];
|
||||
const GdbMi childNumChild = input["childnumchild"];
|
||||
|
||||
qulonglong addressBase = input["addrbase"].data().toULongLong(&ok, 0);
|
||||
qulonglong addressStep = input["addrstep"].data().toULongLong(&ok, 0);
|
||||
|
||||
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
||||
const GdbMi &subinput = children.children().at(i);
|
||||
WatchItem *child = new WatchItem;
|
||||
if (childType.isValid())
|
||||
child->setType(childType.data());
|
||||
if (childNumChild.isValid())
|
||||
child->setHasChildren(childNumChild.toInt() > 0);
|
||||
GdbMi name = subinput["name"];
|
||||
QByteArray nn;
|
||||
if (name.isValid()) {
|
||||
nn = name.data();
|
||||
child->name = QString::fromLatin1(nn);
|
||||
} else {
|
||||
nn.setNum(i);
|
||||
child->name = QString::fromLatin1("[%1]").arg(i);
|
||||
}
|
||||
GdbMi iname = subinput["iname"];
|
||||
if (iname.isValid())
|
||||
child->iname = iname.data();
|
||||
else
|
||||
child->iname = this->iname + '.' + nn;
|
||||
if (addressStep) {
|
||||
child->address = addressBase + i * addressStep;
|
||||
child->exp = "*(" + gdbQuoteTypes(child->type) + "*)" + child->hexAddress();
|
||||
}
|
||||
QByteArray key = subinput["key"].data();
|
||||
if (!key.isEmpty())
|
||||
child->name = decodeData(key, subinput["keyencoded"].data());
|
||||
child->parseHelper(subinput);
|
||||
appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WatchItem::parse(const GdbMi &data)
|
||||
{
|
||||
iname = data["iname"].data();
|
||||
|
||||
GdbMi wname = data["wname"];
|
||||
if (wname.isValid()) // Happens (only) for watched expressions.
|
||||
name = QString::fromUtf8(QByteArray::fromHex(wname.data()));
|
||||
else
|
||||
name = QString::fromLatin1(data["name"].data());
|
||||
|
||||
parseHelper(data);
|
||||
|
||||
if (wname.isValid())
|
||||
exp = name.toUtf8();
|
||||
}
|
||||
|
||||
WatchItem *WatchItem::parentItem() const
|
||||
{
|
||||
return static_cast<WatchItem *>(parent());
|
||||
}
|
||||
|
||||
// Format a tooltip row with aligned colon.
|
||||
static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
|
||||
{
|
||||
@@ -310,13 +544,13 @@ static void formatToolTipRow(QTextStream &str, const QString &category, const QS
|
||||
str << "</td><td>" << val << "</td></tr>";
|
||||
}
|
||||
|
||||
QString WatchData::toToolTip() const
|
||||
QString WatchItem::toToolTip() const
|
||||
{
|
||||
QString res;
|
||||
QTextStream str(&res);
|
||||
str << "<html><body><table>";
|
||||
formatToolTipRow(str, tr("Name"), name);
|
||||
formatToolTipRow(str, tr("Expression"), QLatin1String(exp));
|
||||
formatToolTipRow(str, tr("Expression"), expression());
|
||||
formatToolTipRow(str, tr("Internal Type"), QLatin1String(type));
|
||||
bool ok;
|
||||
const quint64 intValue = value.toULongLong(&ok);
|
||||
@@ -334,367 +568,92 @@ QString WatchData::toToolTip() const
|
||||
}
|
||||
formatToolTipRow(str, tr("Value"), val);
|
||||
}
|
||||
if (address)
|
||||
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(address));
|
||||
if (realAddress())
|
||||
formatToolTipRow(str, tr("Object Address"), formatToolTipAddress(realAddress()));
|
||||
if (origaddr)
|
||||
formatToolTipRow(str, tr("Pointer Address"), formatToolTipAddress(origaddr));
|
||||
if (arrayIndex >= 0)
|
||||
formatToolTipRow(str, tr("Array Index"), QString::number(arrayIndex));
|
||||
if (size)
|
||||
formatToolTipRow(str, tr("Static Object Size"), tr("%n bytes", 0, size));
|
||||
formatToolTipRow(str, tr("Internal ID"), QLatin1String(iname));
|
||||
formatToolTipRow(str, tr("Internal ID"), QLatin1String(internalName()));
|
||||
str << "</table></body></html>";
|
||||
return res;
|
||||
}
|
||||
|
||||
QString WatchData::msgNotInScope()
|
||||
bool WatchItem::isLocal() const
|
||||
{
|
||||
//: Value of variable in Debugger Locals display for variables out
|
||||
//: of scope (stopped above initialization).
|
||||
static const QString rc =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
||||
return rc;
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isLocal();
|
||||
return iname.startsWith("local.");
|
||||
}
|
||||
|
||||
const QString &WatchData::shadowedNameFormat()
|
||||
bool WatchItem::isWatcher() const
|
||||
{
|
||||
//: Display of variables shadowed by variables of the same name
|
||||
//: in nested scopes: Variable %1 is the variable name, %2 is a
|
||||
//: simple count.
|
||||
static const QString format =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
|
||||
return format;
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isWatcher();
|
||||
return iname.startsWith("watch.");
|
||||
}
|
||||
|
||||
QString WatchData::shadowedName(const QString &name, int seen)
|
||||
bool WatchItem::isInspect() const
|
||||
{
|
||||
if (seen <= 0)
|
||||
return name;
|
||||
return shadowedNameFormat().arg(name).arg(seen);
|
||||
if (arrayIndex >= 0)
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->isInspect();
|
||||
return iname.startsWith("inspect.");
|
||||
}
|
||||
|
||||
QByteArray WatchData::hexAddress() const
|
||||
quint64 WatchItem::realAddress() const
|
||||
{
|
||||
if (address)
|
||||
return QByteArray("0x") + QByteArray::number(address, 16);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
//
|
||||
// Protocol convienience
|
||||
//
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
void WatchData::updateValue(const GdbMi &item)
|
||||
{
|
||||
GdbMi value = item["value"];
|
||||
GdbMi encoding = item["valueencoded"];
|
||||
if (value.isValid() || encoding.isValid()) {
|
||||
setValue(decodeData(value.data(), encoding.data()));
|
||||
} else {
|
||||
setValueNeeded();
|
||||
if (arrayIndex >= 0) {
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->address + arrayIndex * size;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
void WatchData::updateChildCount(const GdbMi &mi)
|
||||
QByteArray WatchItem::internalName() const
|
||||
{
|
||||
if (mi.isValid())
|
||||
setHasChildren(mi.toInt() > 0);
|
||||
}
|
||||
|
||||
static void setWatchDataValueEnabled(WatchData &data, const GdbMi &mi)
|
||||
{
|
||||
if (mi.data() == "true")
|
||||
data.valueEnabled = true;
|
||||
else if (mi.data() == "false")
|
||||
data.valueEnabled = false;
|
||||
}
|
||||
|
||||
static void setWatchDataValueEditable(WatchData &data, const GdbMi &mi)
|
||||
{
|
||||
if (mi.data() == "true")
|
||||
data.valueEditable = true;
|
||||
else if (mi.data() == "false")
|
||||
data.valueEditable = false;
|
||||
}
|
||||
|
||||
static void setWatchDataAddress(WatchData &data, quint64 address)
|
||||
{
|
||||
data.address = address;
|
||||
|
||||
if (data.exp.isEmpty()) {
|
||||
if (data.iname.startsWith("local.") && data.iname.count('.') == 1)
|
||||
// Solve one common case of adding 'class' in
|
||||
// *(class X*)0xdeadbeef for gdb.
|
||||
data.exp = data.name.toLatin1();
|
||||
else
|
||||
data.exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress();
|
||||
if (arrayIndex >= 0) {
|
||||
if (const WatchItem *p = parentItem())
|
||||
return p->iname + '.' + QByteArray::number(arrayIndex);
|
||||
}
|
||||
return iname;
|
||||
}
|
||||
|
||||
static void setWatchDataSize(WatchData &data, const GdbMi &mi)
|
||||
QString WatchItem::realName() const
|
||||
{
|
||||
if (mi.isValid()) {
|
||||
bool ok = false;
|
||||
const unsigned size = mi.data().toUInt(&ok);
|
||||
if (ok)
|
||||
data.size = size;
|
||||
if (arrayIndex >= 0)
|
||||
return QString::fromLatin1("[%1]").arg(arrayIndex);
|
||||
return name;
|
||||
}
|
||||
|
||||
QString WatchItem::expression() const
|
||||
{
|
||||
if (!exp.isEmpty())
|
||||
return QString::fromLatin1(exp);
|
||||
if (quint64 addr = realAddress()) {
|
||||
if (!type.isEmpty())
|
||||
return QString::fromLatin1("*(%1*)0x%2").arg(QLatin1String(type)).arg(addr, 0, 16);
|
||||
}
|
||||
const WatchItem *p = parentItem();
|
||||
if (p && !p->exp.isEmpty())
|
||||
return QString::fromLatin1("(%1).%2").arg(QString::fromLatin1(p->exp), name);
|
||||
return name;
|
||||
}
|
||||
|
||||
void WatchData::updateType(const GdbMi &item)
|
||||
WatchItem *WatchItem::findItem(const QByteArray &iname)
|
||||
{
|
||||
if (item.isValid())
|
||||
setType(item.data());
|
||||
}
|
||||
|
||||
// Utilities to decode string data returned by the dumper helpers.
|
||||
|
||||
template <class T>
|
||||
QString decodeItemHelper(const T &t)
|
||||
{
|
||||
return QString::number(t);
|
||||
}
|
||||
|
||||
QString decodeItemHelper(const double &t)
|
||||
{
|
||||
return QString::number(t, 'g', 16);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void decodeArrayHelper(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
||||
const QByteArray &rawData)
|
||||
{
|
||||
const QByteArray ba = QByteArray::fromHex(rawData);
|
||||
const T *p = (const T *) ba.data();
|
||||
WatchData data;
|
||||
const QByteArray exp = "*(" + gdbQuoteTypes(tmplate.type) + "*)0x";
|
||||
for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) {
|
||||
data = tmplate;
|
||||
data.iname += QByteArray::number(i);
|
||||
data.name = QString::fromLatin1("[%1]").arg(i);
|
||||
data.value = decodeItemHelper(p[i]);
|
||||
data.address += i * sizeof(T);
|
||||
data.exp = exp + QByteArray::number(data.address, 16);
|
||||
data.setAllUnneeded();
|
||||
itemHandler(data);
|
||||
if (internalName() == iname)
|
||||
return this;
|
||||
foreach (TreeItem *child, children()) {
|
||||
auto witem = static_cast<WatchItem *>(child);
|
||||
if (WatchItem *result = witem->findItem(iname))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeArrayData(std::function<void(const WatchData &)> itemHandler, const WatchData &tmplate,
|
||||
const QByteArray &rawData, const DebuggerEncoding &encoding)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<signed char>(itemHandler, tmplate, rawData);
|
||||
case 2:
|
||||
return decodeArrayHelper<short>(itemHandler, tmplate, rawData);
|
||||
case 4:
|
||||
return decodeArrayHelper<int>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<qint64>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
return decodeArrayHelper<uchar>(itemHandler, tmplate, rawData);
|
||||
case 2:
|
||||
return decodeArrayHelper<ushort>(itemHandler, tmplate, rawData);
|
||||
case 4:
|
||||
return decodeArrayHelper<uint>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<quint64>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
break;
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
return decodeArrayHelper<float>(itemHandler, tmplate, rawData);
|
||||
case 8:
|
||||
return decodeArrayHelper<double>(itemHandler, tmplate, rawData);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
}
|
||||
|
||||
void parseChildrenData(const WatchData &data0, const GdbMi &item,
|
||||
std::function<void(const WatchData &)> itemHandler,
|
||||
std::function<void(const WatchData &, const GdbMi &)> childHandler,
|
||||
std::function<void(const WatchData &childTemplate, const QByteArray &encodedData,
|
||||
const DebuggerEncoding &encoding)> arrayDecoder)
|
||||
{
|
||||
WatchData data = data0;
|
||||
data.setChildrenUnneeded();
|
||||
|
||||
GdbMi children = item["children"];
|
||||
|
||||
data.updateType(item["type"]);
|
||||
|
||||
data.editvalue = item["editvalue"].data();
|
||||
data.editformat = DebuggerDisplay(item["editformat"].toInt());
|
||||
data.editencoding = DebuggerEncoding(item["editencoding"].data());
|
||||
|
||||
GdbMi mi = item["valueelided"];
|
||||
if (mi.isValid())
|
||||
data.elided = mi.toInt();
|
||||
|
||||
mi = item["bitpos"];
|
||||
if (mi.isValid())
|
||||
data.bitpos = mi.toInt();
|
||||
|
||||
mi = item["bitsize"];
|
||||
if (mi.isValid())
|
||||
data.bitsize = mi.toInt();
|
||||
|
||||
mi = item["origaddr"];
|
||||
if (mi.isValid())
|
||||
data.origaddr = mi.toAddress();
|
||||
|
||||
mi = item["address"];
|
||||
if (mi.isValid())
|
||||
setWatchDataAddress(data, mi.toAddress());
|
||||
|
||||
data.updateValue(item);
|
||||
|
||||
setWatchDataSize(data, item["size"]);
|
||||
|
||||
mi = item["exp"];
|
||||
if (mi.isValid())
|
||||
data.exp = mi.data();
|
||||
|
||||
setWatchDataValueEnabled(data, item["valueenabled"]);
|
||||
setWatchDataValueEditable(data, item["valueeditable"]);
|
||||
data.updateChildCount(item["numchild"]); // GDB/MI
|
||||
data.updateChildCount(item["haschild"]); // native-mixed
|
||||
itemHandler(data);
|
||||
|
||||
bool ok = false;
|
||||
qulonglong addressBase = item["addrbase"].data().toULongLong(&ok, 0);
|
||||
qulonglong addressStep = item["addrstep"].data().toULongLong(&ok, 0);
|
||||
|
||||
// Try not to repeat data too often.
|
||||
WatchData childtemplate;
|
||||
childtemplate.updateType(item["childtype"]);
|
||||
childtemplate.updateChildCount(item["childnumchild"]);
|
||||
|
||||
mi = item["arraydata"];
|
||||
if (mi.isValid()) {
|
||||
DebuggerEncoding encoding(item["arrayencoding"].data());
|
||||
childtemplate.iname = data.iname + '.';
|
||||
childtemplate.address = addressBase;
|
||||
arrayDecoder(childtemplate, mi.data(), encoding);
|
||||
} else {
|
||||
for (int i = 0, n = int(children.children().size()); i != n; ++i) {
|
||||
const GdbMi &child = children.children().at(i);
|
||||
WatchData data1 = childtemplate;
|
||||
GdbMi name = child["name"];
|
||||
if (name.isValid())
|
||||
data1.name = QString::fromLatin1(name.data());
|
||||
else
|
||||
data1.name = QString::number(i);
|
||||
GdbMi iname = child["iname"];
|
||||
if (iname.isValid()) {
|
||||
data1.iname = iname.data();
|
||||
} else {
|
||||
data1.iname = data.iname;
|
||||
data1.iname += '.';
|
||||
data1.iname += data1.name.toLatin1();
|
||||
}
|
||||
if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
|
||||
data1.name = QLatin1Char('[') + data1.name + QLatin1Char(']');
|
||||
if (addressStep) {
|
||||
setWatchDataAddress(data1, addressBase);
|
||||
addressBase += addressStep;
|
||||
}
|
||||
QByteArray key = child["key"].data();
|
||||
if (!key.isEmpty()) {
|
||||
data1.name = decodeData(key, child["keyencoded"].data());
|
||||
}
|
||||
childHandler(data1, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseWatchData(const WatchData &data0, const GdbMi &input,
|
||||
QList<WatchData> *list)
|
||||
{
|
||||
auto itemHandler = [list](const WatchData &data) {
|
||||
list->append(data);
|
||||
};
|
||||
auto childHandler = [list](const WatchData &innerData, const GdbMi &innerInput) {
|
||||
parseWatchData(innerData, innerInput, list);
|
||||
};
|
||||
auto arrayDecoder = [itemHandler](const WatchData &childTemplate,
|
||||
const QByteArray &encodedData, const DebuggerEncoding &encoding) {
|
||||
decodeArrayData(itemHandler, childTemplate, encodedData, encoding);
|
||||
};
|
||||
|
||||
parseChildrenData(data0, input, itemHandler, childHandler, arrayDecoder);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void readNumericVectorHelper(std::vector<double> *v, const QByteArray &ba)
|
||||
{
|
||||
const T *p = (const T *) ba.data();
|
||||
const int n = ba.size() / sizeof(T);
|
||||
v->resize(n);
|
||||
// Losing precision in case of 64 bit ints is ok here, as the result
|
||||
// is only used to plot data.
|
||||
for (int i = 0; i != n; ++i)
|
||||
(*v)[i] = static_cast<double>(p[i]);
|
||||
}
|
||||
|
||||
void readNumericVector(std::vector<double> *v, const QByteArray &rawData, const DebuggerEncoding &encoding)
|
||||
{
|
||||
switch (encoding.type) {
|
||||
case DebuggerEncoding::HexEncodedSignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<signed char>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<short>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<int>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<qint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedUnsignedInteger:
|
||||
switch (encoding.size) {
|
||||
case 1:
|
||||
readNumericVectorHelper<uchar>(v, rawData);
|
||||
return;
|
||||
case 2:
|
||||
readNumericVectorHelper<ushort>(v, rawData);
|
||||
return;
|
||||
case 4:
|
||||
readNumericVectorHelper<uint>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<quint64>(v, rawData);
|
||||
return;
|
||||
}
|
||||
case DebuggerEncoding::HexEncodedFloat:
|
||||
switch (encoding.size) {
|
||||
case 4:
|
||||
readNumericVectorHelper<float>(v, rawData);
|
||||
return;
|
||||
case 8:
|
||||
readNumericVectorHelper<double>(v, rawData);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "ENCODING ERROR: " << encoding.toString();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
Reference in New Issue
Block a user