diff --git a/bin/gdbmacros/gdbmacros.cpp b/bin/gdbmacros/gdbmacros.cpp index 7a304607b6ccbeb506ed3d9096733bf5cf8f8b4e..0a94e527b816012409cc692209235855534b5f3c 100644 --- a/bin/gdbmacros/gdbmacros.cpp +++ b/bin/gdbmacros/gdbmacros.cpp @@ -216,7 +216,8 @@ QT_END_NAMESPACE // this can be mangled typenames of nested templates, each char-by-char // comma-separated integer list static char qDumpInBuffer[10000]; -static char qDumpBuffer[1000]; +static char qDumpOutBuffer[100000]; +static char qDumpSize[20]; namespace { @@ -443,27 +444,28 @@ QDumper::~QDumper() { flush(); - char buf[30]; - int len = qsnprintf(buf, sizeof(buf) - 1, "%d^done\n", token); - write(buf, len); + //char buf[30]; + //int len = qsnprintf(buf, sizeof(buf) - 1, "%d^done\n", token); + //write(buf, len); } void QDumper::write(const void *buf, int len) { - ::fwrite(buf, len, 1, stdout); - ::fflush(stdout); + //::fwrite(buf, len, 1, stdout); + //::fflush(stdout); } void QDumper::flush() { - if (pos != 0) { - char buf[30]; - int len = qsnprintf(buf, sizeof(buf) - 1, "%d#%d,", token, pos); - write(buf, len); - write(qDumpBuffer, pos); - write("\n", 1); - pos = 0; - } + put(0); + //if (pos != 0) { + // char buf[30]; + // int len = qsnprintf(buf, sizeof(buf) - 1, "%d#%d,", token, pos); + // write(buf, len); + // write(qDumpOutBuffer, pos); + // write("\n", 1); + // pos = 0; + // } } void QDumper::setupTemplateParameters() @@ -489,49 +491,49 @@ void QDumper::setupTemplateParameters() QDumper &QDumper::operator<<(unsigned long long c) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%llu", c); + pos += sprintf(qDumpOutBuffer + pos, "%llu", c); return *this; } QDumper &QDumper::operator<<(unsigned long c) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%lu", c); + pos += sprintf(qDumpOutBuffer + pos, "%lu", c); return *this; } QDumper &QDumper::operator<<(float d) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%f", d); + pos += sprintf(qDumpOutBuffer + pos, "%f", d); return *this; } QDumper &QDumper::operator<<(double d) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%f", d); + pos += sprintf(qDumpOutBuffer + pos, "%f", d); return *this; } QDumper &QDumper::operator<<(unsigned int i) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%u", i); + pos += sprintf(qDumpOutBuffer + pos, "%u", i); return *this; } QDumper &QDumper::operator<<(long c) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%ld", c); + pos += sprintf(qDumpOutBuffer + pos, "%ld", c); return *this; } QDumper &QDumper::operator<<(int i) { checkFill(); - pos += sprintf(qDumpBuffer + pos, "%d", i); + pos += sprintf(qDumpOutBuffer + pos, "%d", i); return *this; } @@ -555,21 +557,21 @@ QDumper &QDumper::operator<<(const void *p) void QDumper::checkFill() { - if (pos >= int(sizeof(qDumpBuffer)) - 100) + if (pos >= int(sizeof(qDumpOutBuffer)) - 100) flush(); } void QDumper::put(char c) { checkFill(); - qDumpBuffer[pos++] = c; + qDumpOutBuffer[pos++] = c; } void QDumper::addCommaIfNeeded() { if (pos == 0) return; - char c = qDumpBuffer[pos - 1]; + char c = qDumpOutBuffer[pos - 1]; if (c == '}' || c == '"' || c == ']') put(','); } diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index f62140129eca168e8b22dfd936fdabcbfb1be54a..a42c10382048fcd677b1fbe86dc8e0a521d882f9 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -928,7 +928,7 @@ void DebuggerPlugin::readSettings() m->m_skipKnownFrames = s->value("SkipKnownFrames", false).toBool(); m->m_debugDumpers = s->value("DebugDumpers", false).toBool(); - m->m_useCustomDumpers = s->value("UseCustomDupers", false).toBool(); + m->m_useCustomDumpers = s->value("UseCustomDumpers", true).toBool(); m->m_useFastStart = s->value("UseFastStart", false).toBool(); m->m_useToolTips = s->value("UseToolTips", false).toBool(); m->m_useTerminal = s->value("UseTerminal", false).toBool(); diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index a6d426d21da96c0d0e29d0ed8fff85eeea6d6a6b..ba1042aff8aaaf4f677dd25ca976ea82848ac085 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -113,6 +113,7 @@ enum GdbCommandType GdbInfoProc, GdbQueryDataDumper1, GdbQueryDataDumper2, + GdbQueryDataDumper3, BreakCondition = 200, BreakEnablePending, @@ -144,7 +145,8 @@ enum GdbCommandType WatchToolTip, WatchDumpCustomSetup, WatchDumpCustomValue1, // waiting for gdb ack - WatchDumpCustomValue2, // waiting for actual data + WatchDumpCustomValue2, // waiting for actual data (fd version) + WatchDumpCustomValue3, // waiting for actual data (buffer version) WatchDumpCustomEditValue, }; @@ -807,6 +809,9 @@ void GdbEngine::handleResult(const GdbResultRecord & record, int type, case GdbQueryDataDumper2: handleQueryDataDumper2(record); break; + case GdbQueryDataDumper3: + handleQueryDataDumper3(record); + break; case BreakList: handleBreakList(record); @@ -883,6 +888,9 @@ 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; @@ -1562,6 +1570,7 @@ bool GdbEngine::startDebugger() //sendCommand("set confirm off"); //sendCommand("set pagination off"); sendCommand("set breakpoint pending on", BreakEnablePending); + sendCommand("set print elements 10000"); // one of the following is needed to prevent crashes in gdb on code like: // template <class T> T foo() { return T(0); } @@ -3081,11 +3090,12 @@ void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren) // create response slot for socket data QVariant var; var.setValue(data); - sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var); + //sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var); // this increases the probability that gdb spits out output it // has collected so far //sendCommand("p qDumpInBuffer"); + sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue3, var); } void GdbEngine::createGdbVariable(const WatchData &data) @@ -3360,6 +3370,73 @@ void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record) //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers; } +void GdbEngine::handleQueryDataDumper3(const GdbResultRecord &record) +{ +#if 1 + // is this the official gdb response. However, it won't contain + // interesting data other than the information that 'real' data + // either already arrived or is still in the pipe. So we do + // _not_ register this result for counting purposes, this will + // be done by the 'real' result (with resultClass == GdbResultCustomDone) + //qDebug() << "DATA DUMPER TRIAL:" << record.toString(); + //GdbMi output = record.data.findChild("customvaluecontents"); + GdbMi output = record.data.findChild("consolestreamoutput"); + QByteArray out = output.data(); + out = out.mid(out.indexOf('"') + 1); + out = out.replace('\\', ""); + out = out.left(out.lastIndexOf('"')); + out = "result={" + out + "}"; + qDebug() << "OUTPUT: " << out; + + GdbMi contents; + contents.fromString(out); + GdbMi simple = contents.findChild("dumpers"); + m_namespace = contents.findChild("namespace").data(); + GdbMi qtversion = contents.findChild("qtversion"); + if (qtversion.children().size() == 3) { + m_qtVersion = (qtversion.childAt(0).data().toInt() << 16) + + (qtversion.childAt(1).data().toInt() << 8) + + qtversion.childAt(2).data().toInt(); + //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion; + } else { + m_qtVersion = 0; + } + + qDebug() << "OUTPUT: " << out; + qDebug() << "CONTENTS: " << contents.toString(); + qDebug() << "SIMPLE DUMPERS: " << simple.toString(); + m_availableSimpleDumpers.clear(); + foreach (const GdbMi &item, simple.children()) + m_availableSimpleDumpers.append(item.data()); + if (m_availableSimpleDumpers.isEmpty()) { + m_dataDumperState = DataDumperUnavailable; + QMessageBox::warning(q->mainWindow(), + tr("Cannot find special data dumpers"), + tr("The debugged binary does not contain information needed for " + "nice display of Qt data types.\n\n" + "Try might want to try include the file\n\n" + ".../ide/main/bin/gdbmacros/gdbmacros.cpp'\n\n" + "into your project directly.") + ); + } else { + m_dataDumperState = DataDumperAvailable; + } + qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers; +#else + // divert this to the fd version + GdbMi output = record.data.findChild("consolestreamoutput"); + QByteArray out = output.data(); + out = out.mid(out.indexOf('=') + 3); + out = out.replace("\\\\", "\\"); + out = out.left(out.lastIndexOf('"')); + out = "dummy={customvaluecontents=\"{" + out + "}\"}"; + GdbResultRecord record1 = record; + record1.data = GdbMi(); + record1.data.fromString(out); + handleQueryDataDumper2(record1); +#endif +} + void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) { QByteArray params = params0; @@ -3596,6 +3673,115 @@ void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record, } } +void GdbEngine::handleDumpCustomValue3(const GdbResultRecord &record, + const WatchData &data0) +{ + WatchData data = data0; + QTC_ASSERT(data.isValid(), return); + qDebug() << "CUSTOM VALUE RESULT: " << record.toString(); + qDebug() << "FOR DATA: " << data.toString() << record.resultClass; + if (record.resultClass == GdbResultDone) { + //GdbMi output = record.data.findChild("customvaluecontents"); + + GdbMi output = record.data.findChild("consolestreamoutput"); + QByteArray out = output.data(); + out = out.mid(out.indexOf('"') + 1); + out = out.replace('\\', ""); + out = out.left(out.lastIndexOf('"')); + out = "result={" + out + "}"; + qDebug() << "OUTPUT: " << out; + + GdbMi contents; + contents.fromString(out); + + //qDebug() << "HANDLE VALUE CONTENTS: " << output.toString(true); + if (!contents.isValid()) { + qDebug() << "INVALID"; + // custom dumper produced no output + if (data.isValueNeeded()) + data.setValue("<unknown>"); + if (data.isTypeNeeded()) + data.setType("<unknown>"); + if (data.isChildrenNeeded()) + data.setChildCount(0); + if (data.isChildCountNeeded()) + data.setChildCount(0); + data.setValueToolTip("<custom dumper produced no output>"); + insertData(data); + } else { + //GdbMi contents; + //qDebug() << "OUTPUT" << output.toString(true); + //contents.fromString(output.data()); + qDebug() << "CONTENTS" << contents.toString(true); + setWatchDataType(data, contents.findChild("type")); + setWatchDataValue(data, contents.findChild("value"), + contents.findChild("valueencoded").data().toInt()); + setWatchDataAddress(data, contents.findChild("addr")); + setWatchDataChildCount(data, contents.findChild("numchild")); + setWatchDataValueToolTip(data, contents.findChild("valuetooltip")); + setWatchDataValueDisabled(data, contents.findChild("valuedisabled")); + setWatchDataEditValue(data, contents.findChild("editvalue")); + if (qq->watchHandler()->isDisplayedIName(data.iname)) { + GdbMi editvalue = contents.findChild("editvalue"); + if (editvalue.isValid()) { + setWatchDataEditValue(data, editvalue); + qq->watchHandler()->showEditValue(data); + } + } + if (!qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + GdbMi children = contents.findChild("children"); + if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + data.setValueUnneeded(); + + // try not to repeat data too often + WatchData childtemplate; + setWatchDataType(childtemplate, contents.findChild("childtype")); + setWatchDataChildCount(childtemplate, contents.findChild("childnumchild")); + //qDebug() << "DATA: " << data.toString(); + insertData(data); + foreach (GdbMi item, children.children()) { + WatchData data1 = childtemplate; + data1.name = item.findChild("name").data(); + data1.iname = data.iname + "." + data1.name; + //qDebug() << "NAMEENCODED: " << item.findChild("nameencoded").data() + // << item.findChild("nameencoded").data()[1]; + if (item.findChild("nameencoded").data()[0] == '1') + data1.name = QByteArray::fromBase64(data1.name.toUtf8()); + QString key = item.findChild("key").data(); + if (!key.isEmpty()) + data1.name += " (" + key + ")"; + setWatchDataType(data1, item.findChild("type")); + setWatchDataExpression(data1, item.findChild("exp")); + setWatchDataChildCount(data1, item.findChild("numchild")); + setWatchDataValue(data1, item.findChild("value"), + item.findChild("valueencoded").data().toInt()); + setWatchDataAddress(data1, item.findChild("addr")); + setWatchDataValueToolTip(data1, item.findChild("valuetooltip")); + setWatchDataValueDisabled(data1, item.findChild("valuedisabled")); + if (!qq->watchHandler()->isExpandedIName(data1.iname)) + data1.setChildrenUnneeded(); + //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString(); + insertData(data1); + } + } + //qDebug() << "HANDLE CUSTOM VALUE CONTENTS: " << data.toString(); + } else if (record.resultClass == GdbResultError) { + // FIXME: Should not happen here, i.e. could be removed + QString msg = record.data.findChild("msg").data(); + //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg; + if (msg.startsWith("The program being debugged was sig")) + msg = strNotInScope; + if (msg.startsWith("The program being debugged stopped while")) + msg = strNotInScope; + data.setError(msg); + insertData(data); + } else { + qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString(); + } +} + void GdbEngine::updateLocals() { setTokenBarrier(); @@ -4021,7 +4207,8 @@ void GdbEngine::tryLoadCustomDumpers() sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)", GdbQueryDataDumper1); // create response slot for socket data - sendCommand(QString(), GdbQueryDataDumper2); + //sendCommand(QString(), GdbQueryDataDumper2); + sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper3); } diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h index 60493a53e582233c19efbd7cd42e3b95f82cf17b..1113955ab86695a8c8afb46c8f28d97fa82e7891 100644 --- a/src/plugins/debugger/gdbengine.h +++ b/src/plugins/debugger/gdbengine.h @@ -307,8 +307,11 @@ private: const WatchData &cookie); void handleQueryDataDumper1(const GdbResultRecord &record); void handleQueryDataDumper2(const GdbResultRecord &record); + void handleQueryDataDumper3(const GdbResultRecord &record); 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/src/plugins/debugger/gdbmi.cpp b/src/plugins/debugger/gdbmi.cpp index 88060d7b350fe4cea36e805ff69faf5a8ce45dca..d3fbeb4428123dd52aaea65a12e3080c98588b66 100644 --- a/src/plugins/debugger/gdbmi.cpp +++ b/src/plugins/debugger/gdbmi.cpp @@ -46,20 +46,20 @@ QTextStream & operator<<(QTextStream & os, const GdbMi & mi) return os << mi.toString(); } -//static void skipSpaces(const GdbMi::Char *&from, const GdbMi::Char *to) +//static void skipSpaces(const char *&from, const char *to) //{ // while (from != to && QChar(*from).isSpace()) // ++from; //} -void GdbMi::parseResultOrValue(const Char *&from, const Char *to) +void GdbMi::parseResultOrValue(const char *&from, const char *to) { //skipSpaces(from, to); while (from != to && QChar(*from).isSpace()) ++from; - //qDebug() << "parseResultOrValue: " << QByteArray::fromLatin1(from, to - from); + //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from); parseValue(from, to); if (isValid()) { //qDebug() << "no valid result in " << QByteArray::fromLatin1(from, to - from); @@ -67,7 +67,7 @@ void GdbMi::parseResultOrValue(const Char *&from, const Char *to) } if (from == to || *from == '(') return; - const Char *ptr = from; + const char *ptr = from; while (ptr < to && *ptr != '=') { //qDebug() << "adding" << QChar(*ptr) << "to name"; ++ptr; @@ -80,7 +80,7 @@ void GdbMi::parseResultOrValue(const Char *&from, const Char *to) } } -QByteArray GdbMi::parseCString(const Char *&from, const Char *to) +QByteArray GdbMi::parseCString(const char *&from, const char *to) { QByteArray result; //qDebug() << "parseCString: " << QByteArray::fromUtf16(from, to - from); @@ -88,7 +88,7 @@ QByteArray GdbMi::parseCString(const Char *&from, const Char *to) qDebug() << "MI Parse Error, double quote expected"; return QByteArray(); } - const Char *ptr = from; + const char *ptr = from; ++ptr; while (ptr < to) { if (*ptr == '"') { @@ -115,7 +115,7 @@ QByteArray GdbMi::parseCString(const Char *&from, const Char *to) return result; } -void GdbMi::parseValue(const Char *&from, const Char *to) +void GdbMi::parseValue(const char *&from, const char *to) { //qDebug() << "parseValue: " << QByteArray::fromUtf16(from, to - from); switch (*from) { @@ -135,7 +135,7 @@ void GdbMi::parseValue(const Char *&from, const Char *to) } -void GdbMi::parseTuple(const Char *&from, const Char *to) +void GdbMi::parseTuple(const char *&from, const char *to) { //qDebug() << "parseTuple: " << QByteArray::fromUtf16(from, to - from); QTC_ASSERT(*from == '{', /**/); @@ -143,7 +143,7 @@ void GdbMi::parseTuple(const Char *&from, const Char *to) parseTuple_helper(from, to); } -void GdbMi::parseTuple_helper(const Char *&from, const Char *to) +void GdbMi::parseTuple_helper(const char *&from, const char *to) { //qDebug() << "parseTuple_helper: " << QByteArray::fromUtf16(from, to - from); m_type = Tuple; @@ -163,7 +163,7 @@ void GdbMi::parseTuple_helper(const Char *&from, const Char *to) } } -void GdbMi::parseList(const Char *&from, const Char *to) +void GdbMi::parseList(const char *&from, const char *to) { //qDebug() << "parseList: " << QByteArray::fromUtf16(from, to - from); QTC_ASSERT(*from == '[', /**/); @@ -267,8 +267,8 @@ QByteArray GdbMi::toString(bool multiline, int indent) const void GdbMi::fromString(const QByteArray &ba) { - const Char *from = ba.constBegin(); - const Char *to = ba.constEnd(); + const char *from = ba.constBegin(); + const char *to = ba.constEnd(); parseResultOrValue(from, to); } @@ -449,16 +449,16 @@ static struct Tester { } for (int i = from; i < to; ++i) { if (str[i] == '{') - result += "{\n" + QByteArray(2*++indent + 1, QChar(' ')); + result += "{\n" + QByteArray(2*++indent + 1, ' '); else if (str[i] == '}') { if (!result.isEmpty() && result[result.size() - 1] != '\n') result += "\n"; - result += QByteArray(2*--indent + 1, QChar(' ')) + "}\n"; + result += QByteArray(2*--indent + 1, ' ') + "}\n"; } else if (str[i] == ',') { if (true || !result.isEmpty() && result[result.size() - 1] != '\n') result += "\n"; - result += QByteArray(2*indent, QChar(' ')); + result += QByteArray(2*indent, ' '); } else result += str[i]; diff --git a/src/plugins/debugger/gdbmi.h b/src/plugins/debugger/gdbmi.h index 0e6c36e9756af2ee04dcf00a41234f5529130e15..21810eed40ea026e5d6e3ae9e0ce36e35a99e07b 100644 --- a/src/plugins/debugger/gdbmi.h +++ b/src/plugins/debugger/gdbmi.h @@ -34,8 +34,6 @@ #ifndef DEBUGGER_GDBMI_H #define DEBUGGER_GDBMI_H -#include <qglobal.h> - #include <QtCore/QByteArray> #include <QtCore/QList> @@ -125,8 +123,8 @@ public: inline const QList<GdbMi> &children() const { return m_children; } inline int childCount() const { return m_children.size(); } - const GdbMi & childAt(int index) const { return m_children[index]; } - GdbMi & childAt(int index) { return m_children[index]; } + const GdbMi &childAt(int index) const { return m_children[index]; } + GdbMi &childAt(int index) { return m_children[index]; } GdbMi findChild(const QByteArray &name) const; GdbMi findChild(const QByteArray &name, const QByteArray &defaultString) const; @@ -138,14 +136,12 @@ private: friend class GdbResultRecord; friend class GdbEngine; - //typedef ushort Char; - typedef char Char; - static QByteArray parseCString(const Char *&from, const Char *to); - void parseResultOrValue(const Char *&from, const Char *to); - void parseValue(const Char *&from, const Char *to); - void parseTuple(const Char *&from, const Char *to); - void parseTuple_helper(const Char *&from, const Char *to); - void parseList(const Char *&from, const Char *to); + static QByteArray parseCString(const char *&from, const char *to); + void parseResultOrValue(const char *&from, const char *to); + void parseValue(const char *&from, const char *to); + void parseTuple(const char *&from, const char *to); + void parseTuple_helper(const char *&from, const char *to); + void parseList(const char *&from, const char *to); void dumpChildren(QByteArray *str, bool multiline, int indent) const; }; @@ -171,8 +167,6 @@ public: int token; GdbResultClass resultClass; GdbMi data; -private: - friend class GdbMi; }; } // namespace Internal