summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-02-24 20:34:30 +0100
committerOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-02-24 20:41:04 +0100
commite00b90ccd5696baef7a5c240ad1b2da120b6e6ad (patch)
tree5c9581af8755db1adbb01d5ae42d6e5a88a15fb9
parenta340a9937d60b3df789315eaf3e5cf3c72981740 (diff)
downloadqt-creator-e00b90ccd5696baef7a5c240ad1b2da120b6e6ad.tar.gz
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.
-rw-r--r--src/plugins/debugger/gdbengine.cpp365
-rw-r--r--src/plugins/debugger/gdbengine.h4
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();