Skip to content

Commit

Permalink
add recording via ssh
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lievenhey committed Sep 18, 2023
1 parent 04d876e commit 9dada8c
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 20 deletions.
88 changes: 69 additions & 19 deletions src/perfrecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,22 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
m_perfRecordProcess->kill();
m_perfRecordProcess->deleteLater();
}
m_perfRecordProcess = new QProcess(this);
m_perfRecordProcess->setProcessChannelMode(QProcess::MergedChannels);

const auto outputFileInfo = QFileInfo(outputPath);
const auto folderPath = outputFileInfo.dir().path();
const auto folderInfo = QFileInfo(folderPath);
if (!folderInfo.exists()) {
emit recordingFailed(tr("Folder '%1' does not exist.").arg(folderPath));
return false;
}
if (!folderInfo.isDir()) {
emit recordingFailed(tr("'%1' is not a folder.").arg(folderPath));
return false;
}
if (!folderInfo.isWritable()) {
emit recordingFailed(tr("Folder '%1' is not writable.").arg(folderPath));
return false;
m_outputPath = outputPath;
m_userTerminated = false;

if (m_host->isLocal()) {
return runPerfLocal(elevatePrivileges, perfOptions, outputPath, workingDirectory);
} else {
return runPerfRemote(perfOptions, outputPath, workingDirectory);
}
}

bool PerfRecord::runPerfLocal(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
const QString& workingDirectory)
{
m_perfRecordProcess = new QProcess(this);
m_perfRecordProcess->setProcessChannelMode(QProcess::MergedChannels);

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

m_outputPath = outputPath;
m_userTerminated = false;

if (!workingDirectory.isEmpty()) {
m_perfRecordProcess->setWorkingDirectory(workingDirectory);
}
Expand Down Expand Up @@ -162,6 +157,61 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
return true;
}

bool PerfRecord::runPerfRemote(const QStringList& perfOptions, const QString& outputPath,
const QString& workingDirectory)
{
m_perfRecordProcess = m_host->remoteDevice().runPerf(workingDirectory, perfOptions);

auto output = new QFile(outputPath, m_perfRecordProcess);
if (!output->open(QIODevice::WriteOnly)) {
emit recordingFailed(QStringLiteral("Failed to create output file: %1").arg(outputPath));
return false;
}

connect(m_perfRecordProcess.data(), &QProcess::readyReadStandardOutput, m_perfRecordProcess,
[process = m_perfRecordProcess, output] {
auto data = process->readAllStandardOutput();
qDebug() << data;
output->write(data);
});
connect(m_perfRecordProcess.data(), &QProcess::readyReadStandardError, m_perfRecordProcess,
[this] { emit recordingOutput(QString::fromUtf8(m_perfRecordProcess->readAllStandardError())); });

connect(m_perfRecordProcess.data(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, [this, output](int exitCode, QProcess::ExitStatus exitStatus) {
Q_UNUSED(exitStatus)

output->close();
output->deleteLater();

const auto outputFileInfo = QFileInfo(m_outputPath);
qDebug() << exitCode << EXIT_SUCCESS << outputFileInfo.exists() << outputFileInfo.size();
if ((exitCode == EXIT_SUCCESS || (exitCode == SIGTERM && m_userTerminated) || outputFileInfo.size() > 0)
&& outputFileInfo.exists()) {
if (exitCode != EXIT_SUCCESS && !m_userTerminated) {
emit debuggeeCrashed();
}
emit recordingFinished(m_outputPath);
} else {
emit recordingFailed(tr("Failed to record perf data, error code %1.").arg(exitCode));
}
m_userTerminated = false;
});

connect(m_perfRecordProcess.data(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
Q_UNUSED(error)
if (!m_userTerminated) {
emit recordingFailed(m_perfRecordProcess->errorString());
}
});

connect(m_perfRecordProcess.data(), &QProcess::started, this,
[this] { emit recordingStarted(m_perfRecordProcess->program(), m_perfRecordProcess->arguments()); });

m_perfRecordProcess->start();
return true;
}

void PerfRecord::record(const QStringList& perfOptions, const QString& outputPath, bool elevatePrivileges,
const QStringList& pids)
{
Expand Down
5 changes: 5 additions & 0 deletions src/perfrecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ class PerfRecord : public QObject
bool runPerf(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
const QString& workingDirectory = QString());

bool runPerfLocal(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
const QString& workingDirectory = QString());
bool runPerfRemote(const QStringList& perfOptions, const QString& outputPath,
const QString& workingDirectory = QString());

bool runRemotePerf(const QStringList& perfOptions, const QString& outputPath, const QString& workingDirectory = {});
};
8 changes: 7 additions & 1 deletion src/recordhost.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ class RecordHost : public QObject
// list of pids to record
void setPids(const QStringList& pids);

bool isLocal() const;

const RemoteDevice& remoteDevice() const
{
return m_remoteDevice;
}

signals:
/// disallow "start" on recordpage until this is ready and that should only be the case when there's no error
void isReadyChanged(bool isReady);
Expand All @@ -120,7 +127,6 @@ class RecordHost : public QObject

private:
void checkRequirements();
bool isLocal() const;

QString m_host;
QString m_error;
Expand Down
4 changes: 4 additions & 0 deletions src/recordpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,15 @@ void updateStartRecordingButtonState(const RecordHost* host, const std::unique_p
return;
}

// TODO: move stuff to RecordHost
bool enabled = false;
switch (selectedRecordType(ui)) {
case RecordType::LaunchApplication:
enabled = ui->applicationName->url().isValid();
break;
case RecordType::LaunchRemoteApplication:
enabled = host->isReady();
break;
case RecordType::AttachToProcess:
enabled = ui->processesTableView->selectionModel()->hasSelection();
break;
Expand Down
10 changes: 10 additions & 0 deletions src/remotedevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,13 @@ QProcess* RemoteDevice::sshProcess(const QStringList& args) const

return process;
}

QProcess* RemoteDevice::runPerf(const QString& cwd, const QStringList& perfOptions) const
{
const auto perfCommand = QStringLiteral("perf record -o - %1 ").arg(perfOptions.join(QLatin1Char(' ')));
const QString command = QStringLiteral("cd %1 ; %2").arg(cwd, perfCommand);
qDebug() << command;
auto process = sshProcess({QStringLiteral("sh"), QStringLiteral("-c"), QStringLiteral("\"%1\"").arg(command)});

return process;
}
2 changes: 2 additions & 0 deletions src/remotedevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class RemoteDevice : public QObject
bool checkIfFileExists(const QString& file) const;
QByteArray getProgramOutput(const QStringList& args) const;

QProcess* runPerf(const QString& cdw, const QStringList& perfOptions) const;

signals:
void connected();
void disconnected();
Expand Down

0 comments on commit 9dada8c

Please sign in to comment.