Commit bd96ebf7 authored by hjk's avatar hjk
Browse files

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"
parent 4e1e21da
......@@ -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();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment