File indexing completed on 2024-05-19 05:30:18
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(QList<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 }