can now pluginto any host via ssh.
define QTC_LLDB_GUEST=/some/script in the env
where script is a wrapper that does ssh, or whatever you need
case ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC:
case ProjectExplorer::ToolChain_GCCE_GNUPOC:
case ProjectExplorer::ToolChain_GCC_MAEMO:
+ if (getenv("QTC_LLDB_GUEST"))
+ return LldbEngineType;
#ifdef WITH_LLDB
// lldb override
if (Core::ICore::instance()->settings()->value("LLDB/enabled").toBool())
delete m_listener;
}
+
+void LldbEngineGuest::nuke()
+{
+ ::exit(4);
+}
+
void LldbEngineGuest::setupEngine()
{
DEBUG_FUNC_ENTER;
for (int i = 0; i < m_environment.count(); i++) {
envp[i] = m_environment[i].data();
}
+ lldb::SBError err;
+ *m_process = m_target->Launch(argp, envp, NULL, NULL, false, err);
- *m_process = m_target->LaunchProcess(argp, envp, NULL, NULL, false);
+
+ if (!err.Success()) {
+ showMessage(QString::fromLocal8Bit(err.GetCString()));
+ qDebug() << err.GetCString();
+ notifyEngineRunFailed();
+ }
/*
* note, the actual string ptrs are still valid. They are in m_environment.
if (!m_process->IsValid())
notifyEngineRunFailed();
- QTC_ASSERT(m_listener->IsValid(), qDebug()<<false);
+ QTC_ASSERT(m_listener->IsValid(), qDebug() << false);
m_listener->StartListeningForEvents(m_process->GetBroadcaster(), UINT32_MAX);
QMetaObject::invokeMethod(m_worker, "listen", Qt::QueuedConnection,
Q_ARG(lldb::SBListener *, m_listener));
void LldbEngineGuest::lldbEvent(lldb::SBEvent *ev)
{
qDebug() << "lldbevent" << ev->GetType() <<
- ev->GetDataFlavor() << m_process->GetState() << (int)state();
+ m_process->GetState() << (int)state();
uint32_t etype = ev->GetType();
switch (etype) {
explicit LldbEngineGuest();
~LldbEngineGuest();
+ void nuke();
void setupEngine();
void setupInferior(const QString &executable, const QStringList &arguments,
const QStringList &environment);
#include <QtCore/QCoreApplication>
#include <QtNetwork/QLocalSocket>
#include "lldbengineguest.h"
+#include <cstdio>
+#include <QSocketNotifier>
+#include <QQueue>
-class DiePlease: public QThread
+
+// #define DO_STDIO_DEBUG 1
+#ifdef DO_STDIO_DEBUG
+#define D_STDIO0(x) qDebug(x)
+#define D_STDIO1(x,a1) qDebug(x,a1)
+#define D_STDIO2(x,a1,a2) qDebug(x,a1,a2)
+#define D_STDIO3(x,a1,a2,a3) qDebug(x,a1,a2,a3)
+#else
+#define D_STDIO0(x)
+#define D_STDIO1(x,a1)
+#define D_STDIO2(x,a1,a2)
+#define D_STDIO3(x,a1,a2,a3)
+#endif
+
+class Stdio : public QIODevice
{
- virtual void run()
+ Q_OBJECT
+public:
+ QSocketNotifier notify;
+ Stdio()
+ : QIODevice()
+ , notify(fileno(stdin), QSocketNotifier::Read)
+ , buckethead(0)
{
- getc(stdin);
- ::exit(0);
+ setvbuf(stdin , NULL , _IONBF , 0);
+ setvbuf(stdout , NULL , _IONBF , 0);
+ setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered);
+ connect(¬ify, SIGNAL(activated(int)), this, SLOT(activated()));
+ }
+ virtual qint64 bytesAvailable () const
+ {
+ qint64 r = QIODevice::bytesAvailable();
+ foreach (const QByteArray &bucket, buckets)
+ r += bucket.size();
+ r-= buckethead;
+ return r;
+ }
+
+ virtual qint64 readData (char * data, qint64 maxSize)
+ {
+ D_STDIO1("readData %lli",maxSize);
+ qint64 size = maxSize;
+ while (size > 0) {
+ if (!buckets.size()) {
+ D_STDIO1("done prematurely with %lli", maxSize - size);
+ return maxSize - size;
+ }
+ QByteArray &bucket = buckets.head();
+ if ((size + buckethead) >= bucket.size()) {
+ int d = bucket.size() - buckethead;
+ D_STDIO3("read (over bucket) d: %i buckethead: %i bucket.size(): %i",
+ d, buckethead, bucket.size());
+ memcpy(data, bucket.data() + buckethead, d);
+ data += d;
+ size -= d;
+ buckets.dequeue();
+ buckethead = 0;
+ } else {
+ D_STDIO1("read (in bucket) size: %lli", size);
+ memcpy(data, bucket.data() + buckethead, size);
+ data += size;
+ buckethead += size;
+ size = 0;
+ }
+ }
+ D_STDIO1("done with %lli",(maxSize - size));
+ return maxSize - size;
+ }
+
+ virtual qint64 writeData (const char * data, qint64 maxSize)
+ {
+ return ::write(fileno(stdout), data, maxSize);
+ }
+
+ QQueue<QByteArray> buckets;
+ int buckethead;
+
+private slots:
+ void activated()
+ {
+ QByteArray a;
+ a.resize(1000);
+ int ret = ::read(fileno(stdin), a.data(), 1000);
+ assert(ret <= 1000);
+ D_STDIO1("activated %i", ret);
+ a.resize(ret);
+ buckets.enqueue(a);
+ emit readyRead();
}
};
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
+ qDebug() << "guest engine operational";
Debugger::Internal::LldbEngineGuest lldb;
- QLocalSocket s;
- if (argc > 1) {
- // Pure test code. This is not intended for end users.
- // TODO: maybe some day we can have real unit tests for debugger engines?
- setenv("LLDB_DEBUGSERVER_PATH", "/Users/aep/qt-creator/src/plugins/debugger/lldb/lldb/build/Release/LLDB.framework/Resources/debugserver", 0);
- lldb.setupEngine();
- QStringList env;
- env.append(QLatin1String("DYLD_IMAGE_SUFFIX=_debug"));
- lldb.setupInferior(argv[1], QStringList(), env);
- Debugger::Internal::BreakpointParameters bp;
- bp.ignoreCount = 0;
- bp.lineNumber = 64;
- bp.fileName = QLatin1String("main.cpp");
- lldb.addBreakpoint(0, bp);
- lldb.runEngine();
- }else {
- qDebug() << "guest connected";
- s.connectToServer(QLatin1String("/tmp/qtcreator-debuggeripc"));
- s.waitForConnected();
- qDebug() << "guest connected";
- app.connect(&s, SIGNAL(disconnected ()), &app, SLOT(quit()));
- lldb.setHostDevice(&s);
- }
- DiePlease bla;
- bla.start();
+ Stdio stdio;
+ lldb.setHostDevice(&stdio);
+
return app.exec();
}
extern const double lldbVersionNumber __attribute__ ((used)) = (double)26.;
extern const double LLDBVersionNumber __attribute__ ((used)) = (double)26.;
}
+
+#include "main.moc"
qint64 rrr = m_nextMessagePayloadSize;
QByteArray payload = m_device->read(rrr);
if (quint64(payload.size()) != m_nextMessagePayloadSize || !payload.endsWith('T')) {
- showMessage(QLatin1String("IPC Error: corrupted frame"));
- showMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."), LogError);
- showStatusMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."));
- notifyEngineIll();
+ qDebug("IPC Error: corrupted frame");
+ showMessage(QLatin1String("[guest] IPC Error: corrupted frame"), LogError);
+ nuke();
return;
}
payload.chop(1);
rpcCallback(m_nextMessageFunction, payload);
m_nextMessagePayloadSize = 0;
- if (quint64(m_device->bytesAvailable ()) >= 3 * sizeof(quint64) * 3)
+ if (quint64(m_device->bytesAvailable ()) >= 3 * sizeof(quint64))
QTimer::singleShot(0, this, SLOT(readyRead()));
}
{
switch (f) {
default:
- showMessage(QLatin1String("IPC Error: unhandled id in host to guest call"));
- showMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."), LogError);
- showStatusMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."));
- notifyEngineIll();
+ qDebug("IPC Error: unhandled id in host to guest call");
+ showMessage(QLatin1String("IPC Error: unhandled id in host to guest call"), LogError);
+ nuke();
break;
case IPCEngineHost::SetupIPC:
{
void setLocalHost(IPCEngineHost *);
void setHostDevice(QIODevice *);
+ virtual void nuke() = 0;
virtual void setupEngine() = 0;
virtual void setupInferior(const QString &executeable,
const QStringList &arguments, const QStringList &environment) = 0;
showMessage(QLatin1String("IPC Error: unhandled id in guest to host call"));
showMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."), LogError);
showStatusMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."));
- notifyEngineSpontaneousShutdown();
+ nuke();
break;
case IPCEngineGuest::NotifyEngineSetupOk:
notifyEngineSetupOk();
if (terminator != 'T') {
showStatusMessage(tr("Fatal engine shutdown. Incompatible binary or ipc error."));
showMessage(QLatin1String("IPC Error: terminator missing"));
- notifyEngineSpontaneousShutdown();
+ nuke();
return;
}
rpcCallback(m_nextMessageFunction, payload);
const WatchUpdateFlags &flags = WatchUpdateFlags());
void rpcCall(Function f, QByteArray payload = QByteArray());
+protected:
+ virtual void nuke() = 0;
public slots:
void rpcCallback(quint64 f, QByteArray payload = QByteArray());
private slots:
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
-#include <QtNetwork/QLocalSocket>
-#include <QtNetwork/QLocalServer>
namespace Debugger {
namespace Internal {
{
showMessage(QLatin1String("setting up coms"));
- QLocalServer *s = new QLocalServer(this);
- s->removeServer(QLatin1String("/tmp/qtcreator-debuggeripc"));
- s->listen(QLatin1String("/tmp/qtcreator-debuggeripc"));
-
m_guestProcess = new QProcess(this);
- m_guestProcess->setProcessChannelMode(QProcess::ForwardedChannels);
connect(m_guestProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(finished(int, QProcess::ExitStatus)));
- showStatusMessage(QLatin1String("starting qtcreator-lldb"));
+ 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());
+ m_guestProcess->setReadChannel(QProcess::StandardOutput);
if (!m_guestProcess->waitForStarted()) {
- showStatusMessage(tr("qtcreator-lldb failed to start"));
- notifyEngineIll();
+ showStatusMessage(tr("qtcreator-lldb failed to start %1").arg(m_guestProcess->error()));
+ notifyEngineSpontaneousShutdown();
return;
}
- showMessage(QLatin1String("connecting"));
- s->waitForNewConnection(-1);
- QLocalSocket *f = s->nextPendingConnection();
- s->close(); // wtf race in accept
- showMessage(QLatin1String("connected"));
- setGuestDevice(f);
+ setGuestDevice(m_guestProcess);
}
LldbEngineHost::~LldbEngineHost()
m_guestProcess->kill();
}
-void LldbEngineHost::finished(int, QProcess::ExitStatus)
+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::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()
{
- showStatusMessage(tr("lldb crashed"));
- notifyEngineIll();
+ fprintf(stderr,"%s", m_guestProcess->readAllStandardError().data());
}
DebuggerEngine *createLldbEngine(const DebuggerStartParameters &startParameters)
private:
QProcess *m_guestProcess;
-
+protected:
+ void nuke();
private slots:
void finished(int, QProcess::ExitStatus);
+ void stderrReady();
};
} // namespace Internal