diff options
-rw-r--r-- | share/qtcreator/debugger/lldbbridge.py | 20 | ||||
-rw-r--r-- | src/plugins/debugger/lldb/lldbengine.cpp | 6 | ||||
-rw-r--r-- | src/plugins/ios/iosdebugsupport.cpp | 19 | ||||
-rw-r--r-- | src/plugins/ios/iosdebugsupport.h | 3 | ||||
-rw-r--r-- | src/plugins/ios/iosrunner.cpp | 10 | ||||
-rw-r--r-- | src/plugins/ios/iosrunner.h | 6 | ||||
-rw-r--r-- | src/plugins/ios/iostoolhandler.cpp | 338 | ||||
-rw-r--r-- | src/plugins/ios/iostoolhandler.h | 6 | ||||
-rw-r--r-- | src/tools/iostool/iosdevicemanager.cpp | 15 | ||||
-rw-r--r-- | src/tools/iostool/iostool.pro | 2 | ||||
-rw-r--r-- | src/tools/iostool/iostool.qbs | 1 | ||||
-rw-r--r-- | src/tools/iostool/main.cpp | 272 |
12 files changed, 279 insertions, 419 deletions
diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 92e039dd2c..765af26686 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -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: diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index fc0a937ae5..fa8aea082a 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -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(); diff --git a/src/plugins/ios/iosdebugsupport.cpp b/src/plugins/ios/iosdebugsupport.cpp index f4d6befda8..6ce8ee49e6 100644 --- a/src/plugins/ios/iosdebugsupport.cpp +++ b/src/plugins/ios/iosdebugsupport.cpp @@ -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.")); diff --git a/src/plugins/ios/iosdebugsupport.h b/src/plugins/ios/iosdebugsupport.h index 59b223fd5e..0367179839 100644 --- a/src/plugins/ios/iosdebugsupport.h +++ b/src/plugins/ios/iosdebugsupport.h @@ -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; }; diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index 64749552ff..002ae8fb70 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -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, diff --git a/src/plugins/ios/iosrunner.h b/src/plugins/ios/iosrunner.h index 76f1ed1c89..2e2e777ece 100644 --- a/src/plugins/ios/iosrunner.h +++ b/src/plugins/ios/iosrunner.h @@ -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); diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index c5d15c62e1..4152a045fc 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -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; - if (iEnd - (iBegin + currentData.size()) < lenToRead) { - int lastXmlSize = currentData.size(); - if (iBegin > 0) { - memmove(buffer.data(), buffer.data() + iBegin, iEnd - iBegin); - iEnd -= iBegin; - iBegin = 0; - spacerStart -= iBegin; - currentData = buffer.mid(0, lastXmlSize); // remove this?? - } - qptrdiff toRead = lookaheadSize - (iEnd - spacerStart); - qptrdiff reallyRead = recv(socket, buffer.data() + iEnd, toRead, 0); - if (reallyRead == 0) { // eof + char buf[200]; + while (true) { + qint64 rRead = process.read(buf, sizeof(buf)); + if (rRead == -1) { stop(); return; } - if (reallyRead == -1) { - if (errno == EAGAIN) { // read all so far - if (debugToolHandler) - qDebug() << "read all for now2"; - process.notifier()->setEnabled(true); - return; - } - if (errno == EINTR) - continue; - if (debugToolHandler) - qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno); - stop(); + if (rRead == 0) { + qDebug() << "read 0"; return; } - iEnd += reallyRead; - if (reallyRead != toRead) - continue; - if (spacerStart < iEnd && buffer.at(spacerStart) != 'N') { - ++spacerStart; - while (spacerStart < iEnd && buffer.at(spacerStart) != 'N') - ++spacerStart; - continue; - } + if (debugToolHandler) + qDebug() << "subprocessHasData read " << QByteArray(buf, rRead); + outputParser.addData(QByteArray(buf, rRead)); + processXml(); } - if (buffer.at(iEnd-1) != 'd') { - qDebug() << "IosToolHandler: bad alignment of spacer: " << buffer.mid(iBegin, iEnd - iBegin); - return; - } - gdbSocket = recv_fd(socket); - qDebug() << "IosToolHandler: receivedSocket"; - gotGdbserverSocket(bundlePath, deviceId, gdbSocket); + } + case XmlEndProcessed: stop(); return; - } case Stopped: return; } } - process.notifier()->setEnabled(true); } // IosDeviceToolHandlerPrivate @@ -1048,11 +774,9 @@ void IosToolHandler::subprocessFinished(int exitCode, QProcess::ExitStatus exitS d->subprocessFinished(exitCode, exitStatus); } -void IosToolHandler::subprocessHasData(int socket) +void IosToolHandler::subprocessHasData() { - d->subprocessHasData(socket); + d->subprocessHasData(); } } // namespace Ios - -#include "iostoolhandler.moc" diff --git a/src/plugins/ios/iostoolhandler.h b/src/plugins/ios/iostoolhandler.h index 8d4f5c0b96..11c4a56e75 100644 --- a/src/plugins/ios/iostoolhandler.h +++ b/src/plugins/ios/iostoolhandler.h @@ -83,8 +83,8 @@ signals: const QString &deviceId, Ios::IosToolHandler::OpStatus status); void didStartApp(Ios::IosToolHandler *handler, const QString &bundlePath, const QString &deviceId, Ios::IosToolHandler::OpStatus status); - void gotGdbserverSocket(Ios::IosToolHandler *handler, const QString &bundlePath, - const QString &deviceId, int gdbFd); + void gotGdbserverPort(Ios::IosToolHandler *handler, const QString &bundlePath, + const QString &deviceId, int gdbPort); void gotInferiorPid(Ios::IosToolHandler *handler, const QString &bundlePath, const QString &deviceId, Q_PID pid); void deviceInfo(Ios::IosToolHandler *handler, const QString &deviceId, @@ -98,7 +98,7 @@ public slots: private slots: void subprocessError(QProcess::ProcessError error); void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus); - void subprocessHasData(int socket); + void subprocessHasData(); private: friend class Ios::Internal::IosToolHandlerPrivate; Ios::Internal::IosToolHandlerPrivate *d; diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index cee55b6501..2e3db289d2 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -275,9 +275,10 @@ public: AMDeviceRef device; int progressBase; int unexpectedChars; + bool aknowledge; private: bool checkRead(qptrdiff nRead, int &maxRetry); - int handleChar(QByteArray &res, char c, int status); + int handleChar(int sock, QByteArray &res, char c, int status); }; // ------- IosManagerPrivate interface -------- @@ -709,7 +710,7 @@ int IosDeviceManagerPrivate::processGdbServer(int fd) // ------- ConnectSession implementation -------- CommandSession::CommandSession(const QString &deviceId) : deviceId(deviceId), device(0), - progressBase(0), unexpectedChars(0) + progressBase(0), unexpectedChars(0), aknowledge(true) { } CommandSession::~CommandSession() { } @@ -876,7 +877,7 @@ bool CommandSession::checkRead(qptrdiff nRead, int &maxRetry) return true; } -int CommandSession::handleChar(QByteArray &res, char c, int status) +int CommandSession::handleChar(int fd, QByteArray &res, char c, int status) { switch (status) { case 0: @@ -911,6 +912,8 @@ int CommandSession::handleChar(QByteArray &res, char c, int status) ++unexpectedChars; } } + if (status == 3 && aknowledge) + writeAll(fd, "+", 1); return status + 1; case 4: addError(QString::fromLatin1("gone past end in readGdbReply")); @@ -940,7 +943,7 @@ QByteArray CommandSession::readGdbReply(ServiceSocket fd) qDebug() << "gdbReply read " << buf; } for (qptrdiff i = 0; i< nRead; ++i) - status = handleChar(res, buf[i], status); + status = handleChar(fd, res, buf[i], status); toRead = 4 - status; } if (status != 4) { @@ -1095,8 +1098,8 @@ bool AppOpSession::runApp() // gdbServer protocol, see http://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol // and the lldb handling of that (with apple specific stuff) // http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.cpp - failure = !sendGdbCommand(gdbFd, "QStartNoAckMode"); // avoid and send required aknowledgements? - if (!failure) failure = !expectGdbOkReply(gdbFd); + //failure = !sendGdbCommand(gdbFd, "QStartNoAckMode"); // avoid and send required aknowledgements? + //if (!failure) failure = !expectGdbOkReply(gdbFd); if (!failure) failure = !sendGdbCommand(gdbFd, "QEnvironmentHexEncoded:"); // send the environment with a series of these commands... if (!failure) failure = !sendGdbCommand(gdbFd, "QSetDisableASLR:1"); // avoid address randomization to debug if (!failure) failure = !expectGdbOkReply(gdbFd); diff --git a/src/tools/iostool/iostool.pro b/src/tools/iostool/iostool.pro index 2870efaee2..e33e105a84 100644 --- a/src/tools/iostool/iostool.pro +++ b/src/tools/iostool/iostool.pro @@ -1,7 +1,7 @@ TARGET = iostool QT += core -QT += gui xml +QT += gui xml network CONFIG += console diff --git a/src/tools/iostool/iostool.qbs b/src/tools/iostool/iostool.qbs index c05359c68f..abb95f0975 100644 --- a/src/tools/iostool/iostool.qbs +++ b/src/tools/iostool/iostool.qbs @@ -7,6 +7,7 @@ QtcTool { Depends { name: "Qt.widgets" } Depends { name: "Qt.xml" } + Depends { name: "Qt.network" } Depends { name: "app_version_header" } files: [ diff --git a/src/tools/iostool/main.cpp b/src/tools/iostool/main.cpp index 9acc5a71a2..3a38b6f389 100644 --- a/src/tools/iostool/main.cpp +++ b/src/tools/iostool/main.cpp @@ -38,26 +38,34 @@ #include <QFile> #include <QMapIterator> #include <QScopedArrayPointer> +#include <QTcpServer> +#include <QSocketNotifier> +#include <QTcpSocket> #include "iosdevicemanager.h" #include <sys/types.h> +#include <string.h> #include <sys/socket.h> #include <sys/un.h> -#include <unistd.h> -#include <string.h> #include <errno.h> +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <fcntl.h> +#endif +static const bool debugGdbEchoServer = false; class IosTool: public QObject { Q_OBJECT public: IosTool(QObject *parent = 0); + virtual ~IosTool(); void run(const QStringList &args); void doExit(int errorCode = 0); void writeMsg(const char *msg); void writeMsg(const QString &msg); void stopXml(int errorCode); -public slots: +private slots: void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress, const QString &info); void didTransferApp(const QString &bundlePath, const QString &deviceId, @@ -67,15 +75,27 @@ public slots: void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info); void appOutput(const QString &output); void errorMsg(const QString &msg); + void handleNewConnection(); + void handleGdbServerSocketHasData(int socket); + void handleCreatorHasData(); + void handleCreatorHasError(QAbstractSocket::SocketError error); private: + bool startServer(); + void stopGdbServer(int errorCode = 0); + int maxProgress; int opLeft; bool debug; + bool ipv6; bool inAppOutput; bool splitAppOutput; // as QXmlStreamReader reports the text attributes atomically it is better to split Ios::IosDeviceManager::AppOp appOp; QFile outFile; QXmlStreamWriter out; + int gdbFileDescriptor; + QTcpSocket *creatorSocket; + QSocketNotifier *gdbServerNotifier; + QTcpServer gdbServer; }; IosTool::IosTool(QObject *parent): @@ -83,11 +103,15 @@ IosTool::IosTool(QObject *parent): maxProgress(0), opLeft(0), debug(false), + ipv6(false), inAppOutput(false), splitAppOutput(true), appOp(Ios::IosDeviceManager::Install), outFile(), - out(&outFile) + out(&outFile), + gdbFileDescriptor(-1), + creatorSocket(0), + gdbServerNotifier(0) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) outFile.open(stdout, QIODevice::WriteOnly, QFile::DontCloseHandle); @@ -98,6 +122,12 @@ IosTool::IosTool(QObject *parent): out.setCodec("UTF-8"); } +IosTool::~IosTool() +{ + delete creatorSocket; // not strictly required + delete gdbServerNotifier; // not strictly required +} + void IosTool::run(const QStringList &args) { Ios::IosDeviceManager *manager = Ios::IosDeviceManager::instance(); @@ -128,6 +158,8 @@ void IosTool::run(const QStringList &args) appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Install); } else if (arg == QLatin1String("-run")) { appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run); + } else if (arg == QLatin1String("-ipv6")) { + ipv6 = true; } else if (arg == QLatin1String("-debug")) { appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run); debug = true; @@ -229,68 +261,6 @@ void IosTool::isTransferringApp(const QString &bundlePath, const QString &device outFile.flush(); } -#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 send_fd(int socket, int fd_to_send) -{ - /* storage space needed for an ancillary element with a paylod of length is CMSG_SPACE(sizeof(length)) */ - size_t dimAncillaryBuffer = CMSG_SPACE(sizeof(int)); - QScopedArrayPointer<char> ancillary_element_buffer(new char[dimAncillaryBuffer]); - int available_ancillary_element_buffer_space; - - /* at least one vector of one byte must be sent */ - char message_buffer[1]; - message_buffer[0] = '.'; - iovec io_vector[1]; - - io_vector[0].iov_base = message_buffer; - io_vector[0].iov_len = 1; - - /* initialize socket message */ - msghdr socket_message; - memset(&socket_message, 0, sizeof(msghdr)); - socket_message.msg_iov = io_vector; - socket_message.msg_iovlen = 1; - - /* provide space for the ancillary data */ - available_ancillary_element_buffer_space = dimAncillaryBuffer; - memset(ancillary_element_buffer.data(), 0, available_ancillary_element_buffer_space); - socket_message.msg_control = ancillary_element_buffer.data(); - socket_message.msg_controllen = available_ancillary_element_buffer_space; - - /* initialize a single ancillary data element for fd passing */ - cmsghdr *control_message = NULL; - control_message = CMSG_FIRSTHDR(&socket_message); - control_message->cmsg_level = SOL_SOCKET; - control_message->cmsg_type = SCM_RIGHTS; - control_message->cmsg_len = CMSG_LEN(sizeof(int)); - *((int *) CMSG_DATA(control_message)) = fd_to_send; - - qptrdiff res = sendmsg(socket, &socket_message, 0); - while (true) { - qptrdiff nRead = recv(socket, &message_buffer[0], 1, MSG_WAITALL); - if (nRead == -1) { - if (errno == EINTR) - continue; - qDebug() << "wait in send_fd failed " << qt_error_string(errno); - sleep(4); - return res; - } - if (nRead == 1) - break; - } - return res; -} - void IosTool::didTransferApp(const QString &bundlePath, const QString &deviceId, Ios::IosDeviceManager::OpStatus status) { @@ -326,7 +296,7 @@ void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, QLatin1String("FAILURE")); //out.writeCharacters(QString()); // trigger a complete closing of the empty element outFile.flush(); - if (appOp == Ios::IosDeviceManager::Install) { + if (status != Ios::IosDeviceManager::Success || appOp == Ios::IosDeviceManager::Install) { doExit(); return; } @@ -341,14 +311,14 @@ void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, return; } if (debug) { - stopXml(0); - // these are 67 characters, this is used as read size on the other side... - const char *msg = "Now sending the gdbserver socket, will need a unix socket to succeed"; - outFile.write(msg, strlen(msg)); + gdbFileDescriptor=gdbFd; + if (!startServer()) { + doExit(-4); + return; + } + out.writeTextElement(QLatin1String("gdb_server_port"), + QString::number(gdbServer.serverPort())); outFile.flush(); - int sent = send_fd(1, gdbFd); - sleep(1); - QCoreApplication::exit(sent == -1); } else { if (!splitAppOutput) { out.writeStartElement(QLatin1String("app_output")); @@ -410,6 +380,160 @@ void IosTool::errorMsg(const QString &msg) writeMsg(msg + QLatin1Char('\n')); } +void IosTool::handleNewConnection() +{ + if (creatorSocket) { + gdbServer.close(); + QTcpSocket *s = gdbServer.nextPendingConnection(); + delete s; + return; + } + creatorSocket = gdbServer.nextPendingConnection(); + connect(creatorSocket, SIGNAL(readyRead()), SLOT(handleCreatorHasData())); + connect(creatorSocket, SIGNAL(error(QAbstractSocket::SocketError)), + SLOT(handleCreatorHasError(QAbstractSocket::SocketError))); + gdbServerNotifier = new QSocketNotifier(gdbFileDescriptor, QSocketNotifier::Read, this); + connect(gdbServerNotifier, SIGNAL(activated(int)), SLOT(handleGdbServerSocketHasData(int))); + gdbServer.close(); +} + +void IosTool::handleGdbServerSocketHasData(int socket) +{ + gdbServerNotifier->setEnabled(false); + char buf[255]; + while (true) { + qptrdiff rRead = read(socket, &buf, sizeof(buf)-1); + if (rRead == -1) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + gdbServerNotifier->setEnabled(true); + return; + } + errorMsg(qt_error_string(errno)); + close(socket); + stopGdbServer(-1); + return; + } + if (rRead == 0) { + stopGdbServer(0); + return; + } + if (debugGdbEchoServer) { + writeMsg("gdbServerReplies:"); + buf[rRead] = 0; + writeMsg(buf); + } + qint64 pos = 0; + while (true) { + qint64 writtenNow = creatorSocket->write(buf + int(pos), rRead); + if (writtenNow == -1) { + writeMsg(creatorSocket->errorString()); + stopGdbServer(-1); + return; + } + if (writtenNow < rRead) { + pos += writtenNow; + rRead -= qptrdiff(writtenNow); + } else { + break; + } + } + } +} + +void IosTool::stopGdbServer(int errorCode) +{ + if (debugGdbEchoServer) + writeMsg("gdbServerStops"); + if (!creatorSocket) + return; + if (gdbFileDescriptor > 0) { + ::close(gdbFileDescriptor); + gdbFileDescriptor = -1; + if (gdbServerNotifier) + delete gdbServerNotifier; + } + if (creatorSocket->isOpen()) + creatorSocket->close(); + delete creatorSocket; + doExit(errorCode); +} + +void IosTool::handleCreatorHasData() +{ + char buf[255]; + while (true) { + qint64 toRead = creatorSocket->bytesAvailable(); + if (qint64(sizeof(buf)-1) < toRead) + toRead = sizeof(buf)-1; + qint64 rRead = creatorSocket->read(buf, toRead); + if (rRead == -1) { + errorMsg(creatorSocket->errorString()); + stopGdbServer(); + return; + } + if (rRead == 0) { + if (!creatorSocket->isOpen()) + stopGdbServer(); + return; + } + int pos = 0; + int irep = 0; + if (debugGdbEchoServer) { + writeMsg("sendToGdbServer:"); + buf[rRead] = 0; + writeMsg(buf); + } + while (true) { + qptrdiff written = write(gdbFileDescriptor, buf + pos, rRead); + if (written == -1) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + if (++irep > 10) { + sleep(1); + irep = 0; + } + continue; + } + errorMsg(creatorSocket->errorString()); + stopGdbServer(); + return; + } + if (written == 0) { + stopGdbServer(); + return; + } + if (written < rRead) { + pos += written; + rRead -= written; + } else { + break; + } + } + } +} + +void IosTool::handleCreatorHasError(QAbstractSocket::SocketError error) +{ + errorMsg(tr("Ios Debugging connection to creator failed with error %1").arg(error)); + stopGdbServer(); +} + +bool IosTool::startServer() +{ + if (gdbFileDescriptor <= 0 || gdbServer.isListening() || creatorSocket != 0) + return false; + fcntl(gdbFileDescriptor, F_SETFL, fcntl(gdbFileDescriptor, F_GETFL, 0) | O_NONBLOCK); + gdbServer.setMaxPendingConnections(1); + connect(&gdbServer, SIGNAL(newConnection()), SLOT(handleNewConnection())); + if (ipv6) + return gdbServer.listen(QHostAddress(QHostAddress::LocalHostIPv6), 0); + else + return gdbServer.listen(QHostAddress(QHostAddress::LocalHost), 0); +} + int main(int argc, char *argv[]) { |