diff options
author | Samuli Piippo <samuli.piippo@theqtcompany.com> | 2015-04-22 16:27:14 +0300 |
---|---|---|
committer | Samuli Piippo <samuli.piippo@theqtcompany.com> | 2015-04-22 16:29:29 +0300 |
commit | 53ea6084989e386511f73aa7d1ebbc01b8a6b606 (patch) | |
tree | b3e71185ca8d0d0e58cc17696da47e98736ea8ab | |
parent | e26ef5e48cba706b34376d1122313efad12c2528 (diff) | |
parent | c900307ca2db204572bde2c02c471ec954ec8c1a (diff) |
Merge remote-tracking branch 'origin/stable' into dev
* origin/stable:
pass variable number of arguments to --profile-perf
Don't abort if the output buffer overflows
Add perf profiler run mode
Add usage help message
Add detach option
Fix typo in license header
Change-Id: Ifc940480d6dae8ffde35053968d77772534b2d0d
-rw-r--r-- | appcontroller.pro | 2 | ||||
-rw-r--r-- | main.cpp | 134 | ||||
-rw-r--r-- | perfprocesshandler.cpp | 39 | ||||
-rw-r--r-- | perfprocesshandler.h | 42 | ||||
-rw-r--r-- | portlist.cpp | 2 | ||||
-rw-r--r-- | portlist.h | 2 | ||||
-rw-r--r-- | process.cpp | 52 | ||||
-rw-r--r-- | process.h | 11 |
8 files changed, 264 insertions, 20 deletions
diff --git a/appcontroller.pro b/appcontroller.pro index b467652..97b208e 100644 --- a/appcontroller.pro +++ b/appcontroller.pro @@ -3,11 +3,13 @@ QT+=network HEADERS=\ process.h \ portlist.h \ + perfprocesshandler.h SOURCES=\ main.cpp \ process.cpp \ portlist.cpp \ + perfprocesshandler.cpp android { target.path = $$[INSTALL_ROOT]/system/bin @@ -4,7 +4,7 @@ ** All rights reserved. ** For any questions to Digia, please use contact form at http://www.qt.io ** -** This file is part of QtEnterprise Embedded. +** This file is part of Qt Enterprise Embedded. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the @@ -18,6 +18,7 @@ #include "process.h" #include "portlist.h" +#include "perfprocesshandler.h" #include <QCoreApplication> #include <QTcpServer> #include <QProcess> @@ -29,6 +30,8 @@ #include <sys/un.h> #include <unistd.h> #include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> #define PID_FILE "/data/user/.appcontroller" @@ -42,6 +45,25 @@ static int serverSocket = -1; static const char socketPath[] = "#Boot2Qt_appcontroller"; +static void usage() +{ + printf("appcontroller [--debug-gdb] [--debug-qml] [--port-range <range>] [--stop] [--launch] [--show-platfrom] [--make-default] [--remove-default] [--print-debug] [--version] [--detach] [executable] [arguments]\n" + "\n" + "--port-range <range> Port range to use for debugging connections\n" + "--debug-gdb Start GDB debugging\n" + "--debug-qml Start QML debugging\n" + "--stop Stop already running application\n" + "--launch Start application without stopping already running application\n" + "--show-platform Show platform information\n" + "--make-default Make this application the default on boot\n" + "--remove-default Restore the default application\n" + "--print-debug Print debug messages to stdout on Android\n" + "--version Print version information\n" + "--detach Start application as usual, then go into background\n" + "--help, -h, -help Show this help\n" + ); +} + static void setupAddressStruct(struct sockaddr_un &address) { address.sun_family = AF_UNIX; @@ -123,17 +145,21 @@ static void stop() connectSocket(); } -static int findFirstFreePort(Utils::PortList &range) +static int openServer(QTcpServer *s, Utils::PortList &range) { - QTcpServer s; - while (range.hasMore()) { - if (s.listen(QHostAddress::Any, range.getNext())) - return s.serverPort(); + if (s->listen(QHostAddress::Any, range.getNext())) + return s->serverPort(); } return -1; } +static int findFirstFreePort(Utils::PortList &range) +{ + QTcpServer s; + return openServer(&s, range); +} + static Config parseConfigFile() { Config config; @@ -212,6 +238,30 @@ static bool makeDefault(const QString &filepath) return true; } +static QStringList extractPerfParams(QString s) +{ + QStringList lst; + int h = 0; + int i = 0; + for (;;) { + i = s.indexOf(QLatin1Char(','), i); + if (i >= 0) { + if (i + 1 < s.length() && s.at(i + 1) == QLatin1Char(',')) { + s.remove(i, 1); + i++; + continue; + } + lst << s.mid(h, i - h); + i++; + h = i; + } else { + lst << s.mid(h); + break; + } + } + return lst; +} + int main(int argc, char **argv) { // Save arguments before QCoreApplication handles them @@ -223,7 +273,9 @@ int main(int argc, char **argv) quint16 gdbDebugPort = 0; bool useGDB = false; bool useQML = false; + QStringList perfParams; bool fireAndForget = false; + bool detach = false; Utils::PortList range; if (args.isEmpty()) { @@ -252,6 +304,15 @@ int main(int argc, char **argv) setsid(); } else if (arg == "--debug-qml") { useQML = true; + } else if (arg == "--profile-perf") { + if (args.isEmpty()) { + fprintf(stderr, "--profile-perf requires comma-separated list of parameters that " + "get passed to \"perf record\". Arguments \"-o -\" are " + "automatically appended to capture the output as stream. " + "Escape commas by doubling them."); + return 1; + } + perfParams = extractPerfParams(args.takeFirst()); } else if (arg == "--stop") { stop(); return 0; @@ -280,6 +341,11 @@ int main(int argc, char **argv) } else if (arg == "--version") { printf("Appcontroller version: " GIT_VERSION "\nGit revision: " GIT_HASH "\n"); return 0; + } else if (arg == "--detach") { + detach = true; + } else if (arg == "--help" || arg == "-help" || arg == "-h") { + usage(); + return 0; } else { args.prepend(arg); break; @@ -296,6 +362,11 @@ int main(int argc, char **argv) return 1; } + if (detach && (useGDB || useQML)) { + fprintf(stderr, "Detached debugging not possible. --detach and one of --useGDB, --useQML must not be used together.\n"); + return 1; + } + if (useGDB) { int port = findFirstFreePort(range); if (port < 0) { @@ -331,6 +402,38 @@ int main(int argc, char **argv) return 1; } + // daemonize + if (detach) { + pid_t rc = fork(); + if (rc == -1) { + printf("fork failed\n"); + return -1; + } else if (rc > 0) { + // parent + ::wait(NULL); // wait for the child to exit + return 0; + } + + setsid(); + chdir("/"); + signal(SIGHUP, SIG_IGN); + + // child + int devnull = open("/dev/null", O_RDWR); + if (devnull < 0) + return -1; + dup2(devnull, 0); // Replace file descriptors + dup2(devnull, 1); + dup2(devnull, 2); + rc = fork(); + if (rc == -1) + return -1; + else if (rc > 0) + return 0; + + // child + } + // Create QCoreApplication after parameter parsing to prevent printing evaluation // message to terminal before QtCreator has parsed the output. QCoreApplication app(argc, argv); @@ -339,7 +442,24 @@ int main(int argc, char **argv) if (gdbDebugPort) process.setDebug(); process.setSocketNotifier(new QSocketNotifier(serverSocket, QSocketNotifier::Read, &process)); - process.start(defaultArgs); + + if (!perfParams.isEmpty()) { + QStringList allArgs; + allArgs << QLatin1String("perf") << QLatin1String("record") + << perfParams << QLatin1String("-o") << QLatin1String("-") + << QLatin1String("--") << defaultArgs.join(QLatin1Char(' ')); + + PerfProcessHandler *server = new PerfProcessHandler(&process, allArgs); + int port = openServer(server->server(), range); + if (port < 0) { + fprintf(stderr, "Could not find an unused port in range\n"); + return 1; + } + printf("AppController: Going to wait for perf connection on port %d...\n", port); + } else { + process.start(defaultArgs); + } + app.exec(); if (!fireAndForget) close(serverSocket); diff --git a/perfprocesshandler.cpp b/perfprocesshandler.cpp new file mode 100644 index 0000000..9b609ab --- /dev/null +++ b/perfprocesshandler.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of QtEnterprise Embedded. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ + +#include "perfprocesshandler.h" +#include <QTcpSocket> + +PerfProcessHandler::PerfProcessHandler(Process *process, const QStringList &allArgs) : mProcess(process), mAllArgs(allArgs) +{ + QObject::connect(&mServer, &QTcpServer::newConnection, this, &PerfProcessHandler::acceptConnection); +} + +QTcpServer *PerfProcessHandler::server() +{ + return &mServer; +} + +void PerfProcessHandler::acceptConnection() +{ + QTcpSocket *socket = mServer.nextPendingConnection(); + socket->setParent(mProcess); + mProcess->setStdoutFd(socket->socketDescriptor()); + mProcess->start(mAllArgs); + this->deleteLater(); +} diff --git a/perfprocesshandler.h b/perfprocesshandler.h new file mode 100644 index 0000000..f10e8a6 --- /dev/null +++ b/perfprocesshandler.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of QtEnterprise Embedded. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ + +#ifndef PERFPROCESSHANDLER_H +#define PERFPROCESSHANDLER_H + +#include "process.h" +#include <QTcpServer> + +// Starts the process once a connection to the TCP server is established and then deletes itself. +class PerfProcessHandler : public QObject { + Q_OBJECT + +private: + QTcpServer mServer; + Process *mProcess; + QStringList mAllArgs; + +public: + PerfProcessHandler(Process *process, const QStringList &allArgs); + QTcpServer *server(); + +public slots: + void acceptConnection(); +}; + +#endif // PERFPROCESSHANDLER_H diff --git a/portlist.cpp b/portlist.cpp index 34488ef..a93814e 100644 --- a/portlist.cpp +++ b/portlist.cpp @@ -4,7 +4,7 @@ ** All rights reserved. ** For any questions to Digia, please use contact form at http://www.qt.io ** -** This file is part of QtEnterprise Embedded. +** This file is part of Qt Enterprise Embedded. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the @@ -4,7 +4,7 @@ ** All rights reserved. ** For any questions to Digia, please use contact form at http://www.qt.io ** -** This file is part of QtEnterprise Embedded. +** This file is part of Qt Enterprise Embedded. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the diff --git a/process.cpp b/process.cpp index b7e7342..1fe811b 100644 --- a/process.cpp +++ b/process.cpp @@ -4,7 +4,7 @@ ** All rights reserved. ** For any questions to Digia, please use contact form at http://www.qt.io ** -** This file is part of QtEnterprise Embedded. +** This file is part of Qt Enterprise Embedded. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the @@ -26,6 +26,8 @@ #include <signal.h> #include <fcntl.h> #include <QFileInfo> +#include <QTcpSocket> +#include <errno.h> static int pipefd[2]; @@ -86,6 +88,7 @@ Process::Process() , mProcess(new QProcess(this)) , mDebuggee(0) , mDebug(false) + , mStdoutFd(1) { mProcess->setProcessChannelMode(QProcess::SeparateChannels); connect(mProcess, &QProcess::readyReadStandardError, this, &Process::readyReadStandardError); @@ -112,13 +115,41 @@ Process::~Process() close(pipefd[1]); } -void Process::readyReadStandardOutput() +void Process::forwardProcessOutput(qintptr fd, const QByteArray &data) { - QByteArray b = mProcess->readAllStandardOutput(); - write(1, b.constData(), b.size()); + const char *constData = data.constData(); + int size = data.size(); + while (size > 0) { + int written = write(fd, constData, size); + if (written == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + fd_set outputFdSet; + FD_ZERO(&outputFdSet); + FD_SET(fd, &outputFdSet); + fd_set inputFdSet; + FD_ZERO(&inputFdSet); + FD_SET(pipefd[0], &inputFdSet); + if (select(qMax(fd, pipefd[0]) + 1, &inputFdSet, &outputFdSet, NULL, NULL) > 0 && + !FD_ISSET(pipefd[0], &inputFdSet)) + continue; + // else fprintf below will output the appropriate errno + } + fprintf(stderr, "Cannot forward application output: %d - %s\n", errno, strerror(errno)); + qApp->quit(); + break; + } + size -= written; + constData += written; + } if (mConfig.flags.testFlag(Config::PrintDebugMessages)) - qDebug() << b; + qDebug() << data; +} + + +void Process::readyReadStandardOutput() +{ + forwardProcessOutput(mStdoutFd, mProcess->readAllStandardOutput()); } void Process::readyReadStandardError() @@ -131,10 +162,7 @@ void Process::readyReadStandardError() } mDebug = false; // only search once } - write(2, b.constData(), b.size()); - - if (mConfig.flags.testFlag(Config::PrintDebugMessages)) - qDebug() << b; + forwardProcessOutput(2, b); } void Process::setDebug() @@ -246,6 +274,11 @@ void Process::setConfig(const Config &config) mConfig = config; } +void Process::setStdoutFd(qintptr stdoutFd) +{ + mStdoutFd = stdoutFd; +} + QProcessEnvironment Process::interactiveProcessEnvironment() const { QProcessEnvironment env; @@ -311,4 +344,3 @@ QProcessEnvironment Process::interactiveProcessEnvironment() const return env; } - @@ -4,7 +4,7 @@ ** All rights reserved. ** For any questions to Digia, please use contact form at http://www.qt.io ** -** This file is part of QtEnterprise Embedded. +** This file is part of Qt Enterprise Embedded. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the @@ -16,9 +16,13 @@ ** ****************************************************************************/ +#ifndef PROCESS_H +#define PROCESS_H + #include <QObject> #include <QProcess> #include <QMap> +#include <QTcpServer> class QSocketNotifier; @@ -53,6 +57,7 @@ public: void setSocketNotifier(QSocketNotifier*); void setDebug(); void setConfig(const Config &); + void setStdoutFd(qintptr stdoutFd); public slots: void stop(); private slots: @@ -62,6 +67,7 @@ private slots: void error(QProcess::ProcessError); void incomingConnection(int); private: + void forwardProcessOutput(qintptr fd, const QByteArray &data); void startup(QStringList); QProcessEnvironment interactiveProcessEnvironment() const; QProcess *mProcess; @@ -69,4 +75,7 @@ private: bool mDebug; Config mConfig; QString mBinary; + qintptr mStdoutFd; }; + +#endif // PROCESS_H |