diff --git a/src/plugins/debugger/gdb/remotegdbprocess.cpp b/src/plugins/debugger/gdb/remotegdbprocess.cpp index 6dbc6b207303506254b844895a002ffaabc95136..a8f8cefcc5c3009b9283a8399bf7a577e0e233da 100644 --- a/src/plugins/debugger/gdb/remotegdbprocess.cpp +++ b/src/plugins/debugger/gdb/remotegdbprocess.cpp @@ -57,40 +57,49 @@ QByteArray RemoteGdbProcess::readAllStandardError() void RemoteGdbProcess::start(const QString &cmd, const QStringList &args) { - m_gdbState = CmdNotYetSent; m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo); - if (m_gdbConn->hasError()) - return; - m_appOutputReaderState = CmdNotYetSent; m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo); - if (m_appOutputConn->hasError()) - return; - m_errOutputReaderState = CmdNotYetSent; m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo); - if (m_errOutputConn->hasError()) - return; m_command = cmd; m_cmdArgs = args; - connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()), - this, SLOT(handleGdbOutput())); - connect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()), - this, SLOT(handleAppOutput())); - connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()), - this, SLOT(handleErrOutput())); - m_gdbConn->start(); m_errOutputConn->start(); m_appOutputConn->start(); -} + m_gdbConn->start(); + } bool RemoteGdbProcess::waitForStarted() { + if (!waitForInputReady(m_appOutputConn)) + return false; + if (!sendAndWaitForEcho(m_appOutputConn, readerCmdLine(AppOutputFile))) + return false; + if (!waitForInputReady(m_errOutputConn)) + return false; + if (!sendAndWaitForEcho(m_errOutputConn, readerCmdLine(ErrOutputFile))) + return false; + if (!waitForInputReady(m_gdbConn)) + return false; + connect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()), + this, SLOT(handleAppOutput())); + connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()), + this, SLOT(handleErrOutput())); + connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()), + this, SLOT(handleGdbOutput())); + m_gdbStarted = false; + m_gdbCmdLine = "stty -echo && " + m_command.toUtf8() + ' ' + + m_cmdArgs.join(QLatin1String(" ")).toUtf8() + + " -tty=" + AppOutputFile + " 2>" + ErrOutputFile + '\n'; + if (!m_wd.isEmpty()) + m_gdbCmdLine.prepend("cd " + m_wd.toUtf8() + " && "); + if (sendInput(m_gdbCmdLine) != m_gdbCmdLine.count()) + return false; + return true; } qint64 RemoteGdbProcess::write(const QByteArray &data) { - if (m_gdbState != CmdReceived || !m_inputToSend.isEmpty() - || !m_lastSeqNr.isEmpty()) { + if (!m_gdbStarted || !m_inputToSend.isEmpty() || !m_lastSeqNr.isEmpty()) { m_inputToSend.enqueue(data); return data.size(); } else { @@ -114,15 +123,7 @@ void RemoteGdbProcess::kill() QProcess::ProcessState RemoteGdbProcess::state() const { - switch (m_gdbState) { - case CmdNotYetSent: - return QProcess::NotRunning; - case CmdSent: - return QProcess::Starting; - case CmdReceived: - default: - return QProcess::Running; - } + return m_gdbStarted ? QProcess::Running : QProcess::Starting; } QString RemoteGdbProcess::errorString() const @@ -132,30 +133,30 @@ QString RemoteGdbProcess::errorString() const void RemoteGdbProcess::handleGdbOutput() { + m_currentGdbOutput + += removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0)); #if 0 - qDebug("%s: output is '%s'", Q_FUNC_INFO, output.data()); + qDebug("%s: complete unread output is '%s'", Q_FUNC_INFO, m_currentGdbOutput.data()); #endif - - if (m_gdbState == CmdNotYetSent) + if (checkForGdbExit(m_currentGdbOutput)) { + m_currentGdbOutput.clear(); return; + } - m_currentGdbOutput - += removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0)); if (!m_currentGdbOutput.endsWith('\n')) return; - if (m_gdbState == CmdSent) { - const int index = m_currentGdbOutput.indexOf(m_startCmdLine); + if (!m_gdbStarted) { + const int index = m_currentGdbOutput.indexOf(m_gdbCmdLine); if (index != -1) - m_currentGdbOutput.remove(index, m_startCmdLine.size()); + m_currentGdbOutput.remove(index, m_gdbCmdLine.size()); // Note: We can't guarantee that we will match the command line, // because the remote terminal sometimes inserts control characters. - // Otherwise we could change the state to CmdReceived here. + // Otherwise we could set m_gdbStarted here. } - m_gdbState = CmdReceived; + m_gdbStarted = true; - checkForGdbExit(m_currentGdbOutput); if (m_currentGdbOutput.contains(m_lastSeqNr + '^')) m_lastSeqNr.clear(); @@ -220,61 +221,13 @@ qint64 RemoteGdbProcess::sendInput(const QByteArray &data) void RemoteGdbProcess::handleAppOutput() { - const QByteArray output = m_appOutputConn->waitForRemoteOutput(0); - if (!handleAppOrErrOutput(m_appOutputConn, m_appOutputReaderState, - m_initialAppOutput, AppOutputFile, output)) - m_adapter->handleApplicationOutput(output); + m_adapter->handleApplicationOutput(m_appOutputConn->waitForRemoteOutput(0)); } void RemoteGdbProcess::handleErrOutput() { - const QByteArray output = m_errOutputConn->waitForRemoteOutput(0); - if (!handleAppOrErrOutput(m_errOutputConn, m_errOutputReaderState, - m_initialErrOutput, ErrOutputFile, output)) { - m_errorOutput += output; - emit readyReadStandardError(); - } -} - -bool RemoteGdbProcess::handleAppOrErrOutput(Core::InteractiveSshConnection::Ptr &conn, - CmdState &cmdState, QByteArray &initialOutput, const QByteArray &file, - const QByteArray &output) -{ - const QByteArray cmdLine1 = mkFifoCmdLine(file); - const QByteArray cmdLine2 = readerCmdLine(file); - if (cmdState == CmdNotYetSent) { - conn->sendInput(cmdLine1); - cmdState = CmdSent; - return true; - } - - if (cmdState == CmdSent) { - initialOutput += output; - if (initialOutput.endsWith(cmdLine2)) { - cmdState = CmdReceived; - if (m_appOutputReaderState == m_errOutputReaderState - && m_gdbState == CmdNotYetSent) - startGdb(); - } else if (initialOutput.contains(cmdLine1) - && !initialOutput.endsWith(cmdLine1)) { - initialOutput.clear(); - conn->sendInput(cmdLine2); - } - return true; - } - - return false; -} - -void RemoteGdbProcess::startGdb() -{ - m_startCmdLine = "stty -echo && " + m_command.toUtf8() + ' ' - + m_cmdArgs.join(QLatin1String(" ")).toUtf8() - + " -tty=" + AppOutputFile + " 2>" + ErrOutputFile + '\n'; - if (!m_wd.isEmpty()) - m_startCmdLine.prepend("cd " + m_wd.toUtf8() + " && "); - sendInput(m_startCmdLine); - m_gdbState = CmdSent; + m_errorOutput += m_errOutputConn->waitForRemoteOutput(0); + emit readyReadStandardError(); } void RemoteGdbProcess::stopReaders() @@ -293,14 +246,9 @@ void RemoteGdbProcess::stopReaders() } } -QByteArray RemoteGdbProcess::mkFifoCmdLine(const QByteArray &file) -{ - return "rm -f " + file + " && mkfifo " + file + "\r\n"; -} - QByteArray RemoteGdbProcess::readerCmdLine(const QByteArray &file) { - return "cat " + file + "\r\n"; + return "rm -f " + file + " && mkfifo " + file + " && cat " + file + "\r\n"; } QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data) @@ -314,17 +262,42 @@ QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data) return output; } -void RemoteGdbProcess::checkForGdbExit(QByteArray &output) +bool RemoteGdbProcess::checkForGdbExit(QByteArray &output) { const QByteArray exitString("^exit"); const int exitPos = output.indexOf(exitString); - if (exitPos != -1) { - disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()), - this, SLOT(handleGdbOutput())); - output.remove(exitPos + exitString.size(), output.size()); - stopReaders(); - emit finished(0, QProcess::NormalExit); + if (exitPos == -1) + return false; + + emit finished(0, QProcess::NormalExit); + disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()), + this, SLOT(handleGdbOutput())); + output.remove(exitPos + exitString.size(), output.size()); + stopReaders(); + return true; +} + +bool RemoteGdbProcess::waitForInputReady(Core::InteractiveSshConnection::Ptr &conn) +{ + if (conn->waitForRemoteOutput(m_serverInfo.timeout).isEmpty()) + return false; + while (!conn->waitForRemoteOutput(100).isEmpty()) + ; + return true; +} + +bool RemoteGdbProcess::sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn, + const QByteArray &cmdLine) +{ + conn->sendInput(cmdLine); + QByteArray allOutput; + while (!allOutput.endsWith(cmdLine)) { + const QByteArray curOutput = conn->waitForRemoteOutput(100); + if (curOutput.isEmpty()) + return false; + allOutput += curOutput; } + return true; } diff --git a/src/plugins/debugger/gdb/remotegdbprocess.h b/src/plugins/debugger/gdb/remotegdbprocess.h index 04e2e5da0b4bae5813b909539f3485399646ecd5..1008b334da77a679083680b50e708ed32ca110a4 100644 --- a/src/plugins/debugger/gdb/remotegdbprocess.h +++ b/src/plugins/debugger/gdb/remotegdbprocess.h @@ -73,20 +73,16 @@ private slots: void handleErrOutput(); private: - enum CmdState { CmdNotYetSent, CmdSent, CmdReceived }; - - static QByteArray mkFifoCmdLine(const QByteArray &file); static QByteArray readerCmdLine(const QByteArray &file); int findAnchor(const QByteArray &data) const; - bool handleAppOrErrOutput(Core::InteractiveSshConnection::Ptr &conn, - CmdState &cmdState, QByteArray &initialOutput, - const QByteArray &file, const QByteArray &output); qint64 sendInput(const QByteArray &data); - void startGdb(); void stopReaders(); QByteArray removeCarriageReturn(const QByteArray &data); - void checkForGdbExit(QByteArray &output); + bool checkForGdbExit(QByteArray &output); + bool sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn, + const QByteArray &cmdLine); + bool waitForInputReady(Core::InteractiveSshConnection::Ptr &conn); static const QByteArray AppOutputFile; static const QByteArray ErrOutputFile; @@ -102,14 +98,9 @@ private: QString m_wd; QQueue<QByteArray> m_inputToSend; QByteArray m_currentGdbOutput; - QByteArray m_initialAppOutput; - QByteArray m_initialErrOutput; QByteArray m_lastSeqNr; - QByteArray m_startCmdLine; - - CmdState m_gdbState; - CmdState m_appOutputReaderState; - CmdState m_errOutputReaderState; + QByteArray m_gdbCmdLine; + bool m_gdbStarted; RemotePlainGdbAdapter *m_adapter; };