From adf95bca87410244fde97fc732dcc346e25e0dca Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 29 Jan 2015 19:02:16 +0100 Subject: Add perf profiler run mode Change-Id: If2f84bec32957ab9c45df503efaf592cebbd4f72 Reviewed-by: Joerg Bornemann --- appcontroller.pro | 2 ++ main.cpp | 47 +++++++++++++++++++++++++++++++++++++++++------ perfprocesshandler.cpp | 39 +++++++++++++++++++++++++++++++++++++++ perfprocesshandler.h | 42 ++++++++++++++++++++++++++++++++++++++++++ process.cpp | 38 +++++++++++++++++++++++++++++--------- process.h | 9 +++++++++ 6 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 perfprocesshandler.cpp create mode 100644 perfprocesshandler.h 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 9eef26e..741523c 100644 --- a/main.cpp +++ b/main.cpp @@ -18,6 +18,7 @@ #include "process.h" #include "portlist.h" +#include "perfprocesshandler.h" #include #include #include @@ -144,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; @@ -244,6 +249,7 @@ 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; @@ -274,6 +280,14 @@ int main(int argc, char **argv) setsid(); } else if (arg == "--debug-qml") { useQML = true; + } else if (arg == "--profile-perf") { + if (args.isEmpty() || + (perfParams = args.takeFirst().split(QLatin1Char(','))).length() != 3) { + fprintf(stderr, "--profile-perf requires a parameter specification of" + " \",,\" where can be \"fp\" or " + " \"dwarf\", amd and are integers."); + return 1; + } } else if (arg == "--stop") { stop(); return 0; @@ -403,7 +417,28 @@ int main(int argc, char **argv) if (gdbDebugPort) process.setDebug(); process.setSocketNotifier(new QSocketNotifier(serverSocket, QSocketNotifier::Read, &process)); - process.start(defaultArgs); + + if (perfParams.length() == 3) { + QStringList allArgs; + allArgs << QLatin1String("perf") << QLatin1String("record") << QLatin1String("--call-graph"); + if (perfParams[0] == QLatin1String("dwarf")) + allArgs << QString(QLatin1String("dwarf,%1")).arg(perfParams[1]); + else + allArgs << perfParams[0]; + allArgs << QLatin1String("-F") << perfParams[2] << 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 + +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 + +// 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/process.cpp b/process.cpp index 4c9140d..1320d02 100644 --- a/process.cpp +++ b/process.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include 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,29 @@ 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) { + 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 +150,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 +262,11 @@ void Process::setConfig(const Config &config) mConfig = config; } +void Process::setStdoutFd(qintptr stdoutFd) +{ + mStdoutFd = stdoutFd; +} + QProcessEnvironment Process::interactiveProcessEnvironment() const { QProcessEnvironment env; @@ -311,4 +332,3 @@ QProcessEnvironment Process::interactiveProcessEnvironment() const return env; } - diff --git a/process.h b/process.h index 386dfb2..bf67d96 100644 --- a/process.h +++ b/process.h @@ -16,9 +16,13 @@ ** ****************************************************************************/ +#ifndef PROCESS_H +#define PROCESS_H + #include #include #include +#include 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 -- cgit v1.2.3