@@ -140,12 +140,35 @@ RecordHost::PerfCapabilities fetchLocalPerfCapabilities(const QString& perfPath)
140
140
141
141
return capabilities;
142
142
}
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
+ }
143
165
}
144
166
145
167
RecordHost::RecordHost (QObject* parent)
146
168
: QObject(parent)
147
169
, m_checkPerfCapabilitiesJob(this )
148
170
, m_checkPerfInstalledJob(this )
171
+ , m_remoteDevice(this )
149
172
{
150
173
connect (this , &RecordHost::errorOccurred, this , [this ](const QString& message) { m_error = message; });
151
174
@@ -160,6 +183,10 @@ RecordHost::RecordHost(QObject* parent)
160
183
connectIsReady (&RecordHost::pidsChanged);
161
184
connectIsReady (&RecordHost::currentWorkingDirectoryChanged);
162
185
186
+ connect (&m_remoteDevice, &RemoteDevice::connected, this , &RecordHost::checkRequirements);
187
+
188
+ connect (&m_remoteDevice, &RemoteDevice::connected, this , [this ] { emit isReadyChanged (isReady ()); });
189
+
163
190
setHost (QStringLiteral (" localhost" ));
164
191
}
165
192
@@ -169,7 +196,13 @@ bool RecordHost::isReady() const
169
196
{
170
197
switch (m_recordType) {
171
198
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 ;
173
206
if (m_clientApplication.isEmpty () && m_cwd.isEmpty ())
174
207
return false ;
175
208
break ;
@@ -210,38 +243,19 @@ void RecordHost::setHost(const QString& host)
210
243
m_clientApplication.clear ();
211
244
emit clientApplicationChanged (m_clientApplication);
212
245
246
+ m_clientApplicationArguments.clear ();
247
+ emit clientApplicationArgumentsChanged (m_clientApplicationArguments);
248
+
213
249
m_perfCapabilities = {};
214
250
emit perfCapabilitiesChanged (m_perfCapabilities);
215
251
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
+ }
245
259
}
246
260
247
261
void RecordHost::setCurrentWorkingDirectory (const QString& cwd)
@@ -262,16 +276,25 @@ void RecordHost::setCurrentWorkingDirectory(const QString& cwd)
262
276
m_cwd = cwd;
263
277
emit currentWorkingDirectoryChanged (cwd);
264
278
}
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
+ }
266
287
}
267
-
268
- qWarning () << " is not implemented for remote" ;
269
288
}
270
289
271
290
void RecordHost::setClientApplication (const QString& clientApplication)
272
291
{
273
292
Q_ASSERT (QThread::currentThread () == thread ());
274
293
294
+ if (m_clientApplication == clientApplication) {
295
+ return ;
296
+ }
297
+
275
298
if (isLocal ()) {
276
299
QFileInfo application (KShell::tildeExpand (clientApplication));
277
300
if (!application.exists ()) {
@@ -293,38 +316,48 @@ void RecordHost::setClientApplication(const QString& clientApplication)
293
316
if (m_cwd.isEmpty ()) {
294
317
setCurrentWorkingDirectory (application.dir ().absolutePath ());
295
318
}
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
+ }
297
327
}
298
-
299
- qWarning () << " is not implemented for remote" ;
300
328
}
301
329
302
- void RecordHost::setOutputFileName (const QString& filePath )
330
+ void RecordHost::setClientApplicationArguments (const QString& arguments )
303
331
{
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 ());
323
333
324
- return ;
334
+ if (m_clientApplicationArguments != arguments) {
335
+ m_clientApplicationArguments = arguments;
336
+ emit clientApplicationArgumentsChanged (m_clientApplicationArguments);
325
337
}
338
+ }
326
339
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
+ }
328
361
}
329
362
330
363
void RecordHost::setRecordType (RecordType type)
@@ -366,3 +399,44 @@ QString RecordHost::perfBinaryPath() const
366
399
}
367
400
return {};
368
401
}
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