Commit e00b90cc authored by Oswald Buddenhagen's avatar Oswald Buddenhagen
Browse files

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.
parent a340a993
......@@ -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()
......
......@@ -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();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment