1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
/*
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "freebsdcpuplugin.h"
#include "loadaverages.h"
#include <algorithm>
#include <vector>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <KLocalizedString>
#include <systemstats/SensorContainer.h>
#include <systemstats/SysctlSensor.h>
namespace {
/** Reads a sysctl into a typed buffer, return success-value
*
* Returns @c false if the sysctl fails, otherwise @c true (even if
* the returned size is a mismatch or whatever).
*/
template <typename T>
bool readSysctl(const char *name, T *buffer, size_t size = sizeof(T)) {
return sysctlbyname(name, buffer, &size, nullptr, 0) != -1;
}
double deciKelvinToCelsius(const int deci_kelvin) {
return static_cast<double>(deci_kelvin) / 10.0 - 273.15;
}
/** Calls update() with sysctl cp_time data
*
* For a CPU object, or an AllCpus object, calls update() with the relevant data.
*/
template<typename cpu_t>
inline void updateCpu(cpu_t *cpu, long *cp_time)
{
cpu->update(cp_time[CP_SYS] + cp_time[CP_INTR], cp_time[CP_USER] + cp_time[CP_NICE], cp_time[CP_IDLE]);
}
/** Reads cp_times from sysctl and applies to the vector of CPU objects
*
* Assumes that the CPU objects are ordered in the vector in the same order
* that their data show up in the sysctl return value.
*/
inline void read_cp_times(QList<FreeBsdCpuObject *> &cpus)
{
unsigned int numCores = cpus.count();
std::vector<long> cp_times(numCores * CPUSTATES);
size_t cpTimesSize = sizeof(long) * cp_times.size();
if (readSysctl("kern.cp_times", cp_times.data(), cpTimesSize)) {//, &cpTimesSize, nullptr, 0) != -1) {
for (unsigned int i = 0; i < numCores; ++i) {
auto cpu = cpus[i];
updateCpu(cpu, &cp_times[CPUSTATES * i]);
}
}
}
}
FreeBsdCpuObject::FreeBsdCpuObject(int cpuNumber, const QString &name, KSysGuard::SensorContainer *parent)
: CpuObject(QStringLiteral("cpu%1").arg(cpuNumber), name, parent),
m_sysctlPrefix(QByteArrayLiteral("dev.cpu.") + QByteArray::number(cpuNumber))
{
}
void FreeBsdCpuObject::makeSensors()
{
BaseCpuObject::makeSensors();
m_frequency = new KSysGuard::SysctlSensor<int>(QStringLiteral("frequency"), m_sysctlPrefix + QByteArrayLiteral(".freq"), 0, this);
KSysGuard::SysctlSensor<int> *temp_sensor = new KSysGuard::SysctlSensor<int>(QStringLiteral("temperature"), m_sysctlPrefix + QByteArrayLiteral(".temperature"), 0, this);
// The temperature value is exported in deci-kelvin in the sysctl. See FreeBSD sbin/sysctl/sysctl.c for details.
temp_sensor->setConversionFunction([](int raw_value) { return deciKelvinToCelsius(raw_value); });
m_temperature = temp_sensor;
}
void FreeBsdCpuObject::initialize()
{
CpuObject::initialize();
// For min and max frequency we have to parse the values return by freq_levels because only
// minimum is exposed as a single value
size_t size;
const QByteArray levelsName = m_sysctlPrefix + QByteArrayLiteral(".freq_levels");
// calling sysctl with nullptr writes the needed size to size
if (sysctlbyname(levelsName, nullptr, &size, nullptr, 0) != -1) {
QByteArray freqLevels(size, Qt::Uninitialized);
if (sysctlbyname(levelsName, freqLevels.data(), &size, nullptr, 0) != -1) {
// The format is a list of pairs "frequency/power", see https://svnweb.freebsd.org/base/head/sys/kern/kern_cpu.c?revision=360464&view=markup#l1019
const QList<QByteArray> levels = freqLevels.split(' ');
int min = INT_MAX;
int max = 0;
for (const auto &level : levels) {
const int frequency = level.left(level.indexOf('/')).toInt();
min = std::min(frequency, min);
max = std::max(frequency, max);
}
// value are already in MHz see cpufreq(4)
m_frequency->setMin(min);
m_frequency->setMax(max);
}
}
const QByteArray tjmax = m_sysctlPrefix + QByteArrayLiteral(".coretemp.tjmax");
int maxTemperature;
// This is only availabel on Intel (using the coretemp driver)
if (readSysctl(tjmax.constData(), &maxTemperature)) {
m_temperature->setMax(deciKelvinToCelsius(maxTemperature));
}
}
void FreeBsdCpuObject::update(long system, long user, long idle)
{
// No wait usage on FreeBSD
m_usageComputer.setTicks(system, user, 0, idle);
m_system->setValue(m_usageComputer.systemUsage);
m_user->setValue(m_usageComputer.userUsage);
m_usage->setValue(m_usageComputer.totalUsage);
// The calculations above are "free" because we already have the data;
// is we are not subscribed, don't bother updating the data that needs
// extra work to be done.
if (!isSubscribed()) {
return;
}
m_temperature->update();
m_frequency->update();
}
void FreeBsdAllCpusObject::update(long system, long user, long idle)
{
// No wait usage on FreeBSD
m_usageComputer.setTicks(system, user, 0, idle);
m_system->setValue(m_usageComputer.systemUsage);
m_user->setValue(m_usageComputer.userUsage);
m_usage->setValue(m_usageComputer.totalUsage);
}
FreeBsdCpuPluginPrivate::FreeBsdCpuPluginPrivate(CpuPlugin* q)
: CpuPluginPrivate(q)
{
m_loadAverages = new LoadAverages(m_container);
// The values used here can be found in smp(4)
int numCpu;
readSysctl("hw.ncpu", &numCpu);
for (int i = 0; i < numCpu; ++i) {
auto cpu = new FreeBsdCpuObject(i, i18nc("@title", "CPU %1", i + 1), m_container);
cpu->initialize();
m_cpus.push_back(cpu);
}
m_allCpus = new FreeBsdAllCpusObject(m_container);
m_allCpus->initialize();
read_cp_times(m_cpus);
}
void FreeBsdCpuPluginPrivate::update()
{
m_loadAverages->update();
auto isSubscribed = [] (const KSysGuard::SensorObject *o) {return o->isSubscribed();};
if (std::none_of(m_cpus.cbegin(), m_cpus.cend(), isSubscribed) && !m_allCpus->isSubscribed()) {
return;
}
read_cp_times(m_cpus);
// update total values
long cp_time[CPUSTATES];
if (readSysctl("kern.cp_time", &cp_time)) {
updateCpu(m_allCpus, cp_time);
}
}
|