Commit b39f26e8 authored by Fawzi Mohamed's avatar Fawzi Mohamed
Browse files

ios: device debugging



switched to using a relay server

Change-Id: Ic3ddb48b818fa43894314f7fbaf9d7780fc01ade
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@digia.com>
parent 8429f174
......@@ -650,6 +650,7 @@ class Dumper(DumperBase):
self.processArgs_ = args.get('processArgs', '')
self.attachPid_ = args.get('attachPid', 0)
self.sysRoot_ = args.get('sysRoot', '')
self.remoteChannel_ = args.get('remoteChannel', '')
if len(self.sysRoot_)>0:
self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_)
......@@ -670,15 +671,28 @@ class Dumper(DumperBase):
if self.attachPid_ > 0:
attachInfo = lldb.SBAttachInfo(self.attachPid_)
self.process = self.target.Attach(attachInfo, error)
if not err.Success():
self.report('state="inferiorrunfailed"')
return
self.report('pid="%s"' % self.process.GetProcessID())
self.report('state="enginerunandinferiorstopok"')
elif len(self.remoteChannel_) > 0:
err = lldb.SBError()
self.process = self.target.ConnectRemote(
self.debugger.GetListener(),
self.remoteChannel_, None, error)
self.report('state="enginerunandinferiorstopok"')
else:
launchInfo = lldb.SBLaunchInfo(self.processArgs_.split(' '))
launchInfo.SetWorkingDirectory(os.getcwd())
environmentList = [key + "=" + value for key,value in os.environ.items()]
launchInfo.SetEnvironmentEntries(environmentList, False)
self.process = self.target.Launch(launchInfo, error)
self.report('pid="%s"' % self.process.GetProcessID())
self.report('state="enginerunandinferiorrunok"')
if not err.Success():
self.report('state="inferiorrunfailed"')
return
self.report('pid="%s"' % self.process.GetProcessID())
self.report('state="enginerunandinferiorrunok"')
event = lldb.SBEvent()
while True:
......
......@@ -182,7 +182,8 @@ void LldbEngine::setupInferior()
cmd.arg("attachPid", ((sp.startMode == AttachCrashedExternal || sp.startMode == AttachExternal)
? sp.attachPID : 0));
cmd.arg("sysRoot", sp.sysRoot);
cmd.arg("remoteChannel", ((sp.startMode == AttachToRemoteProcess || sp.startMode == AttachToRemoteServer)
cmd.arg("remoteChannel", ((sp.startMode == AttachToRemoteProcess
|| sp.startMode == AttachToRemoteServer)
? sp.remoteChannel : QString()));
runCommand(cmd);
......@@ -1132,10 +1133,9 @@ void LldbEngine::notifyEngineRemoteSetupDone(int portOrPid, int qmlPort)
} else {
QString &rc = startParameters().remoteChannel;
const int sepIndex = rc.lastIndexOf(QLatin1Char(':'));
if (sepIndex != -1) {
if (sepIndex != -1)
rc.replace(sepIndex + 1, rc.count() - sepIndex - 1,
QString::number(portOrPid));
}
}
}
startLldb();
......
......@@ -92,6 +92,7 @@ RunControl *IosDebugSupport::createDebugRunControl(IosRunConfiguration *runConfi
if (ToolChain *tc = ToolChainKitInformation::toolChain(kit))
params.toolChainAbi = tc->targetAbi();
params.executable = runConfig->exePath().toString();
params.remoteChannel = QLatin1String("connect://localhost:0");
}
if (aspect->useQmlDebugger()) {
params.languages |= QmlLanguage;
......@@ -116,7 +117,7 @@ IosDebugSupport::IosDebugSupport(IosRunConfiguration *runConfig,
DebuggerRunControl *runControl)
: QObject(runControl), m_runControl(runControl),
m_runner(new IosRunner(this, runConfig, true)),
m_gdbServerFd(0), m_qmlPort(0)
m_qmlPort(0)
{
connect(m_runControl->engine(), SIGNAL(requestRemoteSetup()),
......@@ -124,8 +125,8 @@ IosDebugSupport::IosDebugSupport(IosRunConfiguration *runConfig,
connect(m_runControl, SIGNAL(finished()),
m_runner, SLOT(stop()));
connect(m_runner, SIGNAL(gotGdbSocket(int)),
SLOT(handleGdbServerFd(int)));
connect(m_runner, SIGNAL(gotGdbserverPort(int)),
SLOT(handleGdbServerPort(int)));
connect(m_runner, SIGNAL(gotInferiorPid(Q_PID)),
SLOT(handleGotInferiorPid(Q_PID)));
connect(m_runner, SIGNAL(finished(bool)),
......@@ -139,18 +140,12 @@ IosDebugSupport::IosDebugSupport(IosRunConfiguration *runConfig,
IosDebugSupport::~IosDebugSupport()
{
if (m_gdbServerFd > 0)
close(m_gdbServerFd);
}
void IosDebugSupport::handleGdbServerFd(int gdbServerFd)
void IosDebugSupport::handleGdbServerPort(int gdbServerPort)
{
if (m_gdbServerFd > 0) {
close(m_gdbServerFd);
m_gdbServerFd = 0;
}
if (gdbServerFd > 0) {
m_runControl->engine()->notifyEngineRemoteSetupDone(m_gdbServerFd, m_qmlPort);
if (gdbServerPort > 0) {
m_runControl->engine()->notifyEngineRemoteSetupDone(gdbServerPort, m_qmlPort);
} else {
m_runControl->engine()->notifyEngineRemoteSetupFailed(
tr("Could not get debug server file descriptor."));
......
......@@ -54,7 +54,7 @@ public:
~IosDebugSupport();
private slots:
void handleGdbServerFd(int gdbServerFd);
void handleGdbServerPort(int gdbServerFd);
void handleGotInferiorPid(Q_PID);
void handleRemoteProcessFinished(bool cleanEnd);
......@@ -66,7 +66,6 @@ private:
IosRunner * const m_runner;
const QString m_dumperLib;
int m_gdbServerFd;
int m_qmlPort;
};
......
......@@ -110,8 +110,8 @@ void IosRunner::start()
SLOT(handleDidStartApp(Ios::IosToolHandler*,QString,QString,Ios::IosToolHandler::OpStatus)));
connect(m_toolHandler, SIGNAL(errorMsg(Ios::IosToolHandler*,QString)),
SLOT(handleErrorMsg(Ios::IosToolHandler*,QString)));
connect(m_toolHandler, SIGNAL(gotGdbserverSocket(Ios::IosToolHandler*,QString,QString,int)),
SLOT(handleGotGdbserverSocket(Ios::IosToolHandler*,QString,QString,int)));
connect(m_toolHandler, SIGNAL(gotGdbserverPort(Ios::IosToolHandler*,QString,QString,int)),
SLOT(handleGotGdbserverPort(Ios::IosToolHandler*,QString,QString,int)));
connect(m_toolHandler, SIGNAL(gotInferiorPid(Ios::IosToolHandler*,QString,QString,Q_PID)),
SLOT(handleGotInferiorPid(Ios::IosToolHandler*,QString,QString,Q_PID)));
connect(m_toolHandler, SIGNAL(toolExited(Ios::IosToolHandler*,int)),
......@@ -140,12 +140,12 @@ void IosRunner::handleDidStartApp(IosToolHandler *handler, const QString &bundle
emit didStartApp(status);
}
void IosRunner::handleGotGdbserverSocket(IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, int gdbFd)
void IosRunner::handleGotGdbserverPort(IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, int gdbPort)
{
Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
if (m_toolHandler == handler)
emit gotGdbserverSocket(gdbFd);
emit gotGdbserverPort(gdbPort);
}
void IosRunner::handleGotInferiorPid(IosToolHandler *handler, const QString &bundlePath,
......
......@@ -64,7 +64,7 @@ public slots:
signals:
void didStartApp(Ios::IosToolHandler::OpStatus status);
void gotGdbserverSocket(int gdbFd);
void gotGdbserverPort(int gdbPort);
void gotInferiorPid(Q_PID pid);
void appOutput(const QString &output);
void errorMsg(const QString &msg);
......@@ -74,8 +74,8 @@ private slots:
void warnAboutDeployFail();
void handleDidStartApp(Ios::IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, Ios::IosToolHandler::OpStatus status);
void handleGotGdbserverSocket(Ios::IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, int gdbFd);
void handleGotGdbserverPort(Ios::IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, int gdbPort);
void handleGotInferiorPid(Ios::IosToolHandler *handler, const QString &bundlePath,
const QString &deviceId, Q_PID pid);
void handleAppOutput(Ios::IosToolHandler *handler, const QString &output);
......
......@@ -43,16 +43,7 @@
#include <QScopedArrayPointer>
#include <QProcessEnvironment>
#if defined(Q_OS_UNIX)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#else
#include <WinSock2.h>
#endif
#include <string.h>
#include <fcntl.h>
#include <errno.h>
static const bool debugToolHandler = false;
......@@ -61,21 +52,6 @@ namespace Ios {
namespace Internal {
class MyProcess: public QProcess
{
Q_OBJECT
public:
explicit MyProcess(QObject *parent = 0);
~MyProcess();
int processOutputSocket();
QSocketNotifier *notifier();
protected:
virtual void setupChildProcess();
private:
int m_sockets[2];
QSocketNotifier *m_notifier;
};
struct ParserState {
enum Kind {
Msg,
......@@ -86,6 +62,7 @@ struct ParserState {
AppOutput,
AppStarted,
InferiorPid,
GdbServerPort,
Item,
Status,
AppTransfer,
......@@ -107,6 +84,7 @@ struct ParserState {
case Value:
case Status:
case InferiorPid:
case GdbServerPort:
return true;
case QueryResult:
case AppOutput:
......@@ -131,7 +109,6 @@ public:
NonStarted,
Starting,
StartedInferior,
XmlEndSeenNotProcessed,
XmlEndProcessed,
Stopped
};
......@@ -161,7 +138,7 @@ public:
IosToolHandler::OpStatus status);
void didStartApp(const QString &bundlePath, const QString &deviceId,
IosToolHandler::OpStatus status);
void gotGdbserverSocket(const QString &bundlePath, const QString &deviceId, int gdbFd);
void gotGdbserverPort(const QString &bundlePath, const QString &deviceId, int gdbPort);
void gotInferiorPid(const QString &bundlePath, const QString &deviceId, Q_PID pid);
void deviceInfo(const QString &deviceId, const IosToolHandler::Dict &info);
void appOutput(const QString &output);
......@@ -170,14 +147,13 @@ public:
// slots
void subprocessError(QProcess::ProcessError error);
void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void subprocessHasData(int socket);
void subprocessHasData();
virtual bool expectsFileDescriptor() = 0;
protected:
int checkForXmlEnd();
void processXml();
IosToolHandler *q;
MyProcess process;
QProcess process;
QXmlStreamReader outputParser;
QString deviceId;
QString bundlePath;
......@@ -186,8 +162,6 @@ protected:
Op op;
IosToolHandler::DeviceType devType;
static const int lookaheadSize = 67;
QByteArray buffer;
QByteArray currentData;
int iBegin, iEnd, gdbSocket;
QList<ParserState> stack;
};
......@@ -220,66 +194,9 @@ private:
void addDeviceArguments(QStringList &args) const;
};
#if defined(Q_OS_UNIX)
MyProcess::MyProcess(QObject *parent) : QProcess(parent)
{
if (socketpair(PF_UNIX, SOCK_STREAM, 0, &m_sockets[0]) == -1) {
qDebug() << "IosToolHandler socketpair failed ";
}
m_notifier = new QSocketNotifier(m_sockets[0], QSocketNotifier::Read, this);
}
MyProcess::~MyProcess()
{
::close(m_sockets[0]);
::close(m_sockets[1]);
}
int MyProcess::processOutputSocket()
{
return m_sockets[0];
}
QSocketNotifier *MyProcess::notifier()
{
return m_notifier;
}
void MyProcess::setupChildProcess()
{
if (dup2(m_sockets[1], 1) == -1) { // use the unix socket as stdout
qDebug() << "IosToolHandler dup2 call failed";
emit finished(-1, QProcess::CrashExit);
exit(-1);
}
}
#else
MyProcess::MyProcess(QObject *parent) : QProcess(parent)
{
}
MyProcess::~MyProcess()
{
}
int MyProcess::processOutputSocket()
{
return 0;
}
QSocketNotifier *MyProcess::notifier()
{
return m_notifier;
}
void MyProcess::setupChildProcess()
{
}
#endif
IosToolHandlerPrivate::IosToolHandlerPrivate(IosToolHandler::DeviceType devType,
Ios::IosToolHandler *q) :
q(q), state(NonStarted), devType(devType), buffer(4*lookaheadSize, 0), iBegin(0), iEnd(0),
q(q), state(NonStarted), devType(devType), iBegin(0), iEnd(0),
gdbSocket(-1)
{
QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
......@@ -287,17 +204,11 @@ IosToolHandlerPrivate::IosToolHandlerPrivate(IosToolHandler::DeviceType devType,
if (k.startsWith(QLatin1String("DYLD_")))
env.remove(k);
process.setProcessEnvironment(env);
QObject::connect(process.notifier(), SIGNAL(activated(int)), q, SLOT(subprocessHasData(int)));
QObject::connect(&process, SIGNAL(readyReadStandardOutput()), q, SLOT(subprocessHasData()));
QObject::connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)),
q, SLOT(subprocessFinished(int,QProcess::ExitStatus)));
QObject::connect(&process, SIGNAL(error(QProcess::ProcessError)),
q, SLOT(subprocessError(QProcess::ProcessError)));
#if defined(Q_OS_UNIX)
int accessFlags = fcntl(process.processOutputSocket(), F_GETFL);
if (fcntl(process.processOutputSocket(), F_SETFL, accessFlags | O_NONBLOCK) == -1)
qDebug() << "IosToolHandler fcntl F_SETFL failed to set non blocking mode"
<< qt_error_string(errno);
#endif
}
bool IosToolHandlerPrivate::isRunning()
......@@ -320,9 +231,6 @@ void IosToolHandlerPrivate::stop()
if (debugToolHandler)
qDebug() << "IosToolHandlerPrivate::stop";
if (process.state() != QProcess::NotRunning) {
#if defined(Q_OS_UNIX)
close(process.processOutputSocket());
#endif
process.close();
process.kill();
if (debugToolHandler)
......@@ -353,10 +261,10 @@ void IosToolHandlerPrivate::didStartApp(const QString &bundlePath, const QString
emit q->didStartApp(q, bundlePath, deviceId, status);
}
void IosToolHandlerPrivate::gotGdbserverSocket(const QString &bundlePath, const QString &deviceId,
int gdbFd)
void IosToolHandlerPrivate::gotGdbserverPort(const QString &bundlePath,
const QString &deviceId, int gdbPort)
{
emit q->gotGdbserverSocket(q, bundlePath, deviceId, gdbFd);
emit q->gotGdbserverPort(q, bundlePath, deviceId, gdbPort);
}
void IosToolHandlerPrivate::gotInferiorPid(const QString &bundlePath, const QString &deviceId,
......@@ -408,7 +316,6 @@ void IosToolHandlerPrivate::subprocessError(QProcess::ProcessError error)
}
// pass
case StartedInferior:
case XmlEndSeenNotProcessed:
errorMsg(IosToolHandler::tr("Subprocess Error %1").arg(error));
toolExited(-1);
break;
......@@ -421,7 +328,7 @@ void IosToolHandlerPrivate::subprocessError(QProcess::ProcessError error)
void IosToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
// process potentially pending data
subprocessHasData(process.processOutputSocket());
subprocessHasData();
switch (state) {
case NonStarted:
qDebug() << "subprocessFinished() when state was NonStarted";
......@@ -442,7 +349,6 @@ void IosToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatu
}
// pass
case StartedInferior:
case XmlEndSeenNotProcessed:
case XmlEndProcessed:
toolExited((exitStatus == QProcess::CrashExit && exitCode == 0) ? -1 : exitCode);
break;
......@@ -453,106 +359,6 @@ void IosToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatu
}
}
#if defined(Q_OS_UNIX)
#ifndef CMSG_SPACE
size_t CMSG_SPACE(size_t len) {
msghdr msg;
cmsghdr cmsg;
msg.msg_control = &cmsg;
msg.msg_controllen = ~socklen_t(0); /* To maximize the chance that CMSG_NXTHDR won't return NULL */
cmsg.cmsg_len = CMSG_LEN(len);
return reinterpret_cast<unsigned char *>(CMSG_NXTHDR(&msg, &cmsg)) - reinterpret_cast<unsigned char *>(&cmsg);
}
#endif
int recv_fd(int socket)
{
int sent_fd;
char message_buffer[1];
iovec io_vector[1];
memset(&io_vector[0], 0, sizeof(iovec));
/* setup a place to fill in message contents */
io_vector[0].iov_base = message_buffer;
io_vector[0].iov_len = 1;
msghdr socket_message;
memset(&socket_message, 0, sizeof(struct msghdr));
socket_message.msg_iov = io_vector;
socket_message.msg_iovlen = 1;
/* provide space for the ancillary data */
size_t dimAncillaryEl = CMSG_SPACE(sizeof(int));
QScopedArrayPointer<char> ancillary_element_buffer(new char[dimAncillaryEl]);
memset(ancillary_element_buffer.data(), 0, dimAncillaryEl);
socket_message.msg_control = ancillary_element_buffer.data();
socket_message.msg_controllen = dimAncillaryEl;
int flags = 0;
#ifdef MSG_CMSG_CLOEXEC
flags = MSG_CMSG_CLOEXEC;
#endif
if (recvmsg(socket, &socket_message, flags) < 0)
return -1;
if (message_buffer[0] != '.') {
qDebug() << "IosToolHandler, unexpected inband data when receiving socket";
return -1;
}
if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) {
qDebug() << "IosToolHandler, not provide enough space for the ancillary element array";
return -1;
}
/* iterate ancillary elements */
cmsghdr *control_message = NULL;
for (control_message = CMSG_FIRSTHDR(&socket_message);
control_message != NULL;
control_message = CMSG_NXTHDR(&socket_message, control_message)) {
if ( (control_message->cmsg_level == SOL_SOCKET) &&
(control_message->cmsg_type == SCM_RIGHTS) ) {
sent_fd = *((int *) CMSG_DATA(control_message));
// aknowledge fd
send(socket, "x", 1, 0);
return sent_fd;
}
}
return -1;
}
#else
int recv_fd(int socket)
{
Q_UNUSED(socket);
return -1;
}
#endif
int IosToolHandlerPrivate::checkForXmlEnd()
{
const char *xmlEnd = "</query_result>";
int lenXmlEnd = 15;
int i = 0, j = 0;
while (i < lenXmlEnd && j < iEnd) {
if (buffer.at(j) != xmlEnd[i]) {
if (i == 0) {
++j;
if (j + lenXmlEnd > iEnd)
break;
} else {
i = 0;
}
} else {
++i;
++j;
}
}
if (i == lenXmlEnd)
return j;
return -1;
}
void IosToolHandlerPrivate::processXml()
{
while (!outputParser.atEnd()) {
......@@ -630,6 +436,8 @@ void IosToolHandlerPrivate::processXml()
stack.append(ParserState(ParserState::DeviceInfo));
} else if (elName == QLatin1String("inferior_pid")) {
stack.append(ParserState(ParserState::InferiorPid));
} else if (elName == QLatin1String("gdb_server_port")) {
stack.append(ParserState(ParserState::GdbServerPort));
} else {
qDebug() << "unexpected element " << elName;
}
......@@ -679,6 +487,9 @@ void IosToolHandlerPrivate::processXml()
case ParserState::InferiorPid:
gotInferiorPid(bundlePath, deviceId, Q_PID(p.chars.toInt()));
break;
case ParserState::GdbServerPort:
gotGdbserverPort(bundlePath, deviceId, p.chars.toInt());
break;
}
break;
}
......@@ -716,11 +527,10 @@ void IosToolHandlerPrivate::processXml()
}
}
void IosToolHandlerPrivate::subprocessHasData(int socket)
void IosToolHandlerPrivate::subprocessHasData()
{
if (debugToolHandler)
qDebug() << "subprocessHasData, state:" << state;
process.notifier()->setEnabled(false);
while (true) {
switch (state) {
case NonStarted:
......@@ -730,114 +540,30 @@ void IosToolHandlerPrivate::subprocessHasData(int socket)
case StartedInferior:
// read some data
{
if (iEnd + lookaheadSize > buffer.size()) {
memmove(buffer.data(), buffer.data() + (iEnd - lookaheadSize), lookaheadSize);
iBegin = lookaheadSize;
iEnd = iBegin;
} else {
iBegin = iEnd;
}
currentData.clear();
qptrdiff reallyRead = recv(socket, buffer.data() + iBegin, lookaheadSize, 0);
if (reallyRead == 0) { // eof
stop();
return;
}
if (reallyRead == -1) {
if (errno == EAGAIN) { // read all so far
if (debugToolHandler)
qDebug() << "read all for now";
process.notifier()->setEnabled(true);
return;
}
if (errno == EINTR)
continue;
qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno);
stop();
return;
}
iEnd = iBegin + reallyRead;
int xmlEnd = checkForXmlEnd();
if (xmlEnd != -1) {
state = XmlEndSeenNotProcessed;
currentData = buffer.mid(iBegin, xmlEnd - iBegin);
} else {
currentData = buffer.mid(iBegin, reallyRead);
}
if (debugToolHandler)
qDebug() << "subprocessHasData read " << currentData;
outputParser.addData(currentData);
processXml();
break;
}
case XmlEndSeenNotProcessed:
qDebug() << "IosToolHandler unexpected state in subprocessHasData: XmlEndSeenNotProcessed";
// pass
case XmlEndProcessed:
{
// check for sent fd
if (!expectsFileDescriptor()) {
stop();
return;
}
int lenToRead = lookaheadSize;
int spacerStart = iBegin + currentData.size();
while (spacerStart < iEnd && buffer.at(spacerStart) != 'n')
++spacerStart;