Files
qt-creator/src/plugins/perfprofiler/perfprofilerruncontrol.cpp

206 lines
7.0 KiB
C++
Raw Normal View History

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "perfprofilerruncontrol.h"
#include "perfdatareader.h"
#include "perfprofilertool.h"
#include "perfrunconfigurationaspect.h"
#include "perfsettings.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/target.h>
#include <utils/qtcprocess.h>
#include <QAction>
#include <QMessageBox>
#include <QTcpServer>
using namespace ProjectExplorer;
using namespace Utils;
namespace PerfProfiler {
namespace Internal {
class PerfParserWorker : public RunWorker
{
Q_OBJECT
public:
PerfParserWorker(RunControl *runControl)
: RunWorker(runControl)
{
setId("PerfParser");
auto tool = PerfProfilerTool::instance();
m_reader.setTraceManager(tool->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() override
{
Avoid some visible uses of RunControl::runConfiguration() For a long time, probably from the very beginning, a RunControl was meant to hold (a copy of) data needed for its operation, that was valid at the time of its construction, to be resilient in cases where RunConfiguration setting were changed while the RunControl was running, or to properly re-run with the original settings. Unfortunately, the task was repetitive, as RunConfiguration classes had no generic access to properties / "aspects" and there was was the runConfiguration() accessor (probably for mostly unrelated reasons in the output pane handling) which made the idea of just casting that to the original runConfiguration and access the data directly there appealing, with all the expected consequences. This patch here partially addresses the issue by copying some more of the related data at RunControl construction time and adjust the using code, avoiding most uses of the runConfiguration() accessor in a mostly mechanical matter. Complete removal appears possible, but will be less mechanical in "difficult" plugins like ios, so this is left for later. The new accessors in RunControl are very much ad-hoc, leaving room for improvement, e.g. by consolidating the access to the run config settings aspects with the other runconfig aspects or similar. For now the goal is to remove the runConfiguration() accessor, and to as much as possible fixed data after RunControl setup is finished. Next step would be to officially allow construction of RunControls without a specific RunConfiguration by setting the necessary data independently, removing the need for the various workarounds that are currently used for the purpose of faking (parts of) the effect of the non-existing RunConfiguration or refusing to operate at all, even if it would be possible. Change-Id: If8e5596da8422c70e90f97270389adbe6d0b46f2 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2019-03-11 15:42:43 +01:00
QStringList args = m_reader.findTargetArguments(runControl());
QUrl url = runControl()->property("PerfConnection").toUrl();
if (url.isValid()) {
args.append(QStringList{"--host", url.host(), "--port", QString::number(url.port())});
}
appendMessage("PerfParser args: " + args.join(' '), Utils::NormalMessageFormat);
m_reader.createParser(args);
m_reader.startParser();
}
void stop() override
{
m_reader.stopParser();
}
PerfDataReader *reader() { return &m_reader;}
private:
PerfDataReader m_reader;
};
class LocalPerfRecordWorker : public RunWorker
{
Q_OBJECT
public:
LocalPerfRecordWorker(RunControl *runControl)
: RunWorker(runControl)
{
setId("LocalPerfRecordWorker");
auto perfAspect = runControl->aspect<PerfRunConfigurationAspect>();
QTC_ASSERT(perfAspect, return);
PerfSettings *settings = static_cast<PerfSettings *>(perfAspect->currentSettings);
QTC_ASSERT(settings, return);
m_perfRecordArguments = settings->perfRecordArguments();
}
void start() override
{
m_process = new QtcProcess(this);
connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted);
connect(m_process, &QtcProcess::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;
}
reportStopped();
});
CommandLine cmd({device()->filePath("perf"), {"record"}});
cmd.addArgs(m_perfRecordArguments);
cmd.addArgs({"-o", "-", "--"});
cmd.addCommandLineAsArgs(runControl()->commandLine(), CommandLine::Raw);
m_process->setCommand(cmd);
m_process->start();
}
void stop() override
{
if (m_process)
m_process->terminate();
}
QtcProcess *recorder() { return m_process; }
private:
QPointer<QtcProcess> m_process;
QStringList m_perfRecordArguments;
};
PerfProfilerRunner::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);
ProjectExplorer: Standardize RunWorker creation logic This unifies the remaining paths of RunWorker creation to always use RunWorkerFactories in the plugin pimpls. There were, and are, still effectively three basic kinds of workers: - "toplevel" tools corresponding to the run modes, that are often all that's used for local runs and directly started via the fat buttons or e.g. entries in the analyze menu, with factories already previously located in the plugin pimpls - core "tool helpers", providing tool specific functionality typically used in conjunction with a remote device specific run mechanism, set up via RunControl::registerWorkerCreator - target/device specific runhelper like port gatherers contructed e.g. via *Device::workerCreator(Core::Id id) Worse, these categories are partially overlapping, so it was not clear how a "clean" setup would look like, instead some ad-hoc cobbling "to make it work" happened. In some cases, the runMode id was used throughout the whole ensemble of run workers for a given run, and which worker exactly was created depended on which of the mechanism above was used in which order. With the new central setup, the top-level runmodes remain, but the second kind gets new ids, so the implicit dependencies on order of setup mechanism are avoided. This also helps in the cases where there was previously unclarity of where and how to set up worker factories: It's always and only the plugin pimpl now. Change-Id: Icd9a08e2d53e19abe8b21fe546f469fae353a69f Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-08-23 15:31:35 +02:00
if ((m_perfRecordWorker = runControl->createWorker("PerfRecorder"))) {
m_perfParserWorker->addStartDependency(m_perfRecordWorker);
addStartDependency(m_perfParserWorker);
ProjectExplorer: Standardize RunWorker creation logic This unifies the remaining paths of RunWorker creation to always use RunWorkerFactories in the plugin pimpls. There were, and are, still effectively three basic kinds of workers: - "toplevel" tools corresponding to the run modes, that are often all that's used for local runs and directly started via the fat buttons or e.g. entries in the analyze menu, with factories already previously located in the plugin pimpls - core "tool helpers", providing tool specific functionality typically used in conjunction with a remote device specific run mechanism, set up via RunControl::registerWorkerCreator - target/device specific runhelper like port gatherers contructed e.g. via *Device::workerCreator(Core::Id id) Worse, these categories are partially overlapping, so it was not clear how a "clean" setup would look like, instead some ad-hoc cobbling "to make it work" happened. In some cases, the runMode id was used throughout the whole ensemble of run workers for a given run, and which worker exactly was created depended on which of the mechanism above was used in which order. With the new central setup, the top-level runmodes remain, but the second kind gets new ids, so the implicit dependencies on order of setup mechanism are avoided. This also helps in the cases where there was previously unclarity of where and how to set up worker factories: It's always and only the plugin pimpl now. Change-Id: Icd9a08e2d53e19abe8b21fe546f469fae353a69f Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-08-23 15:31:35 +02:00
} 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 PerfProfilerRunner::start()
{
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);
connect(runControl(), &RunControl::finished, PerfProfilerTool::instance(),
&PerfProfilerTool::onRunControlFinished);
PerfDataReader *reader = m_perfParserWorker->reader();
if (auto prw = qobject_cast<LocalPerfRecordWorker *>(m_perfRecordWorker)) {
// That's the local case.
QtcProcess *recorder = prw->recorder();
connect(recorder, &QtcProcess::readyReadStandardError, this, [this, recorder] {
appendMessage(QString::fromLocal8Bit(recorder->readAllStandardError()),
Utils::StdErrFormat);
});
connect(recorder, &QtcProcess::readyReadStandardOutput, this, [this, reader, recorder] {
if (!reader->feedParser(recorder->readAllStandardOutput()))
reportFailure(Tr::tr("Failed to transfer Perf data to perfparser."));
});
}
reportStarted();
}
} // namespace Internal
} // namespace PerfProfiler
#include "perfprofilerruncontrol.moc"