diff --git a/src/perfrecord.cpp b/src/perfrecord.cpp index a40d4d41..5576d10a 100644 --- a/src/perfrecord.cpp +++ b/src/perfrecord.cpp @@ -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(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { @@ -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); } @@ -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(&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) { diff --git a/src/perfrecord.h b/src/perfrecord.h index 9f000691..7767c94f 100644 --- a/src/perfrecord.h +++ b/src/perfrecord.h @@ -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 = {}); }; diff --git a/src/recordhost.h b/src/recordhost.h index 81a6e022..a2bb4475 100644 --- a/src/recordhost.h +++ b/src/recordhost.h @@ -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); @@ -120,7 +127,6 @@ class RecordHost : public QObject private: void checkRequirements(); - bool isLocal() const; QString m_host; QString m_error; diff --git a/src/recordpage.cpp b/src/recordpage.cpp index 46eee770..c7bc01aa 100644 --- a/src/recordpage.cpp +++ b/src/recordpage.cpp @@ -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; @@ -205,7 +209,7 @@ RecordPage::RecordPage(QWidget* parent) connect(ui->applicationParametersBox, &QLineEdit::editingFinished, this, [this] { ui->multiConfig->saveCurrentConfig(); - m_recordHost->setClientApplicationArguments(KShell::splitArgs(ui->applicationParametersBox->text())); + m_recordHost->setClientApplicationArguments(KShell::splitArgs(ui->applicationParametersBox->text()); }); auto settings = Settings::instance(); diff --git a/src/remotedevice.cpp b/src/remotedevice.cpp index 6ec7b496..c28cd7f7 100644 --- a/src/remotedevice.cpp +++ b/src/remotedevice.cpp @@ -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; +} diff --git a/src/remotedevice.h b/src/remotedevice.h index 9c9de552..b91b2ed5 100644 --- a/src/remotedevice.h +++ b/src/remotedevice.h @@ -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();