diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/cpuusage | |
Add the files
Diffstat (limited to 'src/detection/cpuusage')
| -rw-r--r-- | src/detection/cpuusage/cpuusage.c | 74 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage.h | 12 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_apple.c | 33 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_bsd.c | 58 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_haiku.c | 27 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_linux.c | 46 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_nosupport.c | 6 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_sunos.c | 38 | ||||
| -rw-r--r-- | src/detection/cpuusage/cpuusage_windows.c | 169 |
9 files changed, 463 insertions, 0 deletions
diff --git a/src/detection/cpuusage/cpuusage.c b/src/detection/cpuusage/cpuusage.c new file mode 100644 index 0000000..a78ae7b --- /dev/null +++ b/src/detection/cpuusage/cpuusage.c @@ -0,0 +1,74 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" +#include "common/time.h" + +#include <stdint.h> + +static FFlist cpuTimes1; +static uint64_t startTime; + +void ffPrepareCPUUsage(void) { + if (startTime != 0) { + return; // Already prepared + } + + ffListInit(&cpuTimes1); + ffGetCpuUsageInfo(&cpuTimes1); + startTime = ffTimeGetNow(); +} + +const char* ffGetCpuUsageResult(FFCPUUsageOptions* options, FFlist* result) { + const char* error = NULL; + if (startTime == 0) { + ffListInit(&cpuTimes1); + error = ffGetCpuUsageInfo(&cpuTimes1); + if (error) { + return error; + } + ffTimeSleep(options->waitTime); + } else { + uint64_t elapsedTime = ffTimeGetNow() - startTime; + if (elapsedTime < options->waitTime) { + ffTimeSleep(options->waitTime - (uint32_t) elapsedTime); + } + } + + if (cpuTimes1.length == 0) { + return "No CPU cores found"; + } + + FF_LIST_AUTO_DESTROY cpuTimes2 = ffListCreate(); + uint32_t retryCount = 0; + +retry: + error = ffGetCpuUsageInfo(&cpuTimes2); + if (error) { + return error; + } + if (cpuTimes1.length != cpuTimes2.length) { + return "Unexpected CPU usage result"; + } + + for (uint32_t i = 0; i < cpuTimes1.length; ++i) { + FFCpuUsageInfo* cpuTime1 = FF_LIST_GET(FFCpuUsageInfo, cpuTimes1, i); + FFCpuUsageInfo* cpuTime2 = FF_LIST_GET(FFCpuUsageInfo, cpuTimes2, i); + if (cpuTime2->totalAll <= cpuTime1->totalAll) { + if (++retryCount <= 3) { + ffListClear(&cpuTimes2); + ffTimeSleep(options->waitTime); + goto retry; + } + return "CPU time did not increase. Try increasing wait time."; + } + } + + for (uint32_t i = 0; i < cpuTimes1.length; ++i) { + FFCpuUsageInfo* cpuTime1 = FF_LIST_GET(FFCpuUsageInfo, cpuTimes1, i); + FFCpuUsageInfo* cpuTime2 = FF_LIST_GET(FFCpuUsageInfo, cpuTimes2, i); + *FF_LIST_ADD(double, *result) = (double) (cpuTime2->inUseAll - cpuTime1->inUseAll) / (double) (cpuTime2->totalAll - cpuTime1->totalAll) * 100; + cpuTime1->inUseAll = cpuTime2->inUseAll; + cpuTime1->totalAll = cpuTime2->totalAll; + } + startTime = ffTimeGetNow(); + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage.h b/src/detection/cpuusage/cpuusage.h new file mode 100644 index 0000000..353b729 --- /dev/null +++ b/src/detection/cpuusage/cpuusage.h @@ -0,0 +1,12 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/cpuusage/option.h" + +typedef struct FFCpuUsageInfo { + uint64_t inUseAll; + uint64_t totalAll; +} FFCpuUsageInfo; +const char* ffGetCpuUsageInfo(FFlist* cpuTimes); + +const char* ffGetCpuUsageResult(FFCPUUsageOptions* options, FFlist* result); // list of double diff --git a/src/detection/cpuusage/cpuusage_apple.c b/src/detection/cpuusage/cpuusage_apple.c new file mode 100644 index 0000000..50a8b15 --- /dev/null +++ b/src/detection/cpuusage/cpuusage_apple.c @@ -0,0 +1,33 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" + +#include <mach/processor_info.h> +#include <mach/mach_host.h> +#include <mach/vm_map.h> + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + natural_t numCPUs = 0U; + processor_info_array_t cpuInfo; + mach_msg_type_number_t numCpuInfo; + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUs, &cpuInfo, &numCpuInfo) != KERN_SUCCESS) { + return "host_processor_info() failed"; + } + if (numCPUs * CPU_STATE_MAX != numCpuInfo) { + return "Unexpected host_processor_info() result"; + } + + for (natural_t i = 0U; i < numCPUs; ++i) { + integer_t inUse = cpuInfo[CPU_STATE_MAX * i + CPU_STATE_USER] + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_SYSTEM] + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_NICE]; + integer_t total = inUse + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_IDLE]; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = (uint64_t) inUse, + .totalAll = (uint64_t) total, + }; + } + + vm_deallocate(mach_task_self(), (vm_address_t) cpuInfo, numCpuInfo * sizeof(integer_t)); + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage_bsd.c b/src/detection/cpuusage/cpuusage_bsd.c new file mode 100644 index 0000000..f690d15 --- /dev/null +++ b/src/detection/cpuusage/cpuusage_bsd.c @@ -0,0 +1,58 @@ +#include "detection/cpuusage/cpuusage.h" +#include "common/mallocHelper.h" + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/resource.h> +#include <stdlib.h> + +#if __OpenBSD__ || __NetBSD__ + #include <sys/sched.h> +#endif + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + size_t neededLength = 0; +#if __OpenBSD__ || __NetBSD__ + #ifdef KERN_CPTIME + int ctls[] = { CTL_KERN, KERN_CPTIME }; + #else + int ctls[] = { CTL_KERN, KERN_CP_TIME }; + #endif + if (sysctl(ctls, 2, NULL, &neededLength, NULL, 0) != 0) { + return "sysctl({CTL_KERN, KERN_CPTIME}, 2, NULL) failed"; + } +#else + if (sysctlbyname("kern.cp_times", NULL, &neededLength, NULL, 0) != 0) { + return "sysctlbyname(kern.cp_times, NULL) failed"; + } +#endif + + uint32_t coreCount = (uint32_t) (neededLength / (CPUSTATES * sizeof(uint64_t))); + assert(coreCount > 0); + + FF_AUTO_FREE uint64_t (*cpTimes)[CPUSTATES] = malloc(neededLength); + +#if __OpenBSD__ || __NetBSD__ + if (sysctl(ctls, 2, cpTimes, &neededLength, NULL, 0) != 0) { + return "sysctl({CTL_KERN, KERN_CPTIME}, 2, NULL) failed"; + } +#else + if (sysctlbyname("kern.cp_times", cpTimes, &neededLength, NULL, 0) != 0) { + return "sysctlbyname(kern.cp_times, cpTime) failed"; + } +#endif + + for (uint32_t i = 0; i < coreCount; ++i) { + uint64_t* cpTime = cpTimes[i]; + uint64_t inUse = cpTime[CP_USER] + cpTime[CP_NICE] + cpTime[CP_SYS] + cpTime[CP_INTR]; + uint64_t total = inUse + cpTime[CP_IDLE]; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = inUse, + .totalAll = total, + }; + } + + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage_haiku.c b/src/detection/cpuusage/cpuusage_haiku.c new file mode 100644 index 0000000..f4d3c63 --- /dev/null +++ b/src/detection/cpuusage/cpuusage_haiku.c @@ -0,0 +1,27 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" +#include "common/mallocHelper.h" + +#include <OS.h> + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + system_info sysInfo; + if (get_system_info(&sysInfo) != B_OK) { + return "get_system_info() failed"; + } + + FF_AUTO_FREE cpu_info* cpuInfo = malloc(sizeof(*cpuInfo) * sysInfo.cpu_count); + if (get_cpu_info(0, sysInfo.cpu_count, cpuInfo) != B_OK) { + return "get_cpu_info() failed"; + } + + uint64_t uptime = (uint64_t) system_time(); + + for (uint32_t i = 0; i < sysInfo.cpu_count; ++i) { + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + info->inUseAll = (uint64_t) cpuInfo[i].active_time; + info->totalAll = uptime; + } + + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage_linux.c b/src/detection/cpuusage/cpuusage_linux.c new file mode 100644 index 0000000..fd2f43b --- /dev/null +++ b/src/detection/cpuusage/cpuusage_linux.c @@ -0,0 +1,46 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" +#include "common/io.h" + +#include <stdio.h> +#include <inttypes.h> + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + char buf[PROC_FILE_BUFFSIZ]; + ssize_t nRead = ffReadFileData("/proc/stat", ARRAY_SIZE(buf) - 1, buf); + if (nRead < 0) { +#ifdef __ANDROID__ + return "Accessing \"/proc/stat\" is restricted on Android O+"; +#else + return "ffReadFileData(\"/proc/stat\", ARRAY_SIZE(buf) - 1, buf) failed"; +#endif + } + buf[nRead] = '\0'; + + // Skip first line + char* start = NULL; + if ((start = strchr(buf, '\n')) == NULL) { + return "skip first line failed"; + } + ++start; + + uint64_t user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0; + char* token = NULL; + while ((token = strchr(start, '\n'))) { + if (sscanf(start, "cpu%*d%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%*[^\n]\n", &user, &nice, &system, &idle, &iowait, &irq, &softirq) == 7) { + uint64_t inUse = user + nice + system + irq + softirq; + uint64_t total = inUse + idle + iowait; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = inUse, + .totalAll = total, + }; + } else { + break; + } + start = token + 1; + } + + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage_nosupport.c b/src/detection/cpuusage/cpuusage_nosupport.c new file mode 100644 index 0000000..faade7b --- /dev/null +++ b/src/detection/cpuusage/cpuusage_nosupport.c @@ -0,0 +1,6 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" + +const char* ffGetCpuUsageInfo(FF_A_UNUSED FFlist* cpuTimes) { + return "Not support on this platform"; +} diff --git a/src/detection/cpuusage/cpuusage_sunos.c b/src/detection/cpuusage/cpuusage_sunos.c new file mode 100644 index 0000000..813d74d --- /dev/null +++ b/src/detection/cpuusage/cpuusage_sunos.c @@ -0,0 +1,38 @@ +#include "fastfetch.h" +#include "detection/cpuusage/cpuusage.h" + +#include <kstat.h> +#include <sys/sysinfo.h> + +static inline void kstatFreeWrap(kstat_ctl_t** pkc) { + assert(pkc); + if (*pkc) { + kstat_close(*pkc); + } +} + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + FF_A_CLEANUP(kstatFreeWrap) kstat_ctl_t* kc = kstat_open(); + if (!kc) { + return "kstat_open() failed"; + } + + for (int i = 0;; ++i) { + kstat_t* ks = kstat_lookup(kc, "cpu_stat", i, NULL); + + cpu_stat_t cs; + if (!ks || kstat_read(kc, ks, &cs) < 0) { + break; + } + + uint64_t inUse = cs.cpu_sysinfo.cpu[CPU_USER] + cs.cpu_sysinfo.cpu[CPU_KERNEL]; + uint64_t total = inUse + cs.cpu_sysinfo.cpu[CPU_IDLE] + cs.cpu_sysinfo.cpu[CPU_WAIT]; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = inUse, + .totalAll = total, + }; + } + return NULL; +} diff --git a/src/detection/cpuusage/cpuusage_windows.c b/src/detection/cpuusage/cpuusage_windows.c new file mode 100644 index 0000000..c23c28a --- /dev/null +++ b/src/detection/cpuusage/cpuusage_windows.c @@ -0,0 +1,169 @@ +#include "detection/cpuusage/cpuusage.h" +#include "common/mallocHelper.h" +#include "common/debug.h" + +#include <ntstatus.h> +#include <windows.h> +#include <wchar.h> +#include "common/windows/perflib_.h" +#include "common/windows/nt.h" + +static const char* getInfoByNqsi(FFlist* cpuTimes) { + ULONG size = 0; + if (NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) { + return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL) failed"; + } + + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* FF_AUTO_FREE pinfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*) malloc(size); + if (!NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, pinfo, size, &size))) { + return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, size) failed"; + } + + for (uint32_t i = 0; i < size / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); ++i) { + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i; + + // KernelTime includes IdleTime and DpcTime. + coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart; + + uint64_t inUse = (uint64_t) (coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart); + uint64_t total = inUse + (uint64_t) coreInfo->IdleTime.QuadPart; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = inUse, + .totalAll = total, + }; + } + + return NULL; +} + +static const char* getInfoByPerflib(FFlist* cpuTimes) { + static HANDLE hQuery = NULL; + + if (hQuery == NULL) { + struct FFPerfQuerySpec { + PERF_COUNTER_IDENTIFIER Identifier; + WCHAR Name[16]; + } querySpec = { + .Identifier = { + // Processor Information GUID + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{b4fc721a-0378-476f-89ba-a5a79f810b36} + .CounterSetGuid = { 0xb4fc721a, 0x0378, 0x476f, { 0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0x0b, 0x36 } }, + .Size = sizeof(querySpec), + .CounterId = PERF_WILDCARD_COUNTER, // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data + .InstanceId = PERF_WILDCARD_COUNTER, + }, + .Name = PERF_WILDCARD_INSTANCE, + }; + + if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfOpenQueryHandle() failed"; + } + + if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfAddCounters() failed"; + } + + if (querySpec.Identifier.Status != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfAddCounters() reports invalid identifier"; + } + } + + if (hQuery == INVALID_HANDLE_VALUE) { + return "Init hQuery failed"; + } + + DWORD dataSize = 0; + if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY) { + return "PerfQueryCounterData(NULL) failed"; + } + + if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER)) { + return "instance doesn't exist"; + } + + FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = (PERF_DATA_HEADER*) malloc(dataSize); + if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS) { + return "PerfQueryCounterData(pDataHeader) failed"; + } + + PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*) (pDataHeader + 1); + if (pCounterHeader->dwType != PERF_COUNTERSET) { + return "Invalid counter type"; + } + + PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*) (pCounterHeader + 1); + if (pMultiCounters->dwCounters == 0) { + return "No CPU counters found"; + } + + PERF_MULTI_INSTANCES* pMultiInstances = (PERF_MULTI_INSTANCES*) ((BYTE*) pMultiCounters + pMultiCounters->dwSize); + if (pMultiInstances->dwInstances == 0) { + return "No CPU instances found"; + } + + PERF_INSTANCE_HEADER* pInstanceHeader = (PERF_INSTANCE_HEADER*) (pMultiInstances + 1); + for (DWORD iInstance = 0; iInstance < pMultiInstances->dwInstances; ++iInstance) { + const wchar_t* instanceName = (const wchar_t*) ((BYTE*) pInstanceHeader + sizeof(*pInstanceHeader)); + + PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*) ((BYTE*) pInstanceHeader + pInstanceHeader->Size); + + uint64_t processorUtility = UINT64_MAX, utilityBase = UINT64_MAX; + for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++) { + DWORD* pCounterIds = (DWORD*) (pMultiCounters + 1); + // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data + switch (pCounterIds[iCounter]) { + case 26: // % Processor Utility (#26, Type=PERF_AVERAGE_BULK) + assert(pCounterData->dwDataSize == sizeof(uint64_t)); + processorUtility = *(uint64_t*) (pCounterData + 1); + break; + case 27: // % Utility Base (#27, Type=PERF_AVERAGE_BASE) + assert(pCounterData->dwDataSize == sizeof(uint32_t)); + utilityBase = *(uint32_t*) (pCounterData + 1) * 100LLU; + break; + } + + pCounterData = (PERF_COUNTER_DATA*) ((BYTE*) pCounterData + pCounterData->dwSize); + } + + if (wcschr(instanceName, L'_') == NULL /* ignore `_Total` */) { + if (processorUtility == UINT64_MAX) { + return "Counter \"% Processor Utility\" are not supported"; + } + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = processorUtility, + .totalAll = utilityBase, + }; + } + + pInstanceHeader = (PERF_INSTANCE_HEADER*) pCounterData; + } + + return NULL; +} + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + const char* error = NULL; + + if (ffIsWindows10OrGreater()) { + error = getInfoByPerflib(cpuTimes); + FF_DEBUG("Get CPU usage info by Perflib: %s", error ?: "success"); + if (!error) { + return NULL; + } + ffListClear(cpuTimes); + } + + error = getInfoByNqsi(cpuTimes); + FF_DEBUG("Get CPU usage info by NtQuerySystemInformation: %s", error ?: "success"); + return error; +} |