88
99#include " perfrecord.h"
1010
11+ #include " recordhost.h"
12+
1113#include < QDebug>
1214#include < QDir>
1315#include < QFileInfo>
1921#include < csignal>
2022#include < unistd.h>
2123
22- #include < KUser>
23-
2424#include < kwindowsystem_version.h>
2525#if KWINDOWSYSTEM_VERSION >= QT_VERSION_CHECK(5, 101, 0)
2626#include < KX11Extras>
2727#else
2828#include < KWindowSystem>
2929#endif
3030
31- #include < hotspot-config.h>
32-
33- #include < fstream>
34- #include < sys/stat.h>
35-
3631namespace {
3732void createOutputFile (const QString& outputPath)
3833{
@@ -44,17 +39,11 @@ void createOutputFile(const QString& outputPath)
4439 QFile::rename (outputPath, bakPath);
4540 QFile (outputPath).open (QIODevice::WriteOnly);
4641}
47-
48- QString findPkexec ()
49- {
50- return QStandardPaths::findExecutable (QStringLiteral (" pkexec" ));
51- }
5242}
5343
54- PerfRecord::PerfRecord (QObject* parent)
44+ PerfRecord::PerfRecord (const RecordHost *host, QObject* parent)
5545 : QObject(parent)
56- , m_perfRecordProcess(nullptr )
57- , m_userTerminated(false )
46+ , m_host(host)
5847{
5948 connect (&m_perfControlFifo, &PerfControlFifoWrapper::started, this ,
6049 [this ]() { m_targetProcessForPrivilegedPerf.continueStoppedProcess (); });
@@ -72,38 +61,6 @@ PerfRecord::~PerfRecord()
7261 }
7362}
7463
75- static bool privsAlreadyElevated ()
76- {
77- auto readSysctl = [](const char * path) {
78- std::ifstream ifs {path};
79- int i = std::numeric_limits<int >::min ();
80- if (ifs) {
81- ifs >> i;
82- }
83- return i;
84- };
85-
86- bool isElevated = readSysctl (" /proc/sys/kernel/kptr_restrict" ) == 0 ;
87- if (!isElevated) {
88- return false ;
89- }
90-
91- isElevated = readSysctl (" /proc/sys/kernel/perf_event_paranoid" ) == -1 ;
92- if (!isElevated) {
93- return false ;
94- }
95-
96- auto checkPerms = [](const char * path) {
97- const mode_t required = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 755
98- struct stat buf;
99- return stat (path, &buf) == 0 && ((buf.st_mode & 07777 ) & required) == required;
100- };
101- static const auto paths = {" /sys/kernel/debug" , " /sys/kernel/debug/tracing" };
102- isElevated = std::all_of (paths.begin (), paths.end (), checkPerms);
103-
104- return isElevated;
105- }
106-
10764bool PerfRecord::runPerf (bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
10865 const QString& workingDirectory)
10966{
@@ -176,14 +133,14 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
176133 perfCommand += perfOptions;
177134
178135 if (elevatePrivileges) {
179- const auto pkexec = findPkexec ();
136+ const auto pkexec = m_host-> pkexecBinaryPath ();
180137 if (pkexec.isEmpty ()) {
181138 emit recordingFailed (tr (" The pkexec utility was not found, cannot elevate privileges." ));
182139 return false ;
183140 }
184141
185142 auto options = QStringList ();
186- options.append (perfBinaryPath ());
143+ options.append (m_host-> perfBinaryPath ());
187144 options += perfCommand;
188145
189146 if (!m_perfControlFifo.open ()) {
@@ -198,7 +155,7 @@ bool PerfRecord::runPerf(bool elevatePrivileges, const QStringList& perfOptions,
198155
199156 m_perfRecordProcess->start (pkexec, options);
200157 } else {
201- m_perfRecordProcess->start (perfBinaryPath (), perfCommand);
158+ m_perfRecordProcess->start (m_host-> perfBinaryPath (), perfCommand);
202159 }
203160
204161 return true ;
@@ -295,106 +252,13 @@ void PerfRecord::sendInput(const QByteArray& input)
295252 m_perfRecordProcess->write (input);
296253}
297254
298- QString PerfRecord::currentUsername ()
299- {
300- return KUser ().loginName ();
301- }
302-
303- bool PerfRecord::canTrace (const QString& path)
304- {
305- const auto info = QFileInfo (QLatin1String (" /sys/kernel/debug/tracing/" ) + path);
306- if (!info.isDir () || !info.isReadable ()) {
307- return false ;
308- }
309- QFile paranoid (QStringLiteral (" /proc/sys/kernel/perf_event_paranoid" ));
310- return paranoid.open (QIODevice::ReadOnly) && paranoid.readAll ().trimmed () == " -1" ;
311- }
312-
313- static QByteArray perfOutput (const QStringList& arguments)
314- {
315- QProcess process;
316-
317- auto reportError = [&]() {
318- qWarning () << " Failed to run perf" << process.arguments () << process.error () << process.errorString ()
319- << process.readAllStandardError ();
320- };
321-
322- QProcessEnvironment env = QProcessEnvironment::systemEnvironment ();
323- env.insert (QStringLiteral (" LANG" ), QStringLiteral (" C" ));
324- process.setProcessEnvironment (env);
325-
326- QObject::connect (&process, &QProcess::errorOccurred, &process, reportError);
327- process.start (PerfRecord::perfBinaryPath (), arguments);
328- if (!process.waitForFinished (1000 ) || process.exitCode () != 0 )
329- reportError ();
330- return process.readAllStandardOutput ();
331- }
332-
333- static QByteArray perfRecordHelp ()
334- {
335- static const QByteArray recordHelp = []() {
336- static QByteArray help = perfOutput ({QStringLiteral (" record" ), QStringLiteral (" --help" )});
337- if (help.isEmpty ()) {
338- // no man page installed, assume the best
339- help = " --sample-cpu --switch-events" ;
340- }
341- return help;
342- }();
343- return recordHelp;
344- }
345-
346- static QByteArray perfBuildOptions ()
347- {
348- static const QByteArray buildOptions = perfOutput ({QStringLiteral (" version" ), QStringLiteral (" --build-options" )});
349- return buildOptions;
350- }
351-
352- bool PerfRecord::canProfileOffCpu ()
353- {
354- return canTrace (QStringLiteral (" events/sched/sched_switch" ));
355- }
356-
357255QStringList PerfRecord::offCpuProfilingOptions ()
358256{
359257 return {QStringLiteral (" --switch-events" ), QStringLiteral (" --event" ), QStringLiteral (" sched:sched_switch" )};
360258}
361259
362- bool PerfRecord::canSampleCpu ()
363- {
364- return perfRecordHelp ().contains (" --sample-cpu" );
365- }
366-
367- bool PerfRecord::canSwitchEvents ()
368- {
369- return perfRecordHelp ().contains (" --switch-events" );
370- }
371-
372- bool PerfRecord::canUseAio ()
373- {
374- return perfBuildOptions ().contains (" aio: [ on ]" );
375- }
376-
377- bool PerfRecord::canCompress ()
378- {
379- return Zstd_FOUND && perfBuildOptions ().contains (" zstd: [ on ]" );
380- }
381-
382- bool PerfRecord::canElevatePrivileges ()
383- {
384- return !findPkexec ().isEmpty ();
385- }
386-
387- QString PerfRecord::perfBinaryPath ()
388- {
389- return QStandardPaths::findExecutable (QStringLiteral (" perf" ));
390- }
391-
392- bool PerfRecord::isPerfInstalled ()
393- {
394- return !perfBinaryPath ().isEmpty ();
395- }
396-
397- bool PerfRecord::actuallyElevatePrivileges (bool elevatePrivileges)
260+ bool PerfRecord::actuallyElevatePrivileges (bool elevatePrivileges) const
398261{
399- return elevatePrivileges && canElevatePrivileges () && geteuid () != 0 && !privsAlreadyElevated ();
262+ const auto capabilities = m_host->perfCapabilities ();
263+ return elevatePrivileges && capabilities.canElevatePrivileges && !capabilities.privilegesAlreadyElevated ;
400264}
0 commit comments