summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuli Piippo <samuli.piippo@theqtcompany.com>2015-04-22 16:27:14 +0300
committerSamuli Piippo <samuli.piippo@theqtcompany.com>2015-04-22 16:29:29 +0300
commit53ea6084989e386511f73aa7d1ebbc01b8a6b606 (patch)
treeb3e71185ca8d0d0e58cc17696da47e98736ea8ab
parente26ef5e48cba706b34376d1122313efad12c2528 (diff)
parentc900307ca2db204572bde2c02c471ec954ec8c1a (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.pro2
-rw-r--r--main.cpp134
-rw-r--r--perfprocesshandler.cpp39
-rw-r--r--perfprocesshandler.h42
-rw-r--r--portlist.cpp2
-rw-r--r--portlist.h2
-rw-r--r--process.cpp52
-rw-r--r--process.h11
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
diff --git a/main.cpp b/main.cpp
index e00fced..fb52564 100644
--- a/main.cpp
+++ b/main.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
@@ -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
diff --git a/portlist.h b/portlist.h
index 5784d94..171194c 100644
--- a/portlist.h
+++ b/portlist.h
@@ -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;
}
-
diff --git a/process.h b/process.h
index 781c121..bf67d96 100644
--- a/process.h
+++ b/process.h
@@ -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