Skip to content

Commit 34fae39

Browse files
committed
add perf query interface and untangle it from recordpage
moving the query part of the recordpage into its own class will make implementing remote recording much easier
1 parent 9375463 commit 34fae39

17 files changed

+811
-282
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ set(HOTSPOT_SRCS
5353
initiallystoppedprocess.cpp
5454
perfcontrolfifowrapper.cpp
5555
errnoutil.cpp
56+
recordhost.cpp
5657
# ui files:
5758
mainwindow.ui
5859
aboutdialog.ui
@@ -73,6 +74,7 @@ set(HOTSPOT_SRCS
7374
callgraphsettingspage.ui
7475
frequencypage.ui
7576
sourcepathsettings.ui
77+
perfsettingspage.ui
7678
# resources:
7779
resources.qrc
7880
)

src/jobtracker.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
SPDX-FileCopyrightText: Lieven Hey <[email protected]>
3+
SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
4+
5+
SPDX-License-Identifier: GPL-2.0-or-later
6+
*/
7+
8+
#pragma once
9+
10+
#include <ThreadWeaver/ThreadWeaver>
11+
#include <QObject>
12+
#include <QPointer>
13+
14+
class JobTracker
15+
{
16+
public:
17+
explicit JobTracker(QObject* context)
18+
: m_context(context)
19+
{
20+
}
21+
22+
bool isJobRunning() const
23+
{
24+
return m_context && m_isRunning;
25+
}
26+
27+
template<typename Job, typename SetData>
28+
void startJob(Job&& job, SetData&& setData)
29+
{
30+
using namespace ThreadWeaver;
31+
const auto jobId = ++m_currentJobId;
32+
auto jobCancelled = [context = m_context, jobId, currentJobId = &m_currentJobId]() {
33+
return !context || jobId != (*currentJobId);
34+
};
35+
auto maybeSetData = [jobCancelled, setData = std::forward<SetData>(setData),
36+
isRunning = &m_isRunning](auto&& results) {
37+
if (!jobCancelled()) {
38+
setData(std::forward<decltype(results)>(results));
39+
*isRunning = false;
40+
}
41+
};
42+
43+
m_isRunning = true;
44+
stream() << make_job([context = m_context, job = std::forward<Job>(job), maybeSetData = std::move(maybeSetData),
45+
jobCancelled = std::move(jobCancelled)]() mutable {
46+
auto results = job(jobCancelled);
47+
if (jobCancelled())
48+
return;
49+
50+
QMetaObject::invokeMethod(
51+
context.data(),
52+
[results = std::move(results), maybeSetData = std::move(maybeSetData)]() mutable {
53+
maybeSetData(std::move(results));
54+
},
55+
Qt::QueuedConnection);
56+
});
57+
}
58+
59+
private:
60+
QPointer<QObject> m_context;
61+
std::atomic<uint> m_currentJobId;
62+
bool m_isRunning = false;
63+
};

src/perfrecord.cpp

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -295,90 +295,11 @@ void PerfRecord::sendInput(const QByteArray& input)
295295
m_perfRecordProcess->write(input);
296296
}
297297

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-
357298
QStringList PerfRecord::offCpuProfilingOptions()
358299
{
359300
return {QStringLiteral("--switch-events"), QStringLiteral("--event"), QStringLiteral("sched:sched_switch")};
360301
}
361302

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-
382303
bool PerfRecord::canElevatePrivileges()
383304
{
384305
return !findPkexec().isEmpty();

src/perfrecord.h

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,8 @@ class PerfRecord : public QObject
3333
void stopRecording();
3434
void sendInput(const QByteArray& input);
3535

36-
static QString currentUsername();
37-
38-
static bool canTrace(const QString& path);
39-
static bool canProfileOffCpu();
40-
static bool canSampleCpu();
41-
static bool canSwitchEvents();
42-
static bool canUseAio();
43-
static bool canCompress();
44-
static bool canElevatePrivileges();
45-
4636
static QStringList offCpuProfilingOptions();
4737

48-
static QString perfBinaryPath();
49-
static bool isPerfInstalled();
50-
5138
signals:
5239
void recordingStarted(const QString& perfBinary, const QStringList& arguments);
5340
void recordingFinished(const QString& fileLocation);
@@ -63,6 +50,9 @@ class PerfRecord : public QObject
6350
bool m_userTerminated;
6451

6552
static bool actuallyElevatePrivileges(bool elevatePrivileges);
53+
static QString perfBinaryPath();
54+
static bool canElevatePrivileges();
55+
static bool isPerfInstalled();
6656

6757
bool runPerf(bool elevatePrivileges, const QStringList& perfOptions, const QString& outputPath,
6858
const QString& workingDirectory = QString());

src/perfsettingspage.ui

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>PerfSettingsPage</class>
4+
<widget class="QWidget" name="PerfSettingsPage">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>400</width>
10+
<height>300</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Form</string>
15+
</property>
16+
<layout class="QFormLayout" name="formLayout">
17+
<item row="1" column="0">
18+
<widget class="QLabel" name="label">
19+
<property name="text">
20+
<string>Perf Binary:</string>
21+
</property>
22+
</widget>
23+
</item>
24+
<item row="1" column="1">
25+
<widget class="KUrlRequester" name="perfPathEdit"/>
26+
</item>
27+
</layout>
28+
</widget>
29+
<customwidgets>
30+
<customwidget>
31+
<class>KUrlRequester</class>
32+
<extends>QWidget</extends>
33+
<header>kurlrequester.h</header>
34+
</customwidget>
35+
</customwidgets>
36+
<resources/>
37+
<connections/>
38+
</ui>

0 commit comments

Comments
 (0)