Skip to content

Commit 9ba5451

Browse files
committed
add queriering via ssh to RemoteHost
1 parent cfa314b commit 9ba5451

File tree

6 files changed

+136
-47
lines changed

6 files changed

+136
-47
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set(HOTSPOT_SRCS
5454
perfcontrolfifowrapper.cpp
5555
errnoutil.cpp
5656
recordhost.cpp
57+
remotedevice.cpp
5758
# ui files:
5859
mainwindow.ui
5960
aboutdialog.ui

src/recordhost.cpp

Lines changed: 117 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,35 @@ RecordHost::PerfCapabilities fetchLocalPerfCapabilities(const QString& perfPath)
140140

141141
return capabilities;
142142
}
143+
144+
RecordHost::PerfCapabilities fetchRemotePerfCapabilities(const RemoteDevice& device)
145+
{
146+
RecordHost::PerfCapabilities capabilities;
147+
148+
const auto buildOptions =
149+
device.getProgramOutput({QStringLiteral("perf"), QStringLiteral("version"), QStringLiteral("--build-options")});
150+
const auto help = device.getProgramOutput({QStringLiteral("perf"), QStringLiteral("--help")});
151+
152+
capabilities.canCompress = Zstd_FOUND && buildOptions.contains("zszd: [ on ]");
153+
capabilities.canSwitchEvents = help.contains("--switch-events");
154+
capabilities.canSampleCpu = help.contains("--sample-cpu");
155+
156+
// TODO: implement
157+
capabilities.canProfileOffCpu = false;
158+
capabilities.privilegesAlreadyElevated = false;
159+
160+
capabilities.canUseAio = false; // AIO doesn't work with perf streaming
161+
capabilities.canElevatePrivileges = false; // we currently don't support this
162+
163+
return capabilities;
164+
}
143165
}
144166

145167
RecordHost::RecordHost(QObject* parent)
146168
: QObject(parent)
147169
, m_checkPerfCapabilitiesJob(this)
148170
, m_checkPerfInstalledJob(this)
171+
, m_remoteDevice(this)
149172
{
150173
connect(this, &RecordHost::errorOccurred, this, [this](const QString& message) { m_error = message; });
151174

@@ -160,6 +183,10 @@ RecordHost::RecordHost(QObject* parent)
160183
connectIsReady(&RecordHost::pidsChanged);
161184
connectIsReady(&RecordHost::currentWorkingDirectoryChanged);
162185

186+
connect(&m_remoteDevice, &RemoteDevice::connected, this, &RecordHost::checkRequirements);
187+
188+
connect(&m_remoteDevice, &RemoteDevice::connected, this, [this] { emit isReadyChanged(isReady()); });
189+
163190
setHost(QStringLiteral("localhost"));
164191
}
165192

@@ -169,7 +196,13 @@ bool RecordHost::isReady() const
169196
{
170197
switch (m_recordType) {
171198
case RecordType::LaunchApplication:
172-
// client application is already validated in the setter
199+
// client application is already validated in the setter
200+
if (m_clientApplication.isEmpty() && m_cwd.isEmpty())
201+
return false;
202+
break;
203+
case RecordType::LaunchRemoteApplication:
204+
if (!m_remoteDevice.isConnected())
205+
return false;
173206
if (m_clientApplication.isEmpty() && m_cwd.isEmpty())
174207
return false;
175208
break;
@@ -216,35 +249,13 @@ void RecordHost::setHost(const QString& host)
216249
m_perfCapabilities = {};
217250
emit perfCapabilitiesChanged(m_perfCapabilities);
218251

219-
const auto perfPath = perfBinaryPath();
220-
m_checkPerfCapabilitiesJob.startJob([perfPath](auto&&) { return fetchLocalPerfCapabilities(perfPath); },
221-
[this](RecordHost::PerfCapabilities capabilities) {
222-
Q_ASSERT(QThread::currentThread() == thread());
223-
224-
m_perfCapabilities = capabilities;
225-
emit perfCapabilitiesChanged(m_perfCapabilities);
226-
});
227-
228-
m_checkPerfInstalledJob.startJob(
229-
[isLocal = isLocal(), perfPath](auto&&) {
230-
if (isLocal) {
231-
if (perfPath.isEmpty()) {
232-
return !QStandardPaths::findExecutable(QStringLiteral("perf")).isEmpty();
233-
}
234-
235-
return QFileInfo::exists(perfPath);
236-
}
237-
238-
qWarning() << "remote is not implemented";
239-
return false;
240-
},
241-
[this](bool isInstalled) {
242-
if (!isInstalled) {
243-
emit errorOccurred(tr("perf is not installed"));
244-
}
245-
m_isPerfInstalled = isInstalled;
246-
emit isPerfInstalledChanged(isInstalled);
247-
});
252+
m_remoteDevice.disconnect();
253+
if (isLocal()) {
254+
checkRequirements();
255+
} else {
256+
// checkRequirements will be called via RemoteDevice::connected
257+
m_remoteDevice.connectToDevice(m_host);
258+
}
248259
}
249260

250261
void RecordHost::setCurrentWorkingDirectory(const QString& cwd)
@@ -265,16 +276,25 @@ void RecordHost::setCurrentWorkingDirectory(const QString& cwd)
265276
m_cwd = cwd;
266277
emit currentWorkingDirectoryChanged(cwd);
267278
}
268-
return;
279+
} else {
280+
if (!m_remoteDevice.checkIfDirectoryExists(cwd)) {
281+
emit errorOccurred(tr("Working directory folder cannot be found: %1").arg(cwd));
282+
} else {
283+
emit errorOccurred({});
284+
m_cwd = cwd;
285+
emit currentWorkingDirectoryChanged(m_cwd);
286+
}
269287
}
270-
271-
qWarning() << "is not implemented for remote";
272288
}
273289

274290
void RecordHost::setClientApplication(const QString& clientApplication)
275291
{
276292
Q_ASSERT(QThread::currentThread() == thread());
277293

294+
if (m_clientApplication == clientApplication) {
295+
return;
296+
}
297+
278298
if (isLocal()) {
279299
QFileInfo application(KShell::tildeExpand(clientApplication));
280300
if (!application.exists()) {
@@ -296,10 +316,15 @@ void RecordHost::setClientApplication(const QString& clientApplication)
296316
if (m_cwd.isEmpty()) {
297317
setCurrentWorkingDirectory(application.dir().absolutePath());
298318
}
299-
return;
319+
} else {
320+
if (!m_remoteDevice.checkIfFileExists(clientApplication)) {
321+
emit errorOccurred(tr("Application file cannot be found: %1").arg(clientApplication));
322+
} else {
323+
emit errorOccurred({});
324+
m_clientApplication = clientApplication;
325+
emit clientApplicationChanged(m_clientApplication);
326+
}
300327
}
301-
302-
qWarning() << "is not implemented for remote";
303328
}
304329

305330
void RecordHost::setClientApplicationArguments(const QString& arguments)
@@ -314,11 +339,11 @@ void RecordHost::setClientApplicationArguments(const QString& arguments)
314339

315340
void RecordHost::setOutputFileName(const QString& filePath)
316341
{
317-
if (isLocal()) {
318-
const auto perfDataExtension = QStringLiteral(".data");
342+
const auto perfDataExtension = QStringLiteral(".data");
343+
const QFileInfo file(filePath);
344+
const QFileInfo folder(file.absolutePath());
319345

320-
const QFileInfo file(filePath);
321-
const QFileInfo folder(file.absolutePath());
346+
if (isLocal()) {
322347

323348
if (!folder.exists()) {
324349
emit errorOccurred(tr("Output file directory folder cannot be found: %1").arg(folder.path()));
@@ -333,11 +358,18 @@ void RecordHost::setOutputFileName(const QString& filePath)
333358
m_outputFileName = filePath;
334359
emit outputFileNameChanged(m_outputFileName);
335360
}
336-
337-
return;
361+
} else {
362+
// TODO: check if this really nessesary depending if the output will be saved on the device or host
363+
if (m_remoteDevice.checkIfDirectoryExists(folder.path())) {
364+
emit errorOccurred(tr("Output file directory folder cannot be found: %1").arg(folder.path()));
365+
} else if (!filePath.endsWith(perfDataExtension)) {
366+
emit errorOccurred(tr("Output file must end with %1").arg(perfDataExtension));
367+
} else {
368+
emit errorOccurred({});
369+
m_outputFileName = filePath;
370+
emit outputFileNameChanged(m_outputFileName);
371+
}
338372
}
339-
340-
qWarning() << "is not implemented for remote";
341373
}
342374

343375
void RecordHost::setRecordType(RecordType type)
@@ -379,3 +411,44 @@ QString RecordHost::perfBinaryPath() const
379411
}
380412
return {};
381413
}
414+
415+
void RecordHost::checkRequirements()
416+
{
417+
const auto perfPath = perfBinaryPath();
418+
m_checkPerfCapabilitiesJob.startJob(
419+
[isLocal = isLocal(), &remoteDevice = m_remoteDevice, perfPath](auto&&) {
420+
if (isLocal) {
421+
return fetchLocalPerfCapabilities(perfPath);
422+
} else {
423+
return fetchRemotePerfCapabilities(remoteDevice);
424+
}
425+
},
426+
[this](RecordHost::PerfCapabilities capabilities) {
427+
Q_ASSERT(QThread::currentThread() == thread());
428+
429+
m_perfCapabilities = capabilities;
430+
emit perfCapabilitiesChanged(m_perfCapabilities);
431+
});
432+
433+
m_checkPerfInstalledJob.startJob(
434+
[isLocal = isLocal(), &remoteDevice = m_remoteDevice, perfPath](auto&&) {
435+
if (isLocal) {
436+
if (perfPath.isEmpty()) {
437+
return !QStandardPaths::findExecutable(QStringLiteral("perf")).isEmpty();
438+
}
439+
440+
return QFileInfo::exists(perfPath);
441+
} else {
442+
return remoteDevice.checkIfProgramExists(QStringLiteral("perf"));
443+
}
444+
445+
return false;
446+
},
447+
[this](bool isInstalled) {
448+
if (!isInstalled) {
449+
emit errorOccurred(tr("perf is not installed"));
450+
}
451+
m_isPerfInstalled = isInstalled;
452+
emit isPerfInstalledChanged(isInstalled);
453+
});
454+
}

src/recordhost.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#pragma once
99

1010
#include "jobtracker.h"
11+
#include "remotedevice.h"
1112

1213
#include <QObject>
1314

@@ -118,6 +119,7 @@ class RecordHost : public QObject
118119
void pidsChanged();
119120

120121
private:
122+
void checkRequirements();
121123
bool isLocal() const;
122124

123125
QString m_host;
@@ -132,6 +134,7 @@ class RecordHost : public QObject
132134
RecordType m_recordType = RecordType::LaunchApplication;
133135
bool m_isPerfInstalled = false;
134136
QStringList m_pids;
137+
RemoteDevice m_remoteDevice;
135138
};
136139

137140
Q_DECLARE_METATYPE(RecordHost::PerfCapabilities)

src/recordpage.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,16 @@ RecordPage::RecordPage(QWidget* parent)
211211
auto settings = Settings::instance();
212212
connect(settings, &Settings::devicesChanged, this, [this](const QStringList& devices) {
213213
ui->deviceComboBox->clear();
214-
ui->deviceComboBox->insertItems(0, devices);
214+
ui->deviceComboBox->insertItem(0, QStringLiteral("localhost"));
215+
ui->deviceComboBox->insertItems(1, devices);
216+
ui->deviceComboBox->setCurrentIndex(0);
215217
});
218+
ui->deviceComboBox->insertItem(0, QStringLiteral("localhost"));
219+
ui->deviceComboBox->insertItems(1, settings->devices());
220+
ui->deviceComboBox->setCurrentIndex(0);
221+
222+
connect(ui->deviceComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
223+
[this] { m_recordHost->setHost(ui->deviceComboBox->currentText()); });
216224

217225
ui->compressionComboBox->addItem(tr("Disabled"), -1);
218226
ui->compressionComboBox->addItem(tr("Enabled (Default Level)"), 0);

src/settings.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,16 @@ void Settings::loadFromFile()
231231
sharedConfig->group("PerfPaths").writeEntry("lastUsed", envName);
232232
});
233233

234-
setSSHPath(sharedConfig->group("SSH").readEntry("ssh"));
235-
setSSHCopyKeyPath(sharedConfig->group("SSH").readEntry("ssh-copy-id"));
234+
const auto& ssh = sharedConfig->group("SSH");
235+
setSSHPath(ssh.readEntry("ssh"));
236+
setSSHCopyKeyPath(ssh.readEntry("ssh-copy-id"));
236237

237238
connect(this, &Settings::sshPathChanged, this,
238239
[sharedConfig](const QString& path) { sharedConfig->group("SSH").writeEntry("ssh", path); });
239240
connect(this, &Settings::sshCopyIdPathChanged, this,
240241
[sharedConfig](const QString& path) { sharedConfig->group("SSH").writeEntry("ssh-copy-id", path); });
242+
243+
setDevices(ssh.groupList());
241244
}
242245

243246
void Settings::setSourceCodePaths(const QString& paths)

tests/integrationtests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ ecm_add_test(
77
../../src/perfcontrolfifowrapper.cpp
88
../../src/perfrecord.cpp
99
../../src/recordhost.cpp
10+
../../src/remotedevice.cpp
1011
../../src/settings.cpp
1112
../../src/util.cpp
1213
../../src/errnoutil.cpp

0 commit comments

Comments
 (0)