forked from qt-creator/qt-creator
Fixes: debugger: provide gdb macro based dumpers for QString and QStringList
Details: That works with a core file, too. The "real" dumpers require a live process as they use gdb's "call"
This commit is contained in:
@@ -148,6 +148,7 @@ enum GdbCommandType
|
||||
WatchDumpCustomSetup,
|
||||
WatchDumpCustomValue1, // waiting for gdb ack
|
||||
WatchDumpCustomValue2, // waiting for actual data
|
||||
WatchDumpCustomValue3, // macro based
|
||||
WatchDumpCustomEditValue,
|
||||
};
|
||||
|
||||
@@ -897,6 +898,11 @@ void GdbEngine::handleResult(const GdbResultRecord & record, int type,
|
||||
case WatchDumpCustomValue2:
|
||||
handleDumpCustomValue2(record, cookie.value<WatchData>());
|
||||
break;
|
||||
|
||||
case WatchDumpCustomValue3:
|
||||
handleDumpCustomValue3(record, cookie.value<WatchData>());
|
||||
break;
|
||||
|
||||
case WatchDumpCustomSetup:
|
||||
handleDumpCustomSetup(record);
|
||||
break;
|
||||
@@ -954,6 +960,30 @@ void GdbEngine::handleTargetCore(const GdbResultRecord &record)
|
||||
// Registers
|
||||
//
|
||||
qq->reloadRegisters();
|
||||
|
||||
// Gdb-Macro based Dumpers
|
||||
sendCommand(
|
||||
"define qdumpqstring\n"
|
||||
"set $i = 0\n"
|
||||
"set $l = $arg0->d->size\n"
|
||||
"set $p = $arg0->d->data\n"
|
||||
"while $i < $l\n"
|
||||
"printf \"%d \",$p[$i++]\n"
|
||||
"end\n"
|
||||
"printf \"\\n\"\n"
|
||||
"end\n"
|
||||
);
|
||||
|
||||
sendCommand(
|
||||
"define qdumpqstringlist\n"
|
||||
"set $i = $arg0->d->begin\n"
|
||||
"set $e = $arg0->d->end\n"
|
||||
"while $i < $e\n"
|
||||
"printf \"%d \",$arg0->d->array + $i++\n"
|
||||
"end\n"
|
||||
"printf \"\\n\"\n"
|
||||
"end\n"
|
||||
);
|
||||
}
|
||||
|
||||
void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
|
||||
@@ -1613,6 +1643,9 @@ bool GdbEngine::startDebugger()
|
||||
sendCommand("set print elements 10000");
|
||||
sendCommand("-data-list-register-names", RegisterListNames);
|
||||
|
||||
sendCommand("set substitute-path /var/tmp/qt-x11-src-4.5.0 "
|
||||
"/home/sandbox/qtsdk-2009.01/qt");
|
||||
|
||||
// one of the following is needed to prevent crashes in gdb on code like:
|
||||
// template <class T> T foo() { return T(0); }
|
||||
// int main() { return foo<int>(); }
|
||||
@@ -3041,6 +3074,13 @@ bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
|
||||
DebuggerSettings *s = q->settings();
|
||||
if (!s->m_useCustomDumpers)
|
||||
return false;
|
||||
|
||||
if (q->startMode() == AttachCore) {
|
||||
// "call" is not possible in gdb when looking at core files
|
||||
return type == "QString" || type.endsWith("::QString")
|
||||
|| type == "QStringList" || type.endsWith("::QStringList");
|
||||
}
|
||||
|
||||
if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
|
||||
return false;
|
||||
if (m_dataDumperState != DataDumperAvailable)
|
||||
@@ -3058,8 +3098,32 @@ bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
|
||||
return m_availableSimpleDumpers.contains(tmplate);
|
||||
}
|
||||
|
||||
void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
|
||||
void GdbEngine::runDirectDumper(const WatchData &data, bool dumpChildren)
|
||||
{
|
||||
Q_UNUSED(dumpChildren);
|
||||
QString type = data.type;
|
||||
QString cmd;
|
||||
|
||||
if (type == "QString" || type.endsWith("::QString"))
|
||||
cmd = "qdumpqstring (&" + data.exp + ")";
|
||||
else if (type == "QStringList" || type.endsWith("::QStringList"))
|
||||
cmd = "qdumpqstringlist (&" + data.exp + ")";
|
||||
|
||||
QVariant var;
|
||||
var.setValue(data);
|
||||
sendSynchronizedCommand(cmd, WatchDumpCustomValue3, var);
|
||||
|
||||
q->showStatusMessage(
|
||||
tr("Retrieving data for watch view (%1 requests pending)...")
|
||||
.arg(m_pendingRequests + 1), 10000);
|
||||
}
|
||||
|
||||
void GdbEngine::runCustomDumper(const WatchData &data0, bool dumpChildren)
|
||||
{
|
||||
if (q->startMode() == AttachCore) {
|
||||
runDirectDumper(data0, dumpChildren);
|
||||
return;
|
||||
}
|
||||
WatchData data = data0;
|
||||
QTC_ASSERT(!data.exp.isEmpty(), return);
|
||||
QString tmplate;
|
||||
@@ -3081,133 +3145,6 @@ void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
|
||||
extraArgs[1] = "0";
|
||||
extraArgs[2] = "0";
|
||||
extraArgs[3] = "0";
|
||||
int extraArgCount = 0;
|
||||
|
||||
// "generic" template dumpers: passing sizeof(argument)
|
||||
// gives already most information the dumpers need
|
||||
foreach (const QString &arg, inners)
|
||||
extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
|
||||
|
||||
// in rare cases we need more or less:
|
||||
if (outertype == m_namespace + "QObject") {
|
||||
extraArgs[0] = "(char*)&((('"
|
||||
+ m_namespace + "QObjectPrivate'*)&"
|
||||
+ data.exp + ")->children)-(char*)&" + data.exp;
|
||||
} else if (outertype == m_namespace + "QVector") {
|
||||
extraArgs[1] = "(char*)&(("
|
||||
+ data.exp + ").d->array)-(char*)" + data.exp + ".d";
|
||||
} else if (outertype == m_namespace + "QObjectSlot"
|
||||
|| outertype == m_namespace + "QObjectSignal") {
|
||||
// we need the number out of something like
|
||||
// iname="local.ob.slots.[2]deleteLater()"
|
||||
int lastOpened = data.iname.lastIndexOf('[');
|
||||
int lastClosed = data.iname.lastIndexOf(']');
|
||||
QString slotNumber = "-1";
|
||||
if (lastOpened != -1 && lastClosed != -1)
|
||||
slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
|
||||
extraArgs[0] = slotNumber;
|
||||
} else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
|
||||
QString nodetype;
|
||||
if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
|
||||
nodetype = m_namespace + "QMapNode";
|
||||
nodetype += data.type.mid(outertype.size());
|
||||
} else {
|
||||
// FIXME: doesn't work for QMultiMap
|
||||
nodetype = data.type + "::Node";
|
||||
}
|
||||
//qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
|
||||
// << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
|
||||
extraArgs[2] = sizeofTypeExpression(nodetype);
|
||||
extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
|
||||
} else if (outertype == m_namespace + "QMapNode") {
|
||||
extraArgs[2] = sizeofTypeExpression(data.type);
|
||||
extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
|
||||
} else if (outertype == "std::vector") {
|
||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
||||
if (inners.at(0) == "bool") {
|
||||
outertype = "std::vector::bool";
|
||||
} else {
|
||||
//extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
|
||||
//extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
|
||||
}
|
||||
} else if (outertype == "std::deque") {
|
||||
// remove 'std::allocator<...>':
|
||||
extraArgs[1] = "0";
|
||||
} else if (outertype == "std::stack") {
|
||||
// remove 'std::allocator<...>':
|
||||
extraArgs[1] = "0";
|
||||
} else if (outertype == "std::map") {
|
||||
// We don't want the comparator and the allocator confuse gdb.
|
||||
// But we need the offset of the second item in the value pair.
|
||||
// We read the type of the pair from the allocator argument because
|
||||
// that gets the constness "right" (in the sense that gdb can
|
||||
// read it back;
|
||||
QString pairType = inners.at(3);
|
||||
// remove 'std::allocator<...>':
|
||||
pairType = pairType.mid(15, pairType.size() - 15 - 2);
|
||||
extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
|
||||
extraArgs[3] = "0";
|
||||
} else if (outertype == "std::basic_string") {
|
||||
//qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
|
||||
if (inners.at(0) == "char") {
|
||||
outertype = "std::string";
|
||||
} else if (inners.at(0) == "wchar_t") {
|
||||
outertype = "std::wstring";
|
||||
}
|
||||
extraArgs[0] = "0";
|
||||
extraArgs[1] = "0";
|
||||
extraArgs[2] = "0";
|
||||
extraArgs[3] = "0";
|
||||
}
|
||||
|
||||
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
||||
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
||||
int protocol = 2;
|
||||
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
||||
|
||||
QString addr;
|
||||
if (data.addr.startsWith("0x"))
|
||||
addr = "(void*)" + data.addr;
|
||||
else
|
||||
addr = "&(" + data.exp + ")";
|
||||
|
||||
QByteArray params;
|
||||
params.append(outertype.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.iname.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.exp.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(inner.toUtf8());
|
||||
params.append('\0');
|
||||
params.append(data.iname.toUtf8());
|
||||
params.append('\0');
|
||||
|
||||
sendWatchParameters(params);
|
||||
|
||||
QString cmd ="call "
|
||||
+ QString("qDumpObjectData440(")
|
||||
+ QString::number(protocol)
|
||||
+ ',' + "%1+1" // placeholder for token
|
||||
+ ',' + addr
|
||||
+ ',' + (dumpChildren ? "1" : "0")
|
||||
+ ',' + extraArgs[0]
|
||||
+ ',' + extraArgs[1]
|
||||
+ ',' + extraArgs[2]
|
||||
+ ',' + extraArgs[3] + ')';
|
||||
|
||||
//qDebug() << "CMD: " << cmd;
|
||||
|
||||
QVariant var;
|
||||
var.setValue(data);
|
||||
sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
|
||||
|
||||
q->showStatusMessage(
|
||||
tr("Retrieving data for watch view (%1 requests pending)...")
|
||||
.arg(m_pendingRequests + 1), 10000);
|
||||
|
||||
// retrieve response
|
||||
sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
|
||||
}
|
||||
|
||||
void GdbEngine::createGdbVariable(const WatchData &data)
|
||||
@@ -3722,6 +3659,56 @@ void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::handleDumpCustomValue3(const GdbResultRecord &record,
|
||||
const WatchData &data0)
|
||||
{
|
||||
WatchData data = data0;
|
||||
QByteArray out = record.data.findChild("consolestreamoutput").data();
|
||||
while (out.endsWith(' ') || out.endsWith('\n'))
|
||||
out.chop(1);
|
||||
QList<QByteArray> list = out.split(' ');
|
||||
//qDebug() << "RECEIVED" << record.toString() << " FOR " << data0.toString()
|
||||
// << " STREAM: " << out;
|
||||
if (list.isEmpty()) {
|
||||
data.setValue("<unavailable>");
|
||||
data.setAllUnneeded();
|
||||
insertData(data);
|
||||
} else if (data.type == "QString" || data.type.endsWith("::QString")) {
|
||||
QList<QByteArray> list = out.split(' ');
|
||||
QString str;
|
||||
for (int i = 0; i < list.size(); ++i)
|
||||
str.append(list.at(i).toInt());
|
||||
data.setValue('"' + str.toUtf8() + '"');
|
||||
data.setChildCount(0);
|
||||
data.setAllUnneeded();
|
||||
insertData(data);
|
||||
} else if (data.type == "QStringList" || data.type.endsWith("::QStringList")) {
|
||||
int l = list.size();
|
||||
data.setValue(QString("<%1 items>").arg(l).toLatin1());
|
||||
data.setChildCount(list.size());
|
||||
data.setAllUnneeded();
|
||||
insertData(data);
|
||||
for (int i = 0; i < l; ++i) {
|
||||
WatchData data1;
|
||||
data1.name = QString("[%1]").arg(i);
|
||||
data1.type = data.type.left(data.type.size() - 4);
|
||||
data1.iname = data.iname + QString(".%1").arg(i);
|
||||
data1.addr = list.at(i);
|
||||
data1.exp = "((" + gdbQuoteTypes(data1.type) + "*)" + data1.addr + ")";
|
||||
data1.setChildCount(0);
|
||||
data1.setValueNeeded();
|
||||
QString cmd = "qdumpqstring (" + data1.exp + ")";
|
||||
QVariant var;
|
||||
var.setValue(data1);
|
||||
sendSynchronizedCommand(cmd, WatchDumpCustomValue3, var);
|
||||
}
|
||||
} else {
|
||||
data.setValue("<unavailable>");
|
||||
data.setAllUnneeded();
|
||||
insertData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::updateLocals()
|
||||
{
|
||||
m_pendingRequests = 0;
|
||||
|
||||
@@ -305,6 +305,7 @@ private:
|
||||
|
||||
void tryLoadCustomDumpers();
|
||||
void runCustomDumper(const WatchData &data, bool dumpChildren);
|
||||
void runDirectDumper(const WatchData &data, bool dumpChildren);
|
||||
bool isCustomValueDumperAvailable(const QString &type) const;
|
||||
|
||||
void handleVarListChildren(const GdbResultRecord &record,
|
||||
@@ -316,12 +317,14 @@ private:
|
||||
const WatchData &cookie);
|
||||
void handleToolTip(const GdbResultRecord &record,
|
||||
const QString &cookie);
|
||||
void handleDumpCustomValue1(const GdbResultRecord &record,
|
||||
const WatchData &cookie);
|
||||
void handleQueryDataDumper1(const GdbResultRecord &record);
|
||||
void handleQueryDataDumper2(const GdbResultRecord &record);
|
||||
void handleDumpCustomValue1(const GdbResultRecord &record,
|
||||
const WatchData &cookie);
|
||||
void handleDumpCustomValue2(const GdbResultRecord &record,
|
||||
const WatchData &cookie);
|
||||
void handleDumpCustomValue3(const GdbResultRecord &record,
|
||||
const WatchData &cookie);
|
||||
void handleDumpCustomEditValue(const GdbResultRecord &record);
|
||||
void handleDumpCustomSetup(const GdbResultRecord &record);
|
||||
void handleStackListLocals(const GdbResultRecord &record);
|
||||
|
||||
@@ -960,6 +960,9 @@ void testHidden()
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QString hallo = "hallo";
|
||||
QStringList list;
|
||||
list << "aaa" << "bbb" << "cc";
|
||||
testIO();
|
||||
testHidden();
|
||||
testArray();
|
||||
|
||||
Reference in New Issue
Block a user