diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp index 6277c5477cda6ddea509e1bd34dfdd7c981f382f..fdcbfad0f83370de09f8e06b95410c8aaf47af70 100644 --- a/share/qtcreator/gdbmacros/gdbmacros.cpp +++ b/share/qtcreator/gdbmacros/gdbmacros.cpp @@ -94,10 +94,10 @@ int qtGhVersion = QT_VERSION; \c{qDumpObjectData440()}. In any case, dumper processesing should end up in - \c{handleProtocolVersion2and3()} and needs an entry in the bis switch there. + \c{handleProtocolVersion2and3()} and needs an entry in the big switch there. Next step is to create a suitable \c{static void qDumpFoo(QDumper &d)} - function. At the bare minimum it should contain something like: + function. At the bare minimum it should contain something like this: \c{ @@ -127,7 +127,7 @@ int qtGhVersion = QT_VERSION; \endlist If the current item has children, it might be queried to produce information - about thes children. In this case the dumper should use something like + about these children. In this case the dumper should use something like this: \c{ if (d.dumpChildren) { @@ -221,16 +221,19 @@ Q_DECL_EXPORT char qDumpOutBuffer[100000]; namespace { +static QByteArray strPtrConst = "* const"; + static bool isPointerType(const QByteArray &type) { - return type.endsWith("*") || type.endsWith("* const"); + return type.endsWith('*') || type.endsWith(strPtrConst); } -static QByteArray stripPointerType(QByteArray type) +static QByteArray stripPointerType(const QByteArray &_type) { - if (type.endsWith("*")) + QByteArray type = _type; + if (type.endsWith('*')) type.chop(1); - if (type.endsWith("* const")) + if (type.endsWith(strPtrConst)) type.chop(7); if (type.endsWith(' ')) type.chop(1); @@ -279,7 +282,10 @@ static bool isEqual(const char *s, const char *t) static bool startsWith(const char *s, const char *t) { - return qstrncmp(s, t, qstrlen(t)) == 0; + while (char c = *t++) + if (c != *s++) + return false; + return true; } // Check memory for read access and provoke segfault if nothing else helps. @@ -293,11 +299,18 @@ static bool startsWith(const char *s, const char *t) # define qCheckPointer(d) do { if (d) qProvokeSegFaultHelper = *(char*)d; } while (0) #endif +#ifdef QT_NAMESPACE const char *stripNamespace(const char *type) { - static const size_t nslen = qstrlen(NS); + static const size_t nslen = strlen(NS); return startsWith(type, NS) ? type + nslen : type; } +#else +inline const char *stripNamespace(const char *type) +{ + return type; +} +#endif static bool isSimpleType(const char *type) { @@ -1168,7 +1181,7 @@ static void qDumpQHashNode(QDumper &d) P(d, "numchild", 2); if (d.dumpChildren) { - // there is a hash specialization in cast the key are integers or shorts + // there is a hash specialization in case the keys are integers or shorts d << ",children=["; d.beginHash(); P(d, "name", "key"); @@ -2679,7 +2692,7 @@ void *qDumpObjectData440( // This is a list of all available dumpers. Note that some templates // currently require special hardcoded handling in the debugger plugin. - // They are mentioned here nevertheless. For types that not listed + // They are mentioned here nevertheless. For types that are not listed // here, dumpers won't be used. d << "dumpers=[" "\""NS"QByteArray\"," diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 541aa7ea1d0615303b4e67a295364b60ac74a89c..b76398254d0bb1d6c270da66a589416dae79fabf 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -571,7 +571,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool if (der == DumpExecuteSizeFailed) m_failedTypes.push_back(wd.type); // log error - *errorMessage = *errorMessage = msgDumpFailed(wd, errorMessage); + *errorMessage = msgDumpFailed(wd, errorMessage); m_access->showDebuggerOutput(m_messagePrefix, *errorMessage); return DumpError; } diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index 653a6817b62afeb6e8c7f00cdbfd0aaff141efdc..12e1aa1623be3c9755b23ad3df52e763914e5d20 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -1824,9 +1824,9 @@ void GdbEngine::sendInsertBreakpoint(int index) //if (where.isEmpty()) // where = data->fileName; #endif - // we need something like "\"file name.cpp\":100" to - // survive the gdb command line parser with file names intact - where = _("\"\\\"") + where + _("\\\":") + data->lineNumber + _c('"'); + // The argument is simply a C-quoted version of the argument to the + // non-MI "break" command, including the "original" quoting it wants. + where = _("\"\\\"") + GdbMi::escapeCString(where) + _("\\\":") + data->lineNumber + _c('"'); } else { where = data->funcName; } @@ -1987,6 +1987,8 @@ void GdbEngine::handleBreakInsert(const GdbResultRecord &record, const QVariant handler->updateMarkers(); } else if (record.resultClass == GdbResultError) { const BreakpointData *data = handler->at(index); + // Note that it is perfectly correct that the file name is put + // in quotes but not escaped. GDB simply is like that. #ifdef Q_OS_LINUX //QString where = "\"\\\"" + data->fileName + "\\\":" // + data->lineNumber + "\""; @@ -3098,8 +3100,7 @@ void GdbEngine::handleQueryDebuggingHelper(const GdbResultRecord &record, const QByteArray out = output.data(); out = out.mid(out.indexOf('"') + 2); // + 1 is success marker out = out.left(out.lastIndexOf('"')); - //out.replace('\'', '"'); - out.replace("\\", ""); + out.replace('\\', ""); // optimization: dumper output never needs real C unquoting out = "dummy={" + out + "}"; //qDebug() << "OUTPUT: " << out; @@ -3302,7 +3303,7 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResultRecord &record, QByteArray out = output.data(); int markerPos = out.indexOf('"') + 1; // position of 'success marker' - if (markerPos == -1 || out.at(markerPos) == 'f') { // 't' or 'f' + if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f' // custom dumper produced no output data.setError(strNotInScope); insertData(data); @@ -3311,7 +3312,7 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResultRecord &record, out = out.mid(markerPos + 1); out = out.left(out.lastIndexOf('"')); - out.replace("\\", ""); + out.replace('\\', ""); // optimization: dumper output never needs real C unquoting out = "dummy={" + out + "}"; GdbMi contents; @@ -3848,13 +3849,13 @@ void GdbEngine::tryLoadDebuggingHelpers() execCommand(_("sharedlibrary .*")); // for LoadLibraryA //execCommand(_("handle SIGSEGV pass stop print")); //execCommand(_("set unwindonsignal off")); - execCommand(_("call LoadLibraryA(\"") + lib + _("\")"), + execCommand(_("call LoadLibraryA(\"") + GdbMi::escapeCString(lib) + _("\")"), CB(handleDebuggingHelperSetup)); execCommand(_("sharedlibrary ") + dotEscape(lib)); #elif defined(Q_OS_MAC) //execCommand(_("sharedlibrary libc")); // for malloc //execCommand(_("sharedlibrary libdl")); // for dlopen - execCommand(_("call (void)dlopen(\"") + lib + _("\", " STRINGIFY(RTLD_NOW) ")"), + execCommand(_("call (void)dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"), CB(handleDebuggingHelperSetup)); //execCommand(_("sharedlibrary ") + dotEscape(lib)); m_debuggingHelperState = DebuggingHelperLoadTried; @@ -3863,10 +3864,10 @@ void GdbEngine::tryLoadDebuggingHelpers() QString flag = QString::number(RTLD_NOW); execCommand(_("sharedlibrary libc")); // for malloc execCommand(_("sharedlibrary libdl")); // for dlopen - execCommand(_("call (void*)dlopen(\"") + lib + _("\", " STRINGIFY(RTLD_NOW) ")"), + execCommand(_("call (void*)dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"), CB(handleDebuggingHelperSetup)); // some older systems like CentOS 4.6 prefer this: - execCommand(_("call (void*)__dlopen(\"") + lib + _("\", " STRINGIFY(RTLD_NOW) ")"), + execCommand(_("call (void*)__dlopen(\"") + GdbMi::escapeCString(lib) + _("\", " STRINGIFY(RTLD_NOW) ")"), CB(handleDebuggingHelperSetup)); execCommand(_("sharedlibrary ") + dotEscape(lib)); #endif diff --git a/src/plugins/debugger/gdbmi.cpp b/src/plugins/debugger/gdbmi.cpp index 4cbd53ab1fed4bd2e6476d5bba5151314ad8af8a..e2fd7e7a25a67ec2e7ca9a0873daff134bd7363b 100644 --- a/src/plugins/debugger/gdbmi.cpp +++ b/src/plugins/debugger/gdbmi.cpp @@ -34,6 +34,8 @@ #include <QtCore/QByteArray> #include <QtCore/QTextStream> +#include <ctype.h> + namespace Debugger { namespace Internal { @@ -44,7 +46,7 @@ QTextStream &operator<<(QTextStream &os, const GdbMi &mi) void GdbMi::parseResultOrValue(const char *&from, const char *to) { - while (from != to && QChar(*from).isSpace()) + while (from != to && isspace(*from)) ++from; //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from); @@ -74,6 +76,7 @@ QByteArray GdbMi::parseCString(const char *&from, const char *to) //qDebug() << "parseCString: " << QByteArray::fromUtf16(from, to - from); if (*from != '"') { qDebug() << "MI Parse Error, double quote expected"; + ++from; // So we don't hang return QByteArray(); } const char *ptr = from; @@ -84,22 +87,66 @@ QByteArray GdbMi::parseCString(const char *&from, const char *to) result = QByteArray(from + 1, ptr - from - 2); break; } - if (*ptr == '\\' && ptr < to - 1) + if (*ptr == '\\') { ++ptr; + if (ptr == to) { + qDebug() << "MI Parse Error, unterminated backslash escape"; + from = ptr; // So we don't hang + return QByteArray(); + } + } ++ptr; } + from = ptr; - if (result.contains('\\')) { - if (result.contains("\\032\\032")) - result.clear(); - else { - result = result.replace("\\n", "\n"); - result = result.replace("\\t", "\t"); - result = result.replace("\\\"", "\""); - } + int idx = result.indexOf('\\'); + if (idx >= 0) { + char *dst = result.data() + idx; + const char *src = dst + 1, *end = result.data() + result.length(); + do { + char c = *src++; + switch (c) { + case 'a': *dst++ = '\a'; break; + case 'b': *dst++ = '\b'; break; + case 'f': *dst++ = '\f'; break; + case 'n': *dst++ = '\n'; break; + case 'r': *dst++ = '\r'; break; + case 't': *dst++ = '\t'; break; + case 'v': *dst++ = '\v'; break; + case '"': *dst++ = '"'; break; + case '\\': *dst++ = '\\'; break; + default: + { + int chars = 0; + uchar prod = 0; + forever { + if (c < '0' || c > '7') { + --src; + break; + } + prod = prod * 8 + c - '0'; + if (++chars == 3 || src == end) + break; + c = *src++; + } + if (!chars) { + qDebug() << "MI Parse Error, unrecognized backslash escape"; + return QByteArray(); + } + *dst++ = prod; + } + } + while (src != end) { + char c = *src++; + if (c == '\\') + break; + *dst++ = c; + } + } while (src != end); + *dst = 0; + result.truncate(dst - result.data()); } - from = ptr; return result; } @@ -203,10 +250,50 @@ void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const } } -static QByteArray escaped(QByteArray ba) +class MyString : public QString { +public: + ushort at(int i) const { return constData()[i].unicode(); } +}; + +template<class ST, typename CT> +inline ST escapeCStringTpl(const ST &ba) +{ + ST ret; + ret.reserve(ba.length() * 2); + for (int i = 0; i < ba.length(); ++i) { + CT c = ba.at(i); + switch (c) { + case '\\': ret += "\\\\"; break; + case '\a': ret += "\\a"; break; + case '\b': ret += "\\b"; break; + case '\f': ret += "\\f"; break; + case '\n': ret += "\\n"; break; + case '\r': ret += "\\r"; break; + case '\t': ret += "\\t"; break; + case '\v': ret += "\\v"; break; + case '"': ret += "\\\""; break; + default: + if (c < 32 || c == 127) { + ret += '\\'; + ret += '0' + (c >> 6); + ret += '0' + ((c >> 3) & 7); + ret += '0' + (c & 7); + } else { + ret += c; + } + } + } + return ret; +} + +QString GdbMi::escapeCString(const QString &ba) +{ + return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba)); +} + +QByteArray GdbMi::escapeCString(const QByteArray &ba) { - ba.replace("\"", "\\\""); - return ba; + return escapeCStringTpl<QByteArray, uchar>(ba); } QByteArray GdbMi::toString(bool multiline, int indent) const @@ -222,7 +309,7 @@ QByteArray GdbMi::toString(bool multiline, int indent) const case Const: if (!m_name.isEmpty()) result += m_name + "="; - result += "\"" + escaped(m_data) + "\""; + result += "\"" + escapeCString(m_data) + "\""; break; case Tuple: if (!m_name.isEmpty()) diff --git a/src/plugins/debugger/gdbmi.h b/src/plugins/debugger/gdbmi.h index 311bef2a7ce3559dd24511341c0287da2d07d0c5..1e9731bb30362f2f46a786539a16c292922be16a 100644 --- a/src/plugins/debugger/gdbmi.h +++ b/src/plugins/debugger/gdbmi.h @@ -132,6 +132,8 @@ private: friend class GdbEngine; static QByteArray parseCString(const char *&from, const char *to); + static QByteArray escapeCString(const QByteArray &ba); + static QString escapeCString(const QString &ba); void parseResultOrValue(const char *&from, const char *to); void parseValue(const char *&from, const char *to); void parseTuple(const char *&from, const char *to); diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 5693c2c2159adfa034117f184ca3df28b498b724..407ada9cadfbb74c7421917257817a11f5fa1086 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -136,6 +136,7 @@ bool hasSideEffects(const QString &exp) return exp.contains(QLatin1String("-=")) || exp.contains(QLatin1String("+=")) || exp.contains(QLatin1String("/=")) + || exp.contains(QLatin1String("%=")) || exp.contains(QLatin1String("*=")) || exp.contains(QLatin1String("&=")) || exp.contains(QLatin1String("|=")) diff --git a/tests/auto/debugger/main.cpp b/tests/auto/debugger/main.cpp index 4e7eeeeb61fa34ae6daa528a1f968890c98a6869..afb51a38c0118d821ac1f58099995172bd48a021 100644 --- a/tests/auto/debugger/main.cpp +++ b/tests/auto/debugger/main.cpp @@ -52,7 +52,7 @@ static const char test11[] = "{name=\"0\",value=\"one\",type=\"QByteArray\"}]"; static const char test12[] = - "[{iname=\"local.hallo\",value=\"\\\"\\\"\",type=\"QByteArray\"," + "[{iname=\"local.hallo\",value=\"\\\"\\\\\\00382\\t\\377\",type=\"QByteArray\"," "numchild=\"0\"}]"; class tst_Debugger : public QObject