Skip to content

Commit 9dada8c

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 04d876e commit 9dada8c

File tree

6 files changed

+97
-20
lines changed

6 files changed

+97
-20
lines changed

src/perfrecord.cpp

Lines changed: 69 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,61 @@ 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+
qDebug() << data;
175+
output->write(data);
176+
});
177+
connect(m_perfRecordProcess.data(), &QProcess::readyReadStandardError, m_perfRecordProcess,
178+
[this] { emit recordingOutput(QString::fromUtf8(m_perfRecordProcess->readAllStandardError())); });
179+
180+
connect(m_perfRecordProcess.data(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
181+
this, [this, output](int exitCode, QProcess::ExitStatus exitStatus) {
182+
Q_UNUSED(exitStatus)
183+
184+
output->close();
185+
output->deleteLater();
186+
187+
const auto outputFileInfo = QFileInfo(m_outputPath);
188+
qDebug() << exitCode << EXIT_SUCCESS << outputFileInfo.exists() << outputFileInfo.size();
189+
if ((exitCode == EXIT_SUCCESS || (exitCode == SIGTERM && m_userTerminated) || outputFileInfo.size() > 0)
190+
&& outputFileInfo.exists()) {
191+
if (exitCode != EXIT_SUCCESS && !m_userTerminated) {
192+
emit debuggeeCrashed();
193+
}
194+
emit recordingFinished(m_outputPath);
195+
} else {
196+
emit recordingFailed(tr("Failed to record perf data, error code %1.").arg(exitCode));
197+
}
198+
m_userTerminated = false;
199+
});
200+
201+
connect(m_perfRecordProcess.data(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
202+
Q_UNUSED(error)
203+
if (!m_userTerminated) {
204+
emit recordingFailed(m_perfRecordProcess->errorString());
205+
}
206+
});
207+
208+
connect(m_perfRecordProcess.data(), &QProcess::started, this,
209+
[this] { emit recordingStarted(m_perfRecordProcess->program(), m_perfRecordProcess->arguments()); });
210+
211+
m_perfRecordProcess->start();
212+
return true;
213+
}
214+
165215
void PerfRecord::record(const QStringList& perfOptions, const QString& outputPath, bool elevatePrivileges,
166216
const QStringList& pids)
167217
{

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.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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ void updateStartRecordingButtonState(const RecordHost* host, const std::unique_p
8282
return;
8383
}
8484

85+
// TODO: move stuff to RecordHost
8586
bool enabled = false;
8687
switch (selectedRecordType(ui)) {
8788
case RecordType::LaunchApplication:
8889
enabled = ui->applicationName->url().isValid();
8990
break;
91+
case RecordType::LaunchRemoteApplication:
92+
enabled = host->isReady();
93+
break;
9094
case RecordType::AttachToProcess:
9195
enabled = ui->processesTableView->selectionModel()->hasSelection();
9296
break;

src/remotedevice.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,13 @@ QProcess* RemoteDevice::sshProcess(const QStringList& args) const
186186

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

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)