forked from qt-creator/qt-creator
Now it looks even more like QProcess. Things like process channels are still missing. Change-Id: I3f30cd00ed4a054d02e83add9a6f4162b48f8345 Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
241 lines
7.1 KiB
C++
241 lines
7.1 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
**
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
**
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
|
** Please review the following information to ensure the GNU Lesser General
|
|
** Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** Other Usage
|
|
**
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
** If you have questions regarding the use of this file, please contact
|
|
** Nokia at qt-info@nokia.com.
|
|
**
|
|
**************************************************************************/
|
|
|
|
#define QT_NO_CAST_FROM_ASCII
|
|
|
|
#include "lldbenginehost.h"
|
|
|
|
#include "debuggerstartparameters.h"
|
|
#include "debuggeractions.h"
|
|
#include "debuggerconstants.h"
|
|
#include "debuggerdialogs.h"
|
|
#include "debuggerplugin.h"
|
|
#include "debuggerstringutils.h"
|
|
|
|
#include "breakhandler.h"
|
|
#include "breakpoint.h"
|
|
#include "moduleshandler.h"
|
|
#include "registerhandler.h"
|
|
#include "stackhandler.h"
|
|
#include "watchhandler.h"
|
|
#include "watchutils.h"
|
|
#include "threadshandler.h"
|
|
#include "disassembleragent.h"
|
|
#include "memoryagent.h"
|
|
|
|
#include <coreplugin/icore.h>
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QtCore/QDebug>
|
|
#include <QtCore/QProcess>
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QThread>
|
|
#include <QtCore/QCoreApplication>
|
|
|
|
namespace Debugger {
|
|
namespace Internal {
|
|
|
|
SshIODevice::SshIODevice(Utils::SshRemoteProcessRunner *r)
|
|
: runner(r)
|
|
, buckethead(0)
|
|
{
|
|
setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered);
|
|
connect (runner, SIGNAL(processStarted()), this, SLOT(processStarted()));
|
|
connect(runner, SIGNAL(processOutputAvailable(const QByteArray &)),
|
|
this, SLOT(outputAvailable(const QByteArray &)));
|
|
connect(runner, SIGNAL(processErrorOutputAvailable(const QByteArray &)),
|
|
this, SLOT(errorOutputAvailable(const QByteArray &)));
|
|
}
|
|
|
|
SshIODevice::~SshIODevice()
|
|
{
|
|
delete runner;
|
|
}
|
|
|
|
qint64 SshIODevice::bytesAvailable () const
|
|
{
|
|
qint64 r = QIODevice::bytesAvailable();
|
|
foreach (const QByteArray &bucket, buckets)
|
|
r += bucket.size();
|
|
r-= buckethead;
|
|
return r;
|
|
}
|
|
qint64 SshIODevice::writeData (const char * data, qint64 maxSize)
|
|
{
|
|
if (proc == 0) {
|
|
startupbuffer += QByteArray::fromRawData(data, maxSize);
|
|
return maxSize;
|
|
}
|
|
proc->write(data, maxSize);
|
|
return maxSize;
|
|
}
|
|
qint64 SshIODevice::readData (char * data, qint64 maxSize)
|
|
{
|
|
if (proc == 0)
|
|
return 0;
|
|
qint64 size = maxSize;
|
|
while (size > 0) {
|
|
if (!buckets.size()) {
|
|
return maxSize - size;
|
|
}
|
|
QByteArray &bucket = buckets.head();
|
|
if ((size + buckethead) >= bucket.size()) {
|
|
int d = bucket.size() - buckethead;
|
|
memcpy(data, bucket.data() + buckethead, d);
|
|
data += d;
|
|
size -= d;
|
|
buckets.dequeue();
|
|
buckethead = 0;
|
|
} else {
|
|
memcpy(data, bucket.data() + buckethead, size);
|
|
data += size;
|
|
buckethead += size;
|
|
size = 0;
|
|
}
|
|
}
|
|
return maxSize - size;
|
|
}
|
|
|
|
void SshIODevice::processStarted()
|
|
{
|
|
proc = runner->process();
|
|
proc->write(startupbuffer);
|
|
}
|
|
|
|
void SshIODevice::outputAvailable(const QByteArray &output)
|
|
{
|
|
buckets.enqueue(output);
|
|
emit readyRead();
|
|
}
|
|
|
|
void SshIODevice::errorOutputAvailable(const QByteArray &output)
|
|
{
|
|
fprintf(stderr, "%s", output.data());
|
|
}
|
|
|
|
|
|
LldbEngineHost::LldbEngineHost(const DebuggerStartParameters &startParameters)
|
|
:IPCEngineHost(startParameters), m_ssh(0)
|
|
{
|
|
showMessage(QLatin1String("setting up coms"));
|
|
|
|
if (startParameters.startMode == StartRemoteEngine)
|
|
{
|
|
m_guestProcess = 0;
|
|
Utils::SshRemoteProcessRunner * const runner = new Utils::SshRemoteProcessRunner;
|
|
connect (runner, SIGNAL(connectionError(Utils::SshError)),
|
|
this, SLOT(sshConnectionError(Utils::SshError)));
|
|
runner->run(startParameters.serverStartScript.toUtf8(), startParameters.connParams);
|
|
setGuestDevice(new SshIODevice(runner));
|
|
} else {
|
|
m_guestProcess = new QProcess(this);
|
|
|
|
connect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
|
|
this, SLOT(finished(int, QProcess::ExitStatus)));
|
|
|
|
connect(m_guestProcess, SIGNAL(readyReadStandardError()), this,
|
|
SLOT(stderrReady()));
|
|
|
|
|
|
QString a = Core::ICore::instance()->resourcePath() + QLatin1String("/qtcreator-lldb");
|
|
if(getenv("QTC_LLDB_GUEST") != 0)
|
|
a = QString::fromLocal8Bit(getenv("QTC_LLDB_GUEST"));
|
|
|
|
showStatusMessage(QString(QLatin1String("starting %1")).arg(a));
|
|
|
|
m_guestProcess->start(a, QStringList(), QIODevice::ReadWrite | QIODevice::Unbuffered);
|
|
m_guestProcess->setReadChannel(QProcess::StandardOutput);
|
|
|
|
if (!m_guestProcess->waitForStarted()) {
|
|
showStatusMessage(tr("qtcreator-lldb failed to start: %1").arg(m_guestProcess->errorString()));
|
|
notifyEngineSpontaneousShutdown();
|
|
return;
|
|
}
|
|
|
|
setGuestDevice(m_guestProcess);
|
|
}
|
|
}
|
|
|
|
LldbEngineHost::~LldbEngineHost()
|
|
{
|
|
showMessage(QLatin1String("tear down qtcreator-lldb"));
|
|
|
|
if (m_guestProcess) {
|
|
disconnect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
|
|
this, SLOT(finished (int, QProcess::ExitStatus)));
|
|
|
|
|
|
m_guestProcess->terminate();
|
|
m_guestProcess->kill();
|
|
}
|
|
if (m_ssh && m_ssh->process().data()) {
|
|
// TODO: openssh doesn't do that
|
|
|
|
m_ssh->process()->kill();
|
|
}
|
|
}
|
|
|
|
void LldbEngineHost::nuke()
|
|
{
|
|
stderrReady();
|
|
showMessage(QLatin1String("Nuke engaged. Bug in Engine/IPC or incompatible IPC versions. "), LogError);
|
|
showStatusMessage(tr("Fatal engine shutdown. Consult debugger log for details."));
|
|
m_guestProcess->terminate();
|
|
m_guestProcess->kill();
|
|
notifyEngineSpontaneousShutdown();
|
|
}
|
|
void LldbEngineHost::sshConnectionError(Utils::SshError e)
|
|
{
|
|
showStatusMessage(tr("SSH connection error: %1").arg(e));
|
|
}
|
|
|
|
void LldbEngineHost::finished(int, QProcess::ExitStatus status)
|
|
{
|
|
showMessage(QString(QLatin1String("guest went bye bye. exit status: %1 and code: %2"))
|
|
.arg(status).arg(m_guestProcess->exitCode()), LogError);
|
|
nuke();
|
|
}
|
|
|
|
void LldbEngineHost::stderrReady()
|
|
{
|
|
fprintf(stderr,"%s", m_guestProcess->readAllStandardError().data());
|
|
}
|
|
|
|
DebuggerEngine *createLldbEngine(const DebuggerStartParameters &startParameters)
|
|
{
|
|
return new LldbEngineHost(startParameters);
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace Debugger
|
|
|