From e00b90ccd5696baef7a5c240ad1b2da120b6e6ad Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Tue, 24 Feb 2009 20:34:30 +0100 Subject: rewrite gdb output receiver fixes chopping up of very long responses which quickly follow other responses. the line segmentation is done in readGdbStandardOutput() now. handleResponse() is not called through a queued slot any more, as the output receiver is already synchronized to the event loop. --- src/plugins/debugger/gdbengine.cpp | 365 ++++++++++++++++--------------------- src/plugins/debugger/gdbengine.h | 4 +- 2 files changed, 161 insertions(+), 208 deletions(-) diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp index 788e57a63e..0989993f7a 100644 --- a/src/plugins/debugger/gdbengine.cpp +++ b/src/plugins/debugger/gdbengine.cpp @@ -341,12 +341,6 @@ void GdbEngine::gdbProcError(QProcess::ProcessError error) q->exitDebugger(); } -static void skipSpaces(const char *&from, const char *to) -{ - while (from != to && QChar(*from).isSpace()) - ++from; -} - static inline bool isNameChar(char c) { // could be 'stopped' or 'shlibs-added' @@ -369,15 +363,6 @@ static void dump(const char *first, const char *middle, const QString & to) } #endif -static void skipTerminator(const char *&from, const char *to) -{ - skipSpaces(from, to); - // skip '(gdb)' - if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')') - from += 5; - skipSpaces(from, to); -} - void GdbEngine::readDebugeeOutput(const QByteArray &data) { emit applicationOutputAvailable(m_outputCodec->toUnicode( @@ -389,205 +374,179 @@ void GdbEngine::debugMessage(const QString &msg) emit gdbOutputAvailable("debug:", msg); } -// called asyncronously as response to Gdb stdout output in -// gdbResponseAvailable() -void GdbEngine::handleResponse() +void GdbEngine::handleResponse(const QByteArray &buff) { static QTime lastTime; emit gdbOutputAvailable(" ", currentTime()); - emit gdbOutputAvailable("stdout:", m_inbuffer); + emit gdbOutputAvailable("stdout:", buff); #if 0 qDebug() // << "#### start response handling #### " << currentTime() << lastTime.msecsTo(QTime::currentTime()) << "ms," - << "buf: " << m_inbuffer.left(1500) << "..." - //<< "buf: " << m_inbuffer - << "size:" << m_inbuffer.size(); + << "buf: " << buff.left(1500) << "..." + //<< "buf: " << buff + << "size:" << buff.size(); #else - //qDebug() << "buf: " << m_inbuffer; + //qDebug() << "buf: " << buff; #endif lastTime = QTime::currentTime(); - while (1) { - if (m_inbuffer.isEmpty()) - break; + if (buff.isEmpty() || buff == "(gdb) ") + return; - const char *from = m_inbuffer.constData(); - // FIXME: check for line ending in '\n(gdb)\n' - const char *to = from + m_inbuffer.size(); - const char *inner; + const char *from = buff.constData(); + const char *to = from + buff.size(); + const char *inner; - //const char *oldfrom = from; + int token = -1; + // token is a sequence of numbers + for (inner = from; inner != to; ++inner) + if (*inner < '0' || *inner > '9') + break; + if (from != inner) { + token = QByteArray(from, inner - from).toInt(); + from = inner; + //qDebug() << "found token " << token; + } + + // next char decides kind of record + const char c = *from++; + //qDebug() << "CODE:" << c; + switch (c) { + case '*': + case '+': + case '=': { + QByteArray asyncClass; + for (; from != to; ++from) { + const char c = *from; + if (!isNameChar(c)) + break; + asyncClass += *from; + } + //qDebug() << "ASYNCCLASS" << asyncClass; - //skipSpaces(from, to); - skipTerminator(from, to); - int token = -1; + GdbMi record; + while (from != to) { + if (*from != ',') { + qDebug() << "MALFORMED ASYNC OUTPUT" << from; + return; + } + ++from; // skip ',' + GdbMi data; + data.parseResultOrValue(from, to); + if (data.isValid()) { + //qDebug() << "parsed response: " << data.toString(); + record.m_children += data; + record.m_type = GdbMi::Tuple; + } + } + if (asyncClass == "stopped") { + handleAsyncOutput(record); + } else if (asyncClass == "running") { + // Archer has 'thread-id="all"' here + #ifdef Q_OS_MAC + } else if (asyncClass == "shlibs-updated") { + // MAC announces updated libs + } else if (asyncClass == "shlibs-added") { + // MAC announces added libs + // {shlib-info={num="2", name="libmathCommon.A_debug.dylib", + // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y", + // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib", + // description="/usr/lib/system/libmathCommon.A_debug.dylib", + // loaded_addr="0x7f000", slide="0x7f000", prefix=""}} + #endif + } else { + qDebug() << "IGNORED ASYNC OUTPUT " + << asyncClass << record.toString(); + } + break; + } - // token is a sequence of numbers - for (inner = from; inner != to; ++inner) - if (*inner < '0' || *inner > '9') - break; - if (from != inner) { - token = QString(QByteArray(from, inner - from)).toInt(); - from = inner; - //qDebug() << "found token " << token; + case '~': { + m_pendingConsoleStreamOutput += GdbMi::parseCString(from, to); + break; } - if (from == to) { - //qDebug() << "Returning: " << toString(); + case '@': { + m_pendingTargetStreamOutput += GdbMi::parseCString(from, to); break; } - // next char decides kind of record - const char c = *from++; - //qDebug() << "CODE:" << c; - - switch (c) { - case '*': - case '+': - case '=': { - QByteArray asyncClass; - for (; from != to; ++from) { - const char c = *from; - if (!isNameChar(c)) - break; - asyncClass += *from; - } - //qDebug() << "ASYNCCLASS" << asyncClass; - - GdbMi record; - while (from != to && *from == ',') { - ++from; // skip ',' - GdbMi data; - data.parseResultOrValue(from, to); - if (data.isValid()) { - //qDebug() << "parsed response: " << data.toString(); - record.m_children += data; - record.m_type = GdbMi::Tuple; - } - } - //dump(oldfrom, from, record.toString()); - skipTerminator(from, to); - m_inbuffer = QByteArray(from, to - from); - if (asyncClass == "stopped") { - handleAsyncOutput(record); - } else if (asyncClass == "running") { - // Archer has 'thread-id="all"' here - #ifdef Q_OS_MAC - } else if (asyncClass == "shlibs-updated") { - // MAC announces updated libs - } else if (asyncClass == "shlibs-added") { - // MAC announces added libs - // {shlib-info={num="2", name="libmathCommon.A_debug.dylib", - // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y", - // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib", - // description="/usr/lib/system/libmathCommon.A_debug.dylib", - // loaded_addr="0x7f000", slide="0x7f000", prefix=""}} - #endif - } else { - qDebug() << "IGNORED ASYNC OUTPUT " - << asyncClass << record.toString(); - } - break; - } + case '&': { + QByteArray data = GdbMi::parseCString(from, to); + m_pendingLogStreamOutput += data; + // On Windows, the contents seem to depend on the debugger + // version and/or OS version used. + if (data.startsWith("warning:")) + qq->showApplicationOutput(data); + break; + } - case '~': { - QByteArray data = GdbMi::parseCString(from, to); - m_pendingConsoleStreamOutput += data; - m_inbuffer = QByteArray(from, to - from); - break; - } + case '^': { + GdbResultRecord record; - case '@': { - QByteArray data = GdbMi::parseCString(from, to); - m_pendingTargetStreamOutput += data; - m_inbuffer = QByteArray(from, to - from); - break; - } + record.token = token; - case '&': { - QByteArray data = GdbMi::parseCString(from, to); - m_pendingLogStreamOutput += data; - m_inbuffer = QByteArray(from, to - from); - // On Windows, the contents seem to depend on the debugger - // version and/or OS version used. - if (data.startsWith("warning:")) - qq->showApplicationOutput(data); - break; - } + for (inner = from; inner != to; ++inner) + if (*inner < 'a' || *inner > 'z') + break; + + QByteArray resultClass(from, inner - from); + + if (resultClass == "done") + record.resultClass = GdbResultDone; + else if (resultClass == "running") + record.resultClass = GdbResultRunning; + else if (resultClass == "connected") + record.resultClass = GdbResultConnected; + else if (resultClass == "error") + record.resultClass = GdbResultError; + else if (resultClass == "exit") + record.resultClass = GdbResultExit; + else + record.resultClass = GdbResultUnknown; - case '^': { - GdbResultRecord record; - - record.token = token; - - for (inner = from; inner != to; ++inner) - if (*inner < 'a' || *inner > 'z') - break; - - QByteArray resultClass(from, inner - from); - - if (resultClass == "done") - record.resultClass = GdbResultDone; - else if (resultClass == "running") - record.resultClass = GdbResultRunning; - else if (resultClass == "connected") - record.resultClass = GdbResultConnected; - else if (resultClass == "error") - record.resultClass = GdbResultError; - else if (resultClass == "exit") - record.resultClass = GdbResultExit; - else - record.resultClass = GdbResultUnknown; - - from = inner; - skipSpaces(from, to); - if (from != to && *from == ',') { - ++from; - record.data.parseTuple_helper(from, to); - record.data.m_type = GdbMi::Tuple; - record.data.m_name = "data"; + from = inner; + if (from != to) { + if (*from != ',') { + qDebug() << "MALFORMED RESULT OUTPUT" << from; + return; } - skipSpaces(from, to); - skipTerminator(from, to); - - //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput; - //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput; - //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput; - record.data.setStreamOutput("logstreamoutput", - m_pendingLogStreamOutput); - record.data.setStreamOutput("targetstreamoutput", - m_pendingTargetStreamOutput); - record.data.setStreamOutput("consolestreamoutput", - m_pendingConsoleStreamOutput); - QByteArray custom = m_customOutputForToken[token]; - if (!custom.isEmpty()) - record.data.setStreamOutput("customvaluecontents", - '{' + custom + '}'); - //m_customOutputForToken.remove(token); - m_pendingLogStreamOutput.clear(); - m_pendingTargetStreamOutput.clear(); - m_pendingConsoleStreamOutput.clear(); - - //dump(oldfrom, from, record.toString()); - m_inbuffer = QByteArray(from, to - from); - handleResultRecord(record); - break; - } - default: { - qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer; - m_inbuffer = QByteArray(from, to - from); - break; + ++from; + record.data.parseTuple_helper(from, to); + record.data.m_type = GdbMi::Tuple; + record.data.m_name = "data"; } + + //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput; + //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput; + //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput; + record.data.setStreamOutput("logstreamoutput", + m_pendingLogStreamOutput); + record.data.setStreamOutput("targetstreamoutput", + m_pendingTargetStreamOutput); + record.data.setStreamOutput("consolestreamoutput", + m_pendingConsoleStreamOutput); + QByteArray custom = m_customOutputForToken[token]; + if (!custom.isEmpty()) + record.data.setStreamOutput("customvaluecontents", + '{' + custom + '}'); + //m_customOutputForToken.remove(token); + m_pendingLogStreamOutput.clear(); + m_pendingTargetStreamOutput.clear(); + m_pendingConsoleStreamOutput.clear(); + + handleResultRecord(record); + break; + } + default: { + qDebug() << "UNKNOWN RESPONSE TYPE" << c; + break; } } - - //qDebug() << "##### end response handling ####\n\n\n" - // << currentTime() << lastTime.msecsTo(QTime::currentTime()); - lastTime = QTime::currentTime(); } void GdbEngine::readGdbStandardError() @@ -597,30 +556,26 @@ void GdbEngine::readGdbStandardError() void GdbEngine::readGdbStandardOutput() { - // This is the function called whenever the Gdb process created - // output. As a rule of thumb, stdout contains _real_ Gdb output - // as responses to our command - // and "spontaneous" events like messages on loaded shared libraries. - // OTOH, stderr contains application output produced by qDebug etc. - // There is no organized way to pass application stdout output. - - QByteArray out = m_gdbProc.readAllStandardOutput(); - - //qDebug() << "\n\n\nPLUGIN OUT: '" << out.data() << "'\n\n\n"; - - m_inbuffer.append(out); - //QTC_ASSERT(!m_inbuffer.isEmpty(), return); + m_inbuffer.append(m_gdbProc.readAllStandardOutput()); - char c = m_inbuffer[m_inbuffer.size() - 1]; - static const QByteArray termArray("(gdb) "); - if (out.indexOf(termArray) == -1 && c != 10 && c != 13) { - //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive"; - //qDebug() << m_inbuffer.data() << m_inbuffer.size(); - //qDebug() << "\n\n"; - return; + int newstart = 0; + while (newstart < m_inbuffer.size()) { + int start = newstart; + int end = m_inbuffer.indexOf('\n', start); + if (end < 0) { + m_inbuffer.remove(0, start); + return; + } + newstart = end + 1; + if (end == start) + continue; + if (m_inbuffer.at(end - 1) == '\r') { + --end; + if (end == start) + continue; + } + handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start)); } - - emit gdbResponseAvailable(); } void GdbEngine::interruptInferior() diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h index 9e6a0a9380..7d205f513d 100644 --- a/src/plugins/debugger/gdbengine.h +++ b/src/plugins/debugger/gdbengine.h @@ -92,7 +92,6 @@ public: ~GdbEngine(); signals: - void gdbResponseAvailable(); void gdbInputAvailable(const QString &prefix, const QString &msg); void gdbOutputAvailable(const QString &prefix, const QString &msg); void applicationOutputAvailable(const QString &output); @@ -173,8 +172,6 @@ private: void updateLocals(); private slots: - void handleResponse(); - void gdbProcError(QProcess::ProcessError error); void readGdbStandardOutput(); void readGdbStandardError(); @@ -182,6 +179,7 @@ private slots: private: int terminationIndex(const QByteArray &buffer, int &length); + void handleResponse(const QByteArray &buff); void handleStart(const GdbResultRecord &response); void handleAttach(); void handleAqcuiredInferior(); -- cgit v1.2.1