Skip to content

Commit 53ea608

Browse files
author
Samuli Piippo
committed
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
2 parents e26ef5e + c900307 commit 53ea608

8 files changed

+264
-20
lines changed

appcontroller.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ QT+=network
33
HEADERS=\
44
process.h \
55
portlist.h \
6+
perfprocesshandler.h
67

78
SOURCES=\
89
main.cpp \
910
process.cpp \
1011
portlist.cpp \
12+
perfprocesshandler.cpp
1113

1214
android {
1315
target.path = $$[INSTALL_ROOT]/system/bin

main.cpp

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
** All rights reserved.
55
** For any questions to Digia, please use contact form at http://www.qt.io
66
**
7-
** This file is part of QtEnterprise Embedded.
7+
** This file is part of Qt Enterprise Embedded.
88
**
99
** Licensees holding valid Qt Enterprise licenses may use this file in
1010
** accordance with the Qt Enterprise License Agreement provided with the
@@ -18,6 +18,7 @@
1818

1919
#include "process.h"
2020
#include "portlist.h"
21+
#include "perfprocesshandler.h"
2122
#include <QCoreApplication>
2223
#include <QTcpServer>
2324
#include <QProcess>
@@ -29,6 +30,8 @@
2930
#include <sys/un.h>
3031
#include <unistd.h>
3132
#include <fcntl.h>
33+
#include <signal.h>
34+
#include <sys/wait.h>
3235

3336
#define PID_FILE "/data/user/.appcontroller"
3437

@@ -42,6 +45,25 @@ static int serverSocket = -1;
4245

4346
static const char socketPath[] = "#Boot2Qt_appcontroller";
4447

48+
static void usage()
49+
{
50+
printf("appcontroller [--debug-gdb] [--debug-qml] [--port-range <range>] [--stop] [--launch] [--show-platfrom] [--make-default] [--remove-default] [--print-debug] [--version] [--detach] [executable] [arguments]\n"
51+
"\n"
52+
"--port-range <range> Port range to use for debugging connections\n"
53+
"--debug-gdb Start GDB debugging\n"
54+
"--debug-qml Start QML debugging\n"
55+
"--stop Stop already running application\n"
56+
"--launch Start application without stopping already running application\n"
57+
"--show-platform Show platform information\n"
58+
"--make-default Make this application the default on boot\n"
59+
"--remove-default Restore the default application\n"
60+
"--print-debug Print debug messages to stdout on Android\n"
61+
"--version Print version information\n"
62+
"--detach Start application as usual, then go into background\n"
63+
"--help, -h, -help Show this help\n"
64+
);
65+
}
66+
4567
static void setupAddressStruct(struct sockaddr_un &address)
4668
{
4769
address.sun_family = AF_UNIX;
@@ -123,17 +145,21 @@ static void stop()
123145
connectSocket();
124146
}
125147

126-
static int findFirstFreePort(Utils::PortList &range)
148+
static int openServer(QTcpServer *s, Utils::PortList &range)
127149
{
128-
QTcpServer s;
129-
130150
while (range.hasMore()) {
131-
if (s.listen(QHostAddress::Any, range.getNext()))
132-
return s.serverPort();
151+
if (s->listen(QHostAddress::Any, range.getNext()))
152+
return s->serverPort();
133153
}
134154
return -1;
135155
}
136156

157+
static int findFirstFreePort(Utils::PortList &range)
158+
{
159+
QTcpServer s;
160+
return openServer(&s, range);
161+
}
162+
137163
static Config parseConfigFile()
138164
{
139165
Config config;
@@ -212,6 +238,30 @@ static bool makeDefault(const QString &filepath)
212238
return true;
213239
}
214240

241+
static QStringList extractPerfParams(QString s)
242+
{
243+
QStringList lst;
244+
int h = 0;
245+
int i = 0;
246+
for (;;) {
247+
i = s.indexOf(QLatin1Char(','), i);
248+
if (i >= 0) {
249+
if (i + 1 < s.length() && s.at(i + 1) == QLatin1Char(',')) {
250+
s.remove(i, 1);
251+
i++;
252+
continue;
253+
}
254+
lst << s.mid(h, i - h);
255+
i++;
256+
h = i;
257+
} else {
258+
lst << s.mid(h);
259+
break;
260+
}
261+
}
262+
return lst;
263+
}
264+
215265
int main(int argc, char **argv)
216266
{
217267
// Save arguments before QCoreApplication handles them
@@ -223,7 +273,9 @@ int main(int argc, char **argv)
223273
quint16 gdbDebugPort = 0;
224274
bool useGDB = false;
225275
bool useQML = false;
276+
QStringList perfParams;
226277
bool fireAndForget = false;
278+
bool detach = false;
227279
Utils::PortList range;
228280

229281
if (args.isEmpty()) {
@@ -252,6 +304,15 @@ int main(int argc, char **argv)
252304
setsid();
253305
} else if (arg == "--debug-qml") {
254306
useQML = true;
307+
} else if (arg == "--profile-perf") {
308+
if (args.isEmpty()) {
309+
fprintf(stderr, "--profile-perf requires comma-separated list of parameters that "
310+
"get passed to \"perf record\". Arguments \"-o -\" are "
311+
"automatically appended to capture the output as stream. "
312+
"Escape commas by doubling them.");
313+
return 1;
314+
}
315+
perfParams = extractPerfParams(args.takeFirst());
255316
} else if (arg == "--stop") {
256317
stop();
257318
return 0;
@@ -280,6 +341,11 @@ int main(int argc, char **argv)
280341
} else if (arg == "--version") {
281342
printf("Appcontroller version: " GIT_VERSION "\nGit revision: " GIT_HASH "\n");
282343
return 0;
344+
} else if (arg == "--detach") {
345+
detach = true;
346+
} else if (arg == "--help" || arg == "-help" || arg == "-h") {
347+
usage();
348+
return 0;
283349
} else {
284350
args.prepend(arg);
285351
break;
@@ -296,6 +362,11 @@ int main(int argc, char **argv)
296362
return 1;
297363
}
298364

365+
if (detach && (useGDB || useQML)) {
366+
fprintf(stderr, "Detached debugging not possible. --detach and one of --useGDB, --useQML must not be used together.\n");
367+
return 1;
368+
}
369+
299370
if (useGDB) {
300371
int port = findFirstFreePort(range);
301372
if (port < 0) {
@@ -331,6 +402,38 @@ int main(int argc, char **argv)
331402
return 1;
332403
}
333404

405+
// daemonize
406+
if (detach) {
407+
pid_t rc = fork();
408+
if (rc == -1) {
409+
printf("fork failed\n");
410+
return -1;
411+
} else if (rc > 0) {
412+
// parent
413+
::wait(NULL); // wait for the child to exit
414+
return 0;
415+
}
416+
417+
setsid();
418+
chdir("/");
419+
signal(SIGHUP, SIG_IGN);
420+
421+
// child
422+
int devnull = open("/dev/null", O_RDWR);
423+
if (devnull < 0)
424+
return -1;
425+
dup2(devnull, 0); // Replace file descriptors
426+
dup2(devnull, 1);
427+
dup2(devnull, 2);
428+
rc = fork();
429+
if (rc == -1)
430+
return -1;
431+
else if (rc > 0)
432+
return 0;
433+
434+
// child
435+
}
436+
334437
// Create QCoreApplication after parameter parsing to prevent printing evaluation
335438
// message to terminal before QtCreator has parsed the output.
336439
QCoreApplication app(argc, argv);
@@ -339,7 +442,24 @@ int main(int argc, char **argv)
339442
if (gdbDebugPort)
340443
process.setDebug();
341444
process.setSocketNotifier(new QSocketNotifier(serverSocket, QSocketNotifier::Read, &process));
342-
process.start(defaultArgs);
445+
446+
if (!perfParams.isEmpty()) {
447+
QStringList allArgs;
448+
allArgs << QLatin1String("perf") << QLatin1String("record")
449+
<< perfParams << QLatin1String("-o") << QLatin1String("-")
450+
<< QLatin1String("--") << defaultArgs.join(QLatin1Char(' '));
451+
452+
PerfProcessHandler *server = new PerfProcessHandler(&process, allArgs);
453+
int port = openServer(server->server(), range);
454+
if (port < 0) {
455+
fprintf(stderr, "Could not find an unused port in range\n");
456+
return 1;
457+
}
458+
printf("AppController: Going to wait for perf connection on port %d...\n", port);
459+
} else {
460+
process.start(defaultArgs);
461+
}
462+
343463
app.exec();
344464
if (!fireAndForget)
345465
close(serverSocket);

perfprocesshandler.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/****************************************************************************
2+
**
3+
** Copyright (C) 2015 The Qt Company Ltd
4+
** All rights reserved.
5+
** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
6+
**
7+
** This file is part of QtEnterprise Embedded.
8+
**
9+
** Licensees holding valid Qt Enterprise licenses may use this file in
10+
** accordance with the Qt Enterprise License Agreement provided with the
11+
** Software or, alternatively, in accordance with the terms contained in
12+
** a written agreement between you and The Qt Company.
13+
**
14+
** If you have questions regarding the use of this file, please use
15+
** contact form at http://www.qt.io/contact-us
16+
**
17+
****************************************************************************/
18+
19+
#include "perfprocesshandler.h"
20+
#include <QTcpSocket>
21+
22+
PerfProcessHandler::PerfProcessHandler(Process *process, const QStringList &allArgs) : mProcess(process), mAllArgs(allArgs)
23+
{
24+
QObject::connect(&mServer, &QTcpServer::newConnection, this, &PerfProcessHandler::acceptConnection);
25+
}
26+
27+
QTcpServer *PerfProcessHandler::server()
28+
{
29+
return &mServer;
30+
}
31+
32+
void PerfProcessHandler::acceptConnection()
33+
{
34+
QTcpSocket *socket = mServer.nextPendingConnection();
35+
socket->setParent(mProcess);
36+
mProcess->setStdoutFd(socket->socketDescriptor());
37+
mProcess->start(mAllArgs);
38+
this->deleteLater();
39+
}

perfprocesshandler.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/****************************************************************************
2+
**
3+
** Copyright (C) 2015 The Qt Company Ltd
4+
** All rights reserved.
5+
** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
6+
**
7+
** This file is part of QtEnterprise Embedded.
8+
**
9+
** Licensees holding valid Qt Enterprise licenses may use this file in
10+
** accordance with the Qt Enterprise License Agreement provided with the
11+
** Software or, alternatively, in accordance with the terms contained in
12+
** a written agreement between you and The Qt Company.
13+
**
14+
** If you have questions regarding the use of this file, please use
15+
** contact form at http://www.qt.io/contact-us
16+
**
17+
****************************************************************************/
18+
19+
#ifndef PERFPROCESSHANDLER_H
20+
#define PERFPROCESSHANDLER_H
21+
22+
#include "process.h"
23+
#include <QTcpServer>
24+
25+
// Starts the process once a connection to the TCP server is established and then deletes itself.
26+
class PerfProcessHandler : public QObject {
27+
Q_OBJECT
28+
29+
private:
30+
QTcpServer mServer;
31+
Process *mProcess;
32+
QStringList mAllArgs;
33+
34+
public:
35+
PerfProcessHandler(Process *process, const QStringList &allArgs);
36+
QTcpServer *server();
37+
38+
public slots:
39+
void acceptConnection();
40+
};
41+
42+
#endif // PERFPROCESSHANDLER_H

portlist.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
** All rights reserved.
55
** For any questions to Digia, please use contact form at http://www.qt.io
66
**
7-
** This file is part of QtEnterprise Embedded.
7+
** This file is part of Qt Enterprise Embedded.
88
**
99
** Licensees holding valid Qt Enterprise licenses may use this file in
1010
** accordance with the Qt Enterprise License Agreement provided with the

portlist.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
** All rights reserved.
55
** For any questions to Digia, please use contact form at http://www.qt.io
66
**
7-
** This file is part of QtEnterprise Embedded.
7+
** This file is part of Qt Enterprise Embedded.
88
**
99
** Licensees holding valid Qt Enterprise licenses may use this file in
1010
** accordance with the Qt Enterprise License Agreement provided with the

0 commit comments

Comments
 (0)