diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index 16404d4276c057440b85384b9f40323edb24d9d5..5f524fc4f1192c6434221b8b13e80b7f055a3e47 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -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; diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h index 661b8198accbd81a6ca3e61ff4ad95fb746e91c8..0205b1a862ddb61b12b030388fcd81c65cb4bcd8 100644 --- a/src/plugins/debugger/gdbengine.h +++ b/src/plugins/debugger/gdbengine.h @@ -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); diff --git a/tests/manual/gdbdebugger/simple/app.cpp b/tests/manual/gdbdebugger/simple/app.cpp index 99f6ece6c52609ef49cf20b4f015dc44f6c8d8af..f4d92fc8233307f4a10db24c852196f21937e3f1 100644 --- a/tests/manual/gdbdebugger/simple/app.cpp +++ b/tests/manual/gdbdebugger/simple/app.cpp @@ -960,6 +960,9 @@ void testHidden() int main(int argc, char *argv[]) { + QString hallo = "hallo"; + QStringList list; + list << "aaa" << "bbb" << "cc"; testIO(); testHidden(); testArray();