@@ -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
145167RecordHost::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 ;
@@ -210,38 +243,19 @@ void RecordHost::setHost(const QString& host)
210243 m_clientApplication.clear ();
211244 emit clientApplicationChanged (m_clientApplication);
212245
246+ m_clientApplicationArguments.clear ();
247+ emit clientApplicationArgumentsChanged (m_clientApplicationArguments);
248+
213249 m_perfCapabilities = {};
214250 emit perfCapabilitiesChanged (m_perfCapabilities);
215251
216- const auto perfPath = perfBinaryPath ();
217- m_checkPerfCapabilitiesJob.startJob ([perfPath](auto &&) { return fetchLocalPerfCapabilities (perfPath); },
218- [this ](RecordHost::PerfCapabilities capabilities) {
219- Q_ASSERT (QThread::currentThread () == thread ());
220-
221- m_perfCapabilities = capabilities;
222- emit perfCapabilitiesChanged (m_perfCapabilities);
223- });
224-
225- m_checkPerfInstalledJob.startJob (
226- [isLocal = isLocal (), perfPath](auto &&) {
227- if (isLocal) {
228- if (perfPath.isEmpty ()) {
229- return !QStandardPaths::findExecutable (QStringLiteral (" perf" )).isEmpty ();
230- }
231-
232- return QFileInfo::exists (perfPath);
233- }
234-
235- qWarning () << " remote is not implemented" ;
236- return false ;
237- },
238- [this ](bool isInstalled) {
239- if (!isInstalled) {
240- emit errorOccurred (tr (" perf is not installed" ));
241- }
242- m_isPerfInstalled = isInstalled;
243- emit isPerfInstalledChanged (isInstalled);
244- });
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+ }
245259}
246260
247261void RecordHost::setCurrentWorkingDirectory (const QString& cwd)
@@ -262,16 +276,25 @@ void RecordHost::setCurrentWorkingDirectory(const QString& cwd)
262276 m_cwd = cwd;
263277 emit currentWorkingDirectoryChanged (cwd);
264278 }
265- 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+ }
266287 }
267-
268- qWarning () << " is not implemented for remote" ;
269288}
270289
271290void RecordHost::setClientApplication (const QString& clientApplication)
272291{
273292 Q_ASSERT (QThread::currentThread () == thread ());
274293
294+ if (m_clientApplication == clientApplication) {
295+ return ;
296+ }
297+
275298 if (isLocal ()) {
276299 QFileInfo application (KShell::tildeExpand (clientApplication));
277300 if (!application.exists ()) {
@@ -293,38 +316,48 @@ void RecordHost::setClientApplication(const QString& clientApplication)
293316 if (m_cwd.isEmpty ()) {
294317 setCurrentWorkingDirectory (application.dir ().absolutePath ());
295318 }
296- 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+ }
297327 }
298-
299- qWarning () << " is not implemented for remote" ;
300328}
301329
302- void RecordHost::setOutputFileName (const QString& filePath )
330+ void RecordHost::setClientApplicationArguments (const QString& arguments )
303331{
304- if (isLocal ()) {
305- const auto perfDataExtension = QStringLiteral (" .data" );
306-
307- const QFileInfo file (filePath);
308- const QFileInfo folder (file.absolutePath ());
309-
310- if (!folder.exists ()) {
311- emit errorOccurred (tr (" Output file directory folder cannot be found: %1" ).arg (folder.path ()));
312- } else if (!folder.isDir ()) {
313- emit errorOccurred (tr (" Output file directory folder is not valid: %1" ).arg (folder.path ()));
314- } else if (!folder.isWritable ()) {
315- emit errorOccurred (tr (" Output file directory folder is not writable: %1" ).arg (folder.path ()));
316- } else if (!file.absoluteFilePath ().endsWith (perfDataExtension)) {
317- emit errorOccurred (tr (" Output file must end with %1" ).arg (perfDataExtension));
318- } else {
319- emit errorOccurred ({});
320- m_outputFileName = filePath;
321- emit outputFileNameChanged (m_outputFileName);
322- }
332+ Q_ASSERT (QThread::currentThread () == thread ());
323333
324- return ;
334+ if (m_clientApplicationArguments != arguments) {
335+ m_clientApplicationArguments = arguments;
336+ emit clientApplicationArgumentsChanged (m_clientApplicationArguments);
325337 }
338+ }
326339
327- qWarning () << " is not implemented for remote" ;
340+ void RecordHost::setOutputFileName (const QString& filePath)
341+ {
342+ const auto perfDataExtension = QStringLiteral (" .data" );
343+ const QFileInfo file (filePath);
344+ const QFileInfo folder (file.absolutePath ());
345+
346+ // the recording data is streamed from the device (currently) so there is no need to use different logic for local
347+ // vs remote
348+ if (!folder.exists ()) {
349+ emit errorOccurred (tr (" Output file directory folder cannot be found: %1" ).arg (folder.path ()));
350+ } else if (!folder.isDir ()) {
351+ emit errorOccurred (tr (" Output file directory folder is not valid: %1" ).arg (folder.path ()));
352+ } else if (!folder.isWritable ()) {
353+ emit errorOccurred (tr (" Output file directory folder is not writable: %1" ).arg (folder.path ()));
354+ } else if (!file.absoluteFilePath ().endsWith (perfDataExtension)) {
355+ emit errorOccurred (tr (" Output file must end with %1" ).arg (perfDataExtension));
356+ } else {
357+ emit errorOccurred ({});
358+ m_outputFileName = filePath;
359+ emit outputFileNameChanged (m_outputFileName);
360+ }
328361}
329362
330363void RecordHost::setRecordType (RecordType type)
@@ -366,3 +399,44 @@ QString RecordHost::perfBinaryPath() const
366399 }
367400 return {};
368401}
402+
403+ void RecordHost::checkRequirements ()
404+ {
405+ const auto perfPath = perfBinaryPath ();
406+ m_checkPerfCapabilitiesJob.startJob (
407+ [isLocal = isLocal (), &remoteDevice = m_remoteDevice, perfPath](auto &&) {
408+ if (isLocal) {
409+ return fetchLocalPerfCapabilities (perfPath);
410+ } else {
411+ return fetchRemotePerfCapabilities (remoteDevice);
412+ }
413+ },
414+ [this ](RecordHost::PerfCapabilities capabilities) {
415+ Q_ASSERT (QThread::currentThread () == thread ());
416+
417+ m_perfCapabilities = capabilities;
418+ emit perfCapabilitiesChanged (m_perfCapabilities);
419+ });
420+
421+ m_checkPerfInstalledJob.startJob (
422+ [isLocal = isLocal (), &remoteDevice = m_remoteDevice, perfPath](auto &&) {
423+ if (isLocal) {
424+ if (perfPath.isEmpty ()) {
425+ return !QStandardPaths::findExecutable (QStringLiteral (" perf" )).isEmpty ();
426+ }
427+
428+ return QFileInfo::exists (perfPath);
429+ } else {
430+ return remoteDevice.checkIfProgramExists (QStringLiteral (" perf" ));
431+ }
432+
433+ return false ;
434+ },
435+ [this ](bool isInstalled) {
436+ if (!isInstalled) {
437+ emit errorOccurred (tr (" perf is not installed" ));
438+ }
439+ m_isPerfInstalled = isInstalled;
440+ emit isPerfInstalledChanged (isInstalled);
441+ });
442+ }
0 commit comments