Skip to content

Commit 4a48461

Browse files
committed
add recording via ssh
this patch finally runs perf via ssh on a remote device perf is run with -o - to stream the recording to the host in this case stderr contains the output of the program run
1 parent f5e6378 commit 4a48461

9 files changed

+149
-30
lines changed

src/perfrecord.cpp

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,22 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
7272
m_perfRecordProcess->kill();
7373
m_perfRecordProcess->deleteLater();
7474
}
75-
m_perfRecordProcess = new QProcess(this);
76-
m_perfRecordProcess->setProcessChannelMode(QProcess::MergedChannels);
7775

78-
const auto outputFileInfo = QFileInfo(outputPath);
79-
const auto folderPath = outputFileInfo.dir().path();
80-
const auto folderInfo = QFileInfo(folderPath);
81-
if (!folderInfo.exists()) {
82-
emit recordingFailed(tr("Folder '%1' does not exist.").arg(folderPath));
83-
return false;
84-
}
85-
if (!folderInfo.isDir()) {
86-
emit recordingFailed(tr("'%1' is not a folder.").arg(folderPath));
87-
return false;
88-
}
89-
if (!folderInfo.isWritable()) {
90-
emit recordingFailed(tr("Folder '%1' is not writable.").arg(folderPath));
91-
return false;
76+
m_outputPath = outputPath;
77+
m_userTerminated = false;
78+
79+
if (m_host->isLocal()) {
80+
return runPerfLocal(elevatePrivileges, perfOptions, outputPath, workingDirectory);
81+
} else {
82+
return runPerfRemote(perfOptions, outputPath, workingDirectory);
9283
}
84+
}
85+
86+
bool PerfRecord::runPerfLocal(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
87+
const QString& workingDirectory)
88+
{
89+
m_perfRecordProcess = new QProcess(this);
90+
m_perfRecordProcess->setProcessChannelMode(QProcess::MergedChannels);
9391

9492
connect(m_perfRecordProcess.data(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
9593
this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
@@ -123,9 +121,6 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
123121
emit recordingOutput(output);
124122
});
125123

126-
m_outputPath = outputPath;
127-
m_userTerminated = false;
128-
129124
if (!workingDirectory.isEmpty()) {
130125
m_perfRecordProcess->setWorkingDirectory(workingDirectory);
131126
}
@@ -162,6 +157,59 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
162157
return true;
163158
}
164159

160+
bool PerfRecord::runPerfRemote(const QStringList& perfOptions, const QString& outputPath,
161+
const QString& workingDirectory)
162+
{
163+
m_perfRecordProcess = m_host->remoteDevice().runPerf(workingDirectory, perfOptions);
164+
165+
auto output = new QFile(outputPath, m_perfRecordProcess);
166+
if (!output->open(QIODevice::WriteOnly)) {
167+
emit recordingFailed(QStringLiteral("Failed to create output file: %1").arg(outputPath));
168+
return false;
169+
}
170+
171+
connect(m_perfRecordProcess.data(), &QProcess::readyReadStandardOutput, m_perfRecordProcess,
172+
[process = m_perfRecordProcess, output] {
173+
auto data = process->readAllStandardOutput();
174+
output->write(data);
175+
});
176+
connect(m_perfRecordProcess.data(), &QProcess::readyReadStandardError, m_perfRecordProcess,
177+
[this] { emit recordingOutput(QString::fromUtf8(m_perfRecordProcess->readAllStandardError())); });
178+
179+
connect(m_perfRecordProcess.data(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
180+
this, [this, output](int exitCode, QProcess::ExitStatus exitStatus) {
181+
Q_UNUSED(exitStatus)
182+
183+
output->close();
184+
output->deleteLater();
185+
186+
const auto outputFileInfo = QFileInfo(m_outputPath);
187+
if ((exitCode == EXIT_SUCCESS || (exitCode == SIGTERM && m_userTerminated) || outputFileInfo.size() > 0)
188+
&& outputFileInfo.exists()) {
189+
if (exitCode != EXIT_SUCCESS && !m_userTerminated) {
190+
emit debuggeeCrashed();
191+
}
192+
emit recordingFinished(m_outputPath);
193+
} else {
194+
emit recordingFailed(tr("Failed to record perf data, error code %1.").arg(exitCode));
195+
}
196+
m_userTerminated = false;
197+
});
198+
199+
connect(m_perfRecordProcess.data(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
200+
Q_UNUSED(error)
201+
if (!m_userTerminated) {
202+
emit recordingFailed(m_perfRecordProcess->errorString());
203+
}
204+
});
205+
206+
connect(m_perfRecordProcess.data(), &QProcess::started, this,
207+
[this] { emit recordingStarted(m_perfRecordProcess->program(), m_perfRecordProcess->arguments()); });
208+
209+
m_perfRecordProcess->start();
210+
return true;
211+
}
212+
165213
void PerfRecord::record(const QStringList& perfOptions, const QString& outputPath, bool elevatePrivileges,
166214
const QStringList& pids)
167215
{

src/perfrecord.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,10 @@ class PerfRecord : public QObject
5656
bool runPerf(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
5757
const QString& workingDirectory = QString());
5858

59+
bool runPerfLocal(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
60+
const QString& workingDirectory = QString());
61+
bool runPerfRemote(const QStringList& perfOptions, const QString& outputPath,
62+
const QString& workingDirectory = QString());
63+
5964
bool runRemotePerf(const QStringList& perfOptions, const QString& outputPath, const QString& workingDirectory = {});
6065
};

src/recordhost.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ RecordHost::RecordHost(QObject* parent)
184184
connectIsReady(&RecordHost::currentWorkingDirectoryChanged);
185185

186186
connect(&m_remoteDevice, &RemoteDevice::connected, this, &RecordHost::checkRequirements);
187-
188187
connect(&m_remoteDevice, &RemoteDevice::connected, this, [this] { emit isReadyChanged(isReady()); });
188+
connect(&m_remoteDevice, &RemoteDevice::failedToConnect, this,
189+
[this] { emit errorOccurred(tr("Failed to connect to: %1").arg(m_host)); });
189190

190191
setHost(QStringLiteral("localhost"));
191192
}
@@ -317,7 +318,9 @@ void RecordHost::setClientApplication(const QString& clientApplication)
317318
setCurrentWorkingDirectory(application.dir().absolutePath());
318319
}
319320
} else {
320-
if (!m_remoteDevice.checkIfFileExists(clientApplication)) {
321+
if (!m_remoteDevice.isConnected()) {
322+
emit errorOccurred(tr("Hotspot is not connected to the remote device"));
323+
} else if (!m_remoteDevice.checkIfFileExists(clientApplication)) {
321324
emit errorOccurred(tr("Application file cannot be found: %1").arg(clientApplication));
322325
} else {
323326
emit errorOccurred({});

src/recordhost.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ class RecordHost : public QObject
103103
// list of pids to record
104104
void setPids(const QStringList& pids);
105105

106+
bool isLocal() const;
107+
108+
const RemoteDevice& remoteDevice() const
109+
{
110+
return m_remoteDevice;
111+
}
112+
106113
signals:
107114
/// disallow "start" on recordpage until this is ready and that should only be the case when there's no error
108115
void isReadyChanged(bool isReady);
@@ -120,7 +127,6 @@ class RecordHost : public QObject
120127

121128
private:
122129
void checkRequirements();
123-
bool isLocal() const;
124130

125131
QString m_host;
126132
QString m_error;

src/recordpage.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "perfoutputwidgetkonsole.h"
4444
#include "perfoutputwidgettext.h"
4545
#include "perfrecord.h"
46+
#include "settings.h"
4647

4748
namespace {
4849
bool isIntel()
@@ -81,11 +82,15 @@ void updateStartRecordingButtonState(const RecordHost* host, const std::unique_p
8182
return;
8283
}
8384

85+
// TODO: move stuff to RecordHost
8486
bool enabled = false;
8587
switch (selectedRecordType(ui)) {
8688
case RecordType::LaunchApplication:
8789
enabled = ui->applicationName->url().isValid();
8890
break;
91+
case RecordType::LaunchRemoteApplication:
92+
enabled = host->isReady();
93+
break;
8994
case RecordType::AttachToProcess:
9095
enabled = ui->processesTableView->selectionModel()->hasSelection();
9196
break;
@@ -207,6 +212,20 @@ RecordPage::RecordPage(QWidget* parent)
207212
m_recordHost->setClientApplicationArguments(KShell::splitArgs(ui->applicationParametersBox->text()));
208213
});
209214

215+
auto settings = Settings::instance();
216+
connect(settings, &Settings::devicesChanged, this, [this](const QStringList& devices) {
217+
ui->deviceComboBox->clear();
218+
ui->deviceComboBox->insertItem(0, QStringLiteral("localhost"));
219+
ui->deviceComboBox->insertItems(1, devices);
220+
ui->deviceComboBox->setCurrentIndex(0);
221+
});
222+
ui->deviceComboBox->insertItem(0, QStringLiteral("localhost"));
223+
ui->deviceComboBox->insertItems(1, settings->devices());
224+
ui->deviceComboBox->setCurrentIndex(0);
225+
226+
connect(ui->deviceComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
227+
[this] { m_recordHost->setHost(ui->deviceComboBox->currentText()); });
228+
210229
ui->compressionComboBox->addItem(tr("Disabled"), -1);
211230
ui->compressionComboBox->addItem(tr("Enabled (Default Level)"), 0);
212231
ui->compressionComboBox->addItem(tr("Level 1 (Fastest)"), 1);
@@ -246,6 +265,7 @@ RecordPage::RecordPage(QWidget* parent)
246265
});
247266

248267
m_recordHost->setHost(QStringLiteral("localhost"));
268+
m_recordHost->setRecordType(RecordType::LaunchApplication);
249269

250270
ui->applicationName->comboBox()->setEditable(true);
251271
ui->applicationName->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly);
@@ -289,15 +309,18 @@ RecordPage::RecordPage(QWidget* parent)
289309

290310
ui->recordTypeComboBox->addItem(QIcon::fromTheme(QStringLiteral("run-build")), tr("Launch Application"),
291311
QVariant::fromValue(RecordType::LaunchApplication));
312+
ui->recordTypeComboBox->addItem(QIcon::fromTheme(QStringLiteral("run-build")), tr("Launch Remote Application"),
313+
QVariant::fromValue(RecordType::LaunchRemoteApplication));
292314
ui->recordTypeComboBox->addItem(QIcon::fromTheme(QStringLiteral("run-install")), tr("Attach To Process(es)"),
293315
QVariant::fromValue(RecordType::AttachToProcess));
294316
ui->recordTypeComboBox->addItem(QIcon::fromTheme(QStringLiteral("run-build-install-root")), tr("Profile System"),
295317
QVariant::fromValue(RecordType::ProfileSystem));
296-
connect(ui->recordTypeComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
297-
&RecordPage::updateRecordType);
318+
298319
connect(ui->recordTypeComboBox, qOverload<int>(&QComboBox::currentIndexChanged), m_recordHost,
299320
[this] { m_recordHost->setRecordType(ui->recordTypeComboBox->currentData().value<RecordType>()); });
300-
connect(m_recordHost, &RecordHost::clientApplicationChanged, this, &RecordPage::updateRecordType);
321+
322+
connect(m_recordHost, &RecordHost::recordTypeChanged, this, &RecordPage::updateRecordType);
323+
updateRecordType(RecordType::LaunchApplication);
301324

302325
{
303326
ui->callGraphComboBox->addItem(tr("None"), QVariant::fromValue(QString()));
@@ -503,7 +526,7 @@ void RecordPage::showRecordPage()
503526
{
504527
m_resultsFile.clear();
505528
setError({});
506-
updateRecordType();
529+
m_recordHost->setRecordType(RecordType::LaunchApplication);
507530
ui->viewPerfRecordResultsButton->setEnabled(false);
508531
}
509532

@@ -740,17 +763,22 @@ void RecordPage::setError(const QString& message)
740763
ui->applicationRecordErrorMessage->setVisible(!message.isEmpty());
741764
}
742765

743-
void RecordPage::updateRecordType()
766+
void RecordPage::updateRecordType(RecordType recordType)
744767
{
745768
setError({});
746769

747-
const auto recordType = selectedRecordType(ui);
748770
ui->launchAppBox->setVisible(recordType == RecordType::LaunchApplication);
749771
ui->attachAppBox->setVisible(recordType == RecordType::AttachToProcess);
750772

751773
m_perfOutput->setInputVisible(recordType == RecordType::LaunchApplication);
752774
m_perfOutput->clear();
753775

776+
if (recordType == RecordType::LaunchRemoteApplication) {
777+
ui->applicationName->clear();
778+
} else if (recordType == RecordType::LaunchApplication) {
779+
restoreCombobox(config(), QStringLiteral("applications"), ui->applicationName->comboBox());
780+
}
781+
754782
if (recordType == RecordType::AttachToProcess) {
755783
updateProcesses();
756784
}

src/recordpage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private slots:
5959

6060
private:
6161
void recordingStopped();
62-
void updateRecordType();
62+
void updateRecordType(RecordType type);
6363
void appendOutput(const QString& text);
6464
void setError(const QString& message);
6565

src/recordpage.ui

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,25 @@
4949
</item>
5050
</layout>
5151
</item>
52+
<item>
53+
<widget class="QGroupBox" name="remoteDeviceBox">
54+
<property name="title">
55+
<string>Remote Device</string>
56+
</property>
57+
<layout class="QFormLayout" name="formLayout_4">
58+
<item row="0" column="0">
59+
<widget class="QLabel" name="label">
60+
<property name="text">
61+
<string>Device:</string>
62+
</property>
63+
</widget>
64+
</item>
65+
<item row="0" column="1">
66+
<widget class="QComboBox" name="deviceComboBox"/>
67+
</item>
68+
</layout>
69+
</widget>
70+
</item>
5271
<item>
5372
<widget class="QGroupBox" name="launchAppBox">
5473
<property name="title">

src/remotedevice.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ void RemoteDevice::connectToDevice(const QString& device)
9696
} else {
9797
emit disconnected();
9898
}
99-
qDebug() << exitCode << connection->readAll();
10099
connection->deleteLater();
101100
});
102101

@@ -186,3 +185,12 @@ QProcess* RemoteDevice::sshProcess(const QStringList& args) const
186185

187186
return process;
188187
}
188+
189+
QProcess* RemoteDevice::runPerf(const QString& cwd, const QStringList& perfOptions) const
190+
{
191+
const auto perfCommand = QStringLiteral("perf record -o - %1 ").arg(perfOptions.join(QLatin1Char(' ')));
192+
const QString command = QStringLiteral("cd %1 ; %2").arg(cwd, perfCommand);
193+
auto process = sshProcess({QStringLiteral("sh"), QStringLiteral("-c"), QStringLiteral("\"%1\"").arg(command)});
194+
195+
return process;
196+
}

src/remotedevice.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class RemoteDevice : public QObject
3333
bool checkIfFileExists(const QString& file) const;
3434
QByteArray getProgramOutput(const QStringList& args) const;
3535

36+
QProcess* runPerf(const QString& cdw, const QStringList& perfOptions) const;
37+
3638
signals:
3739
void connected();
3840
void disconnected();

0 commit comments

Comments
 (0)