// Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "perfprofilerruncontrol.h" #include "perfdatareader.h" #include "perfprofilertool.h" #include "perfprofilertr.h" #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace Utils; namespace PerfProfiler::Internal { class PerfParserWorker final : public RunWorker { public: PerfParserWorker(RunControl *runControl) : RunWorker(runControl) { setId("PerfParser"); auto tool = PerfProfilerTool::instance(); m_reader.setTraceManager(&traceManager()); m_reader.triggerRecordingStateChange(tool->isRecording()); connect(tool, &PerfProfilerTool::recordingChanged, &m_reader, &PerfDataReader::triggerRecordingStateChange); connect(&m_reader, &PerfDataReader::updateTimestamps, tool, &PerfProfilerTool::updateTime); connect(&m_reader, &PerfDataReader::starting, tool, &PerfProfilerTool::startLoading); connect(&m_reader, &PerfDataReader::started, tool, &PerfProfilerTool::onReaderStarted); connect(&m_reader, &PerfDataReader::finishing, this, [tool] { // Temporarily disable buttons. tool->setToolActionsEnabled(false); }); connect(&m_reader, &PerfDataReader::finished, tool, &PerfProfilerTool::onReaderFinished); connect(&m_reader, &PerfDataReader::processStarted, this, &RunWorker::reportStarted); connect(&m_reader, &PerfDataReader::processFinished, this, &RunWorker::reportStopped); connect(&m_reader, &PerfDataReader::processFailed, this, &RunWorker::reportFailure); } void start() final { CommandLine cmd{findPerfParser()}; m_reader.addTargetArguments(&cmd, runControl()); QUrl url = runControl()->property("PerfConnection").toUrl(); if (url.isValid()) { cmd.addArgs({"--host", url.host(), "--port", QString::number(url.port())}); } appendMessage("PerfParser args: " + cmd.arguments(), NormalMessageFormat); m_reader.createParser(cmd); m_reader.startParser(); } void stop() final { m_reader.stopParser(); } PerfDataReader *reader() { return &m_reader;} private: PerfDataReader m_reader; }; class LocalPerfRecordWorker final : public RunWorker { Q_OBJECT public: LocalPerfRecordWorker(RunControl *runControl) : RunWorker(runControl) { setId("LocalPerfRecordWorker"); } void start() final { m_process = new Process(this); connect(m_process, &Process::started, this, &RunWorker::reportStarted); connect(m_process, &Process::done, this, [this] { // The terminate() below will frequently lead to QProcess::Crashed. We're not interested // in that. FailedToStart is the only actual failure. if (m_process->error() == QProcess::FailedToStart) { const QString msg = Tr::tr("Perf Process Failed to Start"); QMessageBox::warning(Core::ICore::dialogParent(), msg, Tr::tr("Make sure that you are running a recent Linux kernel " "and that the \"perf\" utility is available.")); reportFailure(msg); return; } if (!m_process->cleanedStdErr().isEmpty()) appendMessage(m_process->cleanedStdErr(), StdErrFormat); reportStopped(); }); const Store perfArgs = runControl()->settingsData(PerfProfiler::Constants::PerfSettingsId); const QString recordArgs = perfArgs[Constants::PerfRecordArgsId].toString(); CommandLine cmd({device()->filePath("perf"), {"record"}}); cmd.addArgs(recordArgs, CommandLine::Raw); cmd.addArgs({"-o", "-", "--"}); cmd.addCommandLineAsArgs(runControl()->commandLine(), CommandLine::Raw); m_process->setCommand(cmd); m_process->setWorkingDirectory(runControl()->workingDirectory()); appendMessage("Starting Perf: " + cmd.toUserOutput(), NormalMessageFormat); m_process->start(); } void stop() final { if (m_process) m_process->terminate(); } Process *recorder() { return m_process; } private: QPointer m_process; }; class PerfProfilerRunner final : public RunWorker { public: explicit PerfProfilerRunner(RunControl *runControl) : RunWorker(runControl) { setId("PerfProfilerRunner"); m_perfParserWorker = new PerfParserWorker(runControl); addStopDependency(m_perfParserWorker); // If the parser is gone, there is no point in going on. m_perfParserWorker->setEssential(true); if ((m_perfRecordWorker = runControl->createWorker("PerfRecorder"))) { m_perfParserWorker->addStartDependency(m_perfRecordWorker); addStartDependency(m_perfParserWorker); } else { m_perfRecordWorker = new LocalPerfRecordWorker(runControl); m_perfRecordWorker->addStartDependency(m_perfParserWorker); addStartDependency(m_perfRecordWorker); // In the local case, the parser won't automatically stop when the recorder does. So we need // to mark the recorder as essential, too. m_perfRecordWorker->setEssential(true); } m_perfParserWorker->addStopDependency(m_perfRecordWorker); PerfProfilerTool::instance()->onWorkerCreation(runControl); } void start() final { auto tool = PerfProfilerTool::instance(); connect(tool->stopAction(), &QAction::triggered, runControl(), &RunControl::initiateStop); connect(runControl(), &RunControl::started, PerfProfilerTool::instance(), &PerfProfilerTool::onRunControlStarted); connect(runControl(), &RunControl::stopped, PerfProfilerTool::instance(), &PerfProfilerTool::onRunControlFinished); PerfDataReader *reader = m_perfParserWorker->reader(); Process *perfProcess = nullptr; if (auto prw = qobject_cast(m_perfRecordWorker)) { // That's the local case. perfProcess = prw->recorder(); } else { perfProcess = runControl()->property("PerfProcess").value(); } if (perfProcess) { connect(perfProcess, &Process::readyReadStandardError, this, [this, perfProcess] { appendMessage(QString::fromLocal8Bit(perfProcess->readAllRawStandardError()), StdErrFormat); }); connect(perfProcess, &Process::readyReadStandardOutput, this, [this, reader, perfProcess] { if (!reader->feedParser(perfProcess->readAllRawStandardOutput())) reportFailure(Tr::tr("Failed to transfer Perf data to perfparser.")); }); } reportStarted(); } private: PerfParserWorker *m_perfParserWorker = nullptr; RunWorker *m_perfRecordWorker = nullptr; }; // PerfProfilerRunWorkerFactory class PerfProfilerRunWorkerFactory final : public RunWorkerFactory { public: PerfProfilerRunWorkerFactory() { setProduct(); addSupportedRunMode(ProjectExplorer::Constants::PERFPROFILER_RUN_MODE); } }; void setupPerfProfilerRunWorker() { static PerfProfilerRunWorkerFactory thePerfProfilerRunWorkerFactory; } } // PerfProfiler::Internal #include "perfprofilerruncontrol.moc"