File indexing completed on 2024-05-12 17:00:14

0001 /*
0002     SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "freebsdcpuplugin.h"
0008 
0009 #include "loadaverages.h"
0010 
0011 #include <algorithm>
0012 #include <vector>
0013 
0014 #include <sys/types.h>
0015 #include <sys/resource.h>
0016 #include <sys/sysctl.h>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <systemstats/SensorContainer.h>
0021 #include <systemstats/SysctlSensor.h>
0022 
0023 namespace {
0024 
0025 /** Reads a sysctl into a typed buffer, return success-value
0026  *
0027  * Returns @c false if the sysctl fails, otherwise @c true (even if
0028  * the returned size is a mismatch or whatever).
0029  */
0030 template <typename T>
0031 bool readSysctl(const char *name, T *buffer, size_t size = sizeof(T)) {
0032     return sysctlbyname(name, buffer, &size, nullptr, 0) != -1;
0033 }
0034 
0035 /** Calls update() with sysctl cp_time data
0036  *
0037  * For a CPU object, or an AllCpus object, calls update() with the relevant data.
0038  */
0039 template<typename cpu_t>
0040 inline void updateCpu(cpu_t *cpu, long *cp_time)
0041 {
0042     cpu->update(cp_time[CP_SYS] + cp_time[CP_INTR], cp_time[CP_USER] + cp_time[CP_NICE], cp_time[CP_IDLE]);
0043 }
0044 
0045 /** Reads cp_times from sysctl and applies to the vector of CPU objects
0046  *
0047  * Assumes that the CPU objects are ordered in the vector in the same order
0048  * that their data show up in the sysctl return value.
0049  */
0050 inline void read_cp_times(QVector<FreeBsdCpuObject*> &cpus)
0051 {
0052     unsigned int numCores = cpus.count();
0053     std::vector<long> cp_times(numCores * CPUSTATES);
0054     size_t cpTimesSize = sizeof(long) *  cp_times.size();
0055     if (readSysctl("kern.cp_times", cp_times.data(), cpTimesSize)) {//, &cpTimesSize, nullptr, 0) != -1) {
0056         for (unsigned int  i = 0; i < numCores; ++i) {
0057             auto cpu = cpus[i];
0058             updateCpu(cpu, &cp_times[CPUSTATES * i]);
0059         }
0060     }
0061 }
0062 
0063 }
0064 
0065 FreeBsdCpuObject::FreeBsdCpuObject(int cpuNumber, const QString &name, KSysGuard::SensorContainer *parent)
0066     : CpuObject(QStringLiteral("cpu%1").arg(cpuNumber), name, parent),
0067     m_sysctlPrefix(QByteArrayLiteral("dev.cpu.") + QByteArray::number(cpuNumber))
0068 {
0069 }
0070 
0071 void FreeBsdCpuObject::makeSensors()
0072 {
0073     BaseCpuObject::makeSensors();
0074 
0075     m_frequency = new KSysGuard::SysctlSensor<int>(QStringLiteral("frequency"), m_sysctlPrefix + QByteArrayLiteral(".freq"), 0, this);
0076     m_temperature = new KSysGuard::SysctlSensor<int>(QStringLiteral("temperature"), m_sysctlPrefix + QByteArrayLiteral(".temperature"), 0, this);
0077 }
0078 
0079 void FreeBsdCpuObject::initialize()
0080 {
0081     CpuObject::initialize();
0082 
0083     // For min and max frequency we have to parse the values return by freq_levels because only
0084     // minimum is exposed as a single value
0085     size_t size;
0086     const QByteArray levelsName = m_sysctlPrefix + QByteArrayLiteral(".freq_levels");
0087     // calling sysctl with nullptr writes the needed size to size
0088     if (sysctlbyname(levelsName, nullptr, &size, nullptr, 0) != -1) {
0089         QByteArray freqLevels(size, Qt::Uninitialized);
0090         if (sysctlbyname(levelsName, freqLevels.data(), &size, nullptr, 0) != -1) {
0091             // 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
0092             const QList<QByteArray> levels = freqLevels.split(' ');
0093             int min = INT_MAX;
0094             int max = 0;
0095             for (const auto &level : levels) {
0096                 const int frequency = level.left(level.indexOf('/')).toInt();
0097                 min = std::min(frequency, min);
0098                 max = std::max(frequency, max);
0099             }
0100             // value are already in MHz  see cpufreq(4)
0101             m_frequency->setMin(min);
0102             m_frequency->setMax(max);
0103         }
0104     }
0105     const QByteArray tjmax = m_sysctlPrefix + QByteArrayLiteral(".coretemp.tjmax");
0106     int maxTemperature;
0107     // This is only availabel on Intel (using the coretemp driver)
0108     if (readSysctl(tjmax.constData(), &maxTemperature)) {
0109         m_temperature->setMax(maxTemperature);
0110     }
0111 }
0112 
0113 void FreeBsdCpuObject::update(long system, long user, long idle)
0114 {
0115     // No wait usage on FreeBSD
0116     m_usageComputer.setTicks(system, user, 0, idle);
0117 
0118     m_system->setValue(m_usageComputer.systemUsage);
0119     m_user->setValue(m_usageComputer.userUsage);
0120     m_usage->setValue(m_usageComputer.totalUsage);
0121 
0122     // The calculations above are "free" because we already have the data;
0123     // is we are not subscribed, don't bother updating the data that needs
0124     // extra work to be done.
0125     if (!isSubscribed()) {
0126         return;
0127     }
0128 
0129     m_temperature->update();
0130     m_frequency->update();
0131 }
0132 
0133 void FreeBsdAllCpusObject::update(long system, long user, long idle)
0134 {
0135     // No wait usage on FreeBSD
0136     m_usageComputer.setTicks(system, user, 0, idle);
0137 
0138     m_system->setValue(m_usageComputer.systemUsage);
0139     m_user->setValue(m_usageComputer.userUsage);
0140     m_usage->setValue(m_usageComputer.totalUsage);
0141 }
0142 
0143 FreeBsdCpuPluginPrivate::FreeBsdCpuPluginPrivate(CpuPlugin* q)
0144     : CpuPluginPrivate(q)
0145 {
0146     m_loadAverages = new LoadAverages(m_container);
0147     // The values used here can be found in smp(4)
0148     int numCpu;
0149     readSysctl("hw.ncpu", &numCpu);
0150     for (int i = 0; i < numCpu; ++i) {
0151         auto cpu = new FreeBsdCpuObject(i, i18nc("@title", "CPU %1", i + 1), m_container);
0152         cpu->initialize();
0153         m_cpus.push_back(cpu);
0154     }
0155     m_allCpus = new FreeBsdAllCpusObject(m_container);
0156     m_allCpus->initialize();
0157 
0158     read_cp_times(m_cpus);
0159 }
0160 
0161 void FreeBsdCpuPluginPrivate::update()
0162 {
0163     m_loadAverages->update();
0164 
0165     auto isSubscribed = [] (const KSysGuard::SensorObject *o) {return o->isSubscribed();};
0166     if (std::none_of(m_cpus.cbegin(), m_cpus.cend(), isSubscribed) && !m_allCpus->isSubscribed()) {
0167         return;
0168     }
0169     read_cp_times(m_cpus);
0170     // update total values
0171     long cp_time[CPUSTATES];
0172     if (readSysctl("kern.cp_time", &cp_time)) {
0173         updateCpu(m_allCpus, cp_time);
0174     }
0175 }