forked from qt-creator/qt-creator
Debugger: Use new SSH interface in RemoteGdbProcess.
This commit is contained in:
@@ -57,40 +57,49 @@ QByteArray RemoteGdbProcess::readAllStandardError()
|
|||||||
|
|
||||||
void RemoteGdbProcess::start(const QString &cmd, const QStringList &args)
|
void RemoteGdbProcess::start(const QString &cmd, const QStringList &args)
|
||||||
{
|
{
|
||||||
m_gdbState = CmdNotYetSent;
|
|
||||||
m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
||||||
if (m_gdbConn->hasError())
|
|
||||||
return;
|
|
||||||
m_appOutputReaderState = CmdNotYetSent;
|
|
||||||
m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
||||||
if (m_appOutputConn->hasError())
|
|
||||||
return;
|
|
||||||
m_errOutputReaderState = CmdNotYetSent;
|
|
||||||
m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
|
||||||
if (m_errOutputConn->hasError())
|
|
||||||
return;
|
|
||||||
m_command = cmd;
|
m_command = cmd;
|
||||||
m_cmdArgs = args;
|
m_cmdArgs = args;
|
||||||
connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
|
m_errOutputConn->start();
|
||||||
this, SLOT(handleGdbOutput()));
|
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()),
|
connect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()),
|
||||||
this, SLOT(handleAppOutput()));
|
this, SLOT(handleAppOutput()));
|
||||||
connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
|
connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
|
||||||
this, SLOT(handleErrOutput()));
|
this, SLOT(handleErrOutput()));
|
||||||
m_gdbConn->start();
|
connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
|
||||||
m_errOutputConn->start();
|
this, SLOT(handleGdbOutput()));
|
||||||
m_appOutputConn->start();
|
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;
|
||||||
|
|
||||||
bool RemoteGdbProcess::waitForStarted()
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 RemoteGdbProcess::write(const QByteArray &data)
|
qint64 RemoteGdbProcess::write(const QByteArray &data)
|
||||||
{
|
{
|
||||||
if (m_gdbState != CmdReceived || !m_inputToSend.isEmpty()
|
if (!m_gdbStarted || !m_inputToSend.isEmpty() || !m_lastSeqNr.isEmpty()) {
|
||||||
|| !m_lastSeqNr.isEmpty()) {
|
|
||||||
m_inputToSend.enqueue(data);
|
m_inputToSend.enqueue(data);
|
||||||
return data.size();
|
return data.size();
|
||||||
} else {
|
} else {
|
||||||
@@ -114,15 +123,7 @@ void RemoteGdbProcess::kill()
|
|||||||
|
|
||||||
QProcess::ProcessState RemoteGdbProcess::state() const
|
QProcess::ProcessState RemoteGdbProcess::state() const
|
||||||
{
|
{
|
||||||
switch (m_gdbState) {
|
return m_gdbStarted ? QProcess::Running : QProcess::Starting;
|
||||||
case CmdNotYetSent:
|
|
||||||
return QProcess::NotRunning;
|
|
||||||
case CmdSent:
|
|
||||||
return QProcess::Starting;
|
|
||||||
case CmdReceived:
|
|
||||||
default:
|
|
||||||
return QProcess::Running;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString RemoteGdbProcess::errorString() const
|
QString RemoteGdbProcess::errorString() const
|
||||||
@@ -132,30 +133,30 @@ QString RemoteGdbProcess::errorString() const
|
|||||||
|
|
||||||
void RemoteGdbProcess::handleGdbOutput()
|
void RemoteGdbProcess::handleGdbOutput()
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
qDebug("%s: output is '%s'", Q_FUNC_INFO, output.data());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (m_gdbState == CmdNotYetSent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_currentGdbOutput
|
m_currentGdbOutput
|
||||||
+= removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0));
|
+= removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0));
|
||||||
|
#if 0
|
||||||
|
qDebug("%s: complete unread output is '%s'", Q_FUNC_INFO, m_currentGdbOutput.data());
|
||||||
|
#endif
|
||||||
|
if (checkForGdbExit(m_currentGdbOutput)) {
|
||||||
|
m_currentGdbOutput.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_currentGdbOutput.endsWith('\n'))
|
if (!m_currentGdbOutput.endsWith('\n'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_gdbState == CmdSent) {
|
if (!m_gdbStarted) {
|
||||||
const int index = m_currentGdbOutput.indexOf(m_startCmdLine);
|
const int index = m_currentGdbOutput.indexOf(m_gdbCmdLine);
|
||||||
if (index != -1)
|
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,
|
// Note: We can't guarantee that we will match the command line,
|
||||||
// because the remote terminal sometimes inserts control characters.
|
// 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 + '^'))
|
if (m_currentGdbOutput.contains(m_lastSeqNr + '^'))
|
||||||
m_lastSeqNr.clear();
|
m_lastSeqNr.clear();
|
||||||
|
|
||||||
@@ -220,61 +221,13 @@ qint64 RemoteGdbProcess::sendInput(const QByteArray &data)
|
|||||||
|
|
||||||
void RemoteGdbProcess::handleAppOutput()
|
void RemoteGdbProcess::handleAppOutput()
|
||||||
{
|
{
|
||||||
const QByteArray output = m_appOutputConn->waitForRemoteOutput(0);
|
m_adapter->handleApplicationOutput(m_appOutputConn->waitForRemoteOutput(0));
|
||||||
if (!handleAppOrErrOutput(m_appOutputConn, m_appOutputReaderState,
|
|
||||||
m_initialAppOutput, AppOutputFile, output))
|
|
||||||
m_adapter->handleApplicationOutput(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteGdbProcess::handleErrOutput()
|
void RemoteGdbProcess::handleErrOutput()
|
||||||
{
|
{
|
||||||
const QByteArray output = m_errOutputConn->waitForRemoteOutput(0);
|
m_errorOutput += m_errOutputConn->waitForRemoteOutput(0);
|
||||||
if (!handleAppOrErrOutput(m_errOutputConn, m_errOutputReaderState,
|
|
||||||
m_initialErrOutput, ErrOutputFile, output)) {
|
|
||||||
m_errorOutput += output;
|
|
||||||
emit readyReadStandardError();
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteGdbProcess::stopReaders()
|
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)
|
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)
|
QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data)
|
||||||
@@ -314,17 +262,42 @@ QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteGdbProcess::checkForGdbExit(QByteArray &output)
|
bool RemoteGdbProcess::checkForGdbExit(QByteArray &output)
|
||||||
{
|
{
|
||||||
const QByteArray exitString("^exit");
|
const QByteArray exitString("^exit");
|
||||||
const int exitPos = output.indexOf(exitString);
|
const int exitPos = output.indexOf(exitString);
|
||||||
if (exitPos != -1) {
|
if (exitPos == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
emit finished(0, QProcess::NormalExit);
|
||||||
disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
|
disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
|
||||||
this, SLOT(handleGdbOutput()));
|
this, SLOT(handleGdbOutput()));
|
||||||
output.remove(exitPos + exitString.size(), output.size());
|
output.remove(exitPos + exitString.size(), output.size());
|
||||||
stopReaders();
|
stopReaders();
|
||||||
emit finished(0, QProcess::NormalExit);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -73,20 +73,16 @@ private slots:
|
|||||||
void handleErrOutput();
|
void handleErrOutput();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum CmdState { CmdNotYetSent, CmdSent, CmdReceived };
|
|
||||||
|
|
||||||
static QByteArray mkFifoCmdLine(const QByteArray &file);
|
|
||||||
static QByteArray readerCmdLine(const QByteArray &file);
|
static QByteArray readerCmdLine(const QByteArray &file);
|
||||||
|
|
||||||
int findAnchor(const QByteArray &data) const;
|
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);
|
qint64 sendInput(const QByteArray &data);
|
||||||
void startGdb();
|
|
||||||
void stopReaders();
|
void stopReaders();
|
||||||
QByteArray removeCarriageReturn(const QByteArray &data);
|
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 AppOutputFile;
|
||||||
static const QByteArray ErrOutputFile;
|
static const QByteArray ErrOutputFile;
|
||||||
@@ -102,14 +98,9 @@ private:
|
|||||||
QString m_wd;
|
QString m_wd;
|
||||||
QQueue<QByteArray> m_inputToSend;
|
QQueue<QByteArray> m_inputToSend;
|
||||||
QByteArray m_currentGdbOutput;
|
QByteArray m_currentGdbOutput;
|
||||||
QByteArray m_initialAppOutput;
|
|
||||||
QByteArray m_initialErrOutput;
|
|
||||||
QByteArray m_lastSeqNr;
|
QByteArray m_lastSeqNr;
|
||||||
QByteArray m_startCmdLine;
|
QByteArray m_gdbCmdLine;
|
||||||
|
bool m_gdbStarted;
|
||||||
CmdState m_gdbState;
|
|
||||||
CmdState m_appOutputReaderState;
|
|
||||||
CmdState m_errOutputReaderState;
|
|
||||||
|
|
||||||
RemotePlainGdbAdapter *m_adapter;
|
RemotePlainGdbAdapter *m_adapter;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user