Files
fastfetch/src/detection/cpu/cpu_linux.c
T

1096 lines
35 KiB
C
Raw Normal View History

2022-09-04 22:20:12 +02:00
#include "cpu.h"
2026-01-06 09:48:38 +08:00
#include "common/io.h"
#include "common/processing.h"
#include "common/properties.h"
#include "common/mallocHelper.h"
#include "common/stringUtils.h"
#include "common/path.h"
2022-09-04 22:20:12 +02:00
2022-12-28 17:18:08 +08:00
#include <sys/sysinfo.h>
2022-09-04 22:20:12 +02:00
#include <stdlib.h>
#include <unistd.h>
2024-09-24 01:51:45 +00:00
#include <dirent.h>
#include <fcntl.h>
2024-09-24 01:51:45 +00:00
#define FF_CPUINFO_PATH "/proc/cpuinfo"
2026-03-29 09:28:49 +08:00
static double readTempFile(int dfd, const char* filename, FFstrbuf* buffer) {
if (filename ? !ffReadFileBufferRelative(dfd, filename, buffer) : !ffReadFDBuffer(dfd, buffer)) {
2025-12-10 16:13:22 +08:00
return FF_CPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2025-12-10 16:13:22 +08:00
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET); // millidegree Celsius
2026-03-29 09:28:49 +08:00
if (value == FF_CPU_TEMP_UNSET) {
2025-12-10 16:13:22 +08:00
return FF_CPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2025-12-10 16:13:22 +08:00
return value / 1000.;
}
2026-03-29 09:28:49 +08:00
static double parseTZDir(int dfd, FFstrbuf* buffer) {
if (!ffReadFileBufferRelative(dfd, "type", buffer)) {
2025-08-27 16:14:59 +08:00
return FF_CPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2024-09-24 01:51:45 +00:00
if (!ffStrbufStartsWithS(buffer, "cpu") &&
!ffStrbufStartsWithS(buffer, "soc") &&
2026-03-29 09:28:49 +08:00
#if __x86_64__ || __i386__
!ffStrbufEqualS(buffer, "x86_pkg_temp") &&
2026-03-29 09:28:49 +08:00
#endif
true)
return FF_CPU_TEMP_UNSET;
2025-12-10 16:13:22 +08:00
return readTempFile(dfd, "temp", buffer);
2025-08-27 16:14:59 +08:00
}
2026-03-29 09:28:49 +08:00
static double parseHwmonDir(int dfd, FFstrbuf* buffer) {
// https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
if (!ffReadFileBufferRelative(dfd, "name", buffer)) {
2025-08-14 09:47:14 +08:00
return FF_CPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2024-09-24 01:51:45 +00:00
ffStrbufTrimRightSpace(buffer);
if (
!ffStrbufContainS(buffer, "cpu") &&
2026-03-29 09:28:49 +08:00
#if __x86_64__ || __i386__
!ffStrbufEqualS(buffer, "k10temp") && // AMD
!ffStrbufEqualS(buffer, "fam15h_power") && // AMD
2026-03-29 09:28:49 +08:00
!ffStrbufEqualS(buffer, "coretemp") && // Intel
#endif
true)
return FF_CPU_TEMP_UNSET;
2025-12-10 16:13:22 +08:00
return readTempFile(dfd, "temp1_input", buffer);
2024-09-24 01:51:45 +00:00
}
2026-03-29 09:28:49 +08:00
static double detectCPUTemp(const FFCPUOptions* options) {
2024-09-24 01:51:45 +00:00
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
2025-12-10 16:13:22 +08:00
2026-03-29 09:28:49 +08:00
if (options->tempSensor.length > 0) {
2025-12-10 16:13:22 +08:00
FF_AUTO_CLOSE_FD int subfd = -1;
const char* fileName = NULL;
2026-03-29 09:28:49 +08:00
if (ffStrbufStartsWithS(&options->tempSensor, "hwmon") && ffCharIsDigit(options->tempSensor.chars[strlen("hwmon")])) {
2025-12-10 16:13:22 +08:00
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/hwmon/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd >= 0) {
2025-12-10 16:13:22 +08:00
fileName = "temp1_input";
2026-03-29 09:28:49 +08:00
} else {
2025-12-10 16:13:22 +08:00
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
}
} else if (ffStrbufStartsWithS(&options->tempSensor, "thermal_zone") && ffCharIsDigit(options->tempSensor.chars[strlen("thermal_zone")])) {
2025-12-10 16:13:22 +08:00
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/thermal/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd >= 0) {
2025-12-10 16:13:22 +08:00
fileName = "temp";
2026-03-29 09:28:49 +08:00
} else {
2025-12-10 16:13:22 +08:00
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
}
} else if (ffStrbufStartsWithS(&options->tempSensor, "cputemp.") && ffCharIsDigit(options->tempSensor.chars[strlen("cputemp.")])) {
2025-12-10 16:13:22 +08:00
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/platform/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd >= 0) {
2025-12-10 16:13:22 +08:00
fileName = "temp1_input";
2026-03-29 09:28:49 +08:00
} else {
2025-12-10 16:13:22 +08:00
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
}
} else if (ffIsAbsolutePath(options->tempSensor.chars)) {
2025-12-10 16:13:22 +08:00
subfd = open(options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd >= 0) {
2025-12-10 16:13:22 +08:00
fileName = "temp1_input";
2026-03-29 09:28:49 +08:00
} else {
2025-12-10 16:13:22 +08:00
subfd = open(options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
}
2025-12-10 16:13:22 +08:00
}
2026-03-29 09:28:49 +08:00
if (subfd < 0) {
2025-12-10 16:13:22 +08:00
return FF_CPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2025-12-10 16:13:22 +08:00
return readTempFile(subfd, fileName, &buffer);
}
2024-09-24 01:51:45 +00:00
{
2025-08-27 16:14:59 +08:00
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/hwmon/");
2026-03-29 09:28:49 +08:00
if (dirp) {
2025-08-27 16:14:59 +08:00
int dfd = dirfd(dirp);
2025-12-10 16:13:22 +08:00
2025-08-27 16:14:59 +08:00
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd < 0) {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
double result = parseHwmonDir(subfd, &buffer);
2026-03-29 09:28:49 +08:00
if (result != FF_CPU_TEMP_UNSET) {
2025-08-27 16:14:59 +08:00
return result;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
}
}
}
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/thermal/");
2026-03-29 09:28:49 +08:00
if (dirp) {
2025-08-27 16:14:59 +08:00
int dfd = dirfd(dirp);
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
if (!ffStrStartsWith(entry->d_name, "thermal_zone")) {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd < 0) {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
double result = parseTZDir(subfd, &buffer);
2026-03-29 09:28:49 +08:00
if (result != FF_CPU_TEMP_UNSET) {
2025-08-27 16:14:59 +08:00
return result;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
}
}
}
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/devices/platform/");
2026-03-29 09:28:49 +08:00
if (dirp) {
2025-08-27 16:14:59 +08:00
int dfd = dirfd(dirp);
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.') {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
if (!ffStrStartsWith(entry->d_name, "cputemp.")) {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (subfd < 0) {
2025-08-27 16:14:59 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
double result = parseHwmonDir(subfd, &buffer);
2026-03-29 09:28:49 +08:00
if (result != FF_CPU_TEMP_UNSET) {
2025-08-27 16:14:59 +08:00
return result;
2026-03-29 09:28:49 +08:00
}
2025-08-27 16:14:59 +08:00
}
}
2024-09-24 01:51:45 +00:00
}
2025-08-27 16:14:59 +08:00
return FF_CPU_TEMP_UNSET;
2024-09-24 01:51:45 +00:00
}
2022-09-04 22:20:12 +02:00
2026-03-29 09:28:49 +08:00
static void detectNumaNodes(FFCPUResult* cpu) {
2025-11-17 20:15:56 +08:00
FF_AUTO_CLOSE_DIR DIR* dir = opendir("/sys/devices/system/node/");
2026-03-29 09:28:49 +08:00
if (!dir) {
return;
}
2025-11-17 20:15:56 +08:00
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) {
2025-11-17 20:15:56 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
if (ffStrStartsWith(entry->d_name, "node") && ffCharIsDigit(entry->d_name[strlen("node")])) {
2025-11-17 20:15:56 +08:00
cpu->numaNodes++;
2026-03-29 09:28:49 +08:00
}
2025-11-17 20:15:56 +08:00
}
}
2023-07-27 19:35:55 +08:00
#ifdef __ANDROID__
2026-03-29 09:28:49 +08:00
# include "common/settings.h"
2023-07-27 19:35:55 +08:00
2026-03-29 09:28:49 +08:00
static void detectQualcomm(FFCPUResult* cpu) {
2024-09-06 14:01:56 +08:00
// https://en.wikipedia.org/wiki/List_of_Qualcomm_Snapdragon_systems_on_chips
assert(cpu->name.length >= 2);
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
const char* name = NULL;
2026-03-29 09:28:49 +08:00
switch (code) {
case 8845:
name = "8 Gen 5";
break; // ?
case 8850:
name = "8 Elite Gen 5";
break;
case 8735:
name = "8s Gen 4";
break;
case 8750:
name = "8 Elite";
break;
case 8635:
name = "8s Gen 3";
break;
case 8650:
name = "8 Gen 3";
break;
case 8550:
name = "8 Gen 2";
break;
case 8475:
name = "8+ Gen 1";
break;
case 8450:
name = "8 Gen 1";
break;
case 7750:
name = "7 Gen 4";
break;
case 7675:
name = "7+ Gen 3";
break;
case 7635:
name = "7s Gen 3";
break;
case 7550:
name = "7 Gen 3";
break;
case 7475:
name = "7+ Gen 2";
break;
case 7435:
name = "7s Gen 2";
break;
case 7450:
name = "7 Gen 1";
break;
case 6650:
name = "6 Gen 4";
break;
case 6375:
name = "6s Gen 3";
break;
case 6475:
name = "6 Gen 3";
break;
case 6115:
name = "6s Gen 1";
break;
case 6450:
name = "6 Gen 1";
break;
case 4635:
name = "4s Gen 2";
break;
case 4450:
name = "4 Gen 2";
break;
case 4375:
name = "4 Gen 1";
break;
}
if (name) {
char str[32];
ffStrCopy(str, cpu->name.chars, sizeof(str));
ffStrbufSetF(&cpu->name, "Qualcomm Snapdragon %s [%s]", name, str);
return;
}
}
2026-03-29 09:28:49 +08:00
static void detectMediaTek(FFCPUResult* cpu) {
2025-01-07 16:28:34 +08:00
// https://en.wikipedia.org/wiki/List_of_MediaTek_systems_on_chips
assert(cpu->name.length >= 2);
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
const char* name = NULL;
switch (code) // The SOC code of MTK Dimensity series is full of mess
{
2026-03-29 09:28:49 +08:00
case 6993:
name = "9500";
break;
case 6991:
name = "9400";
break;
case 6989:
2026-03-29 09:28:49 +08:00
case 8796:
name = "9300";
break;
case 6985:
name = "9200";
break;
case 6983:
2026-03-29 09:28:49 +08:00
case 8798:
name = "9000";
break;
2026-03-29 09:28:49 +08:00
case 6899:
name = "8400";
break;
case 6897:
2026-03-29 09:28:49 +08:00
case 8792:
name = "8300";
break;
case 6896:
name = "8200";
break;
case 8795:
name = "8100";
break;
case 6895:
name = "8000";
break;
}
2026-03-29 09:28:49 +08:00
if (name) {
char str[32];
ffStrCopy(str, cpu->name.chars, sizeof(str));
ffStrbufSetF(&cpu->name, "MediaTek Dimensity %s [%s]", name, str);
return;
}
2025-01-07 16:28:34 +08:00
}
2026-03-29 09:28:49 +08:00
static void detectExynos(FFCPUResult* cpu) {
// https://en.wikipedia.org/wiki/Exynos
assert(cpu->name.length > 3);
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 3, NULL, 10);
const char* name = NULL;
2026-03-29 09:28:49 +08:00
switch (code) {
case 9955:
name = "2500";
break;
case 9945:
name = "2400";
break;
// No 2300
2026-03-29 09:28:49 +08:00
case 9925:
name = "2200";
break;
case 9840:
name = "2100";
break;
2026-03-29 09:28:49 +08:00
case 8855:
name = "1580";
break;
case 8845:
name = "1480";
break;
case 8835:
name = "1380";
break;
case 8535:
name = "1330";
break;
case 8825:
name = "1280";
break;
case 9815:
name = "1080";
break;
2026-03-29 09:28:49 +08:00
case 9830:
name = "990";
break;
case 9630:
name = "980";
break;
2026-03-29 09:28:49 +08:00
case 8805:
name = "880";
break;
case 3830:
name = "850";
break;
}
2026-03-29 09:28:49 +08:00
if (name) {
char str[32];
ffStrCopy(str, cpu->name.chars, sizeof(str));
ffStrbufSetF(&cpu->name, "Samsung Exynos %s [%s]", name, str);
return;
}
}
2026-03-29 09:28:49 +08:00
static void detectAndroid(FFCPUResult* cpu) {
if (cpu->name.length == 0) {
if (ffSettingsGetAndroidProperty("ro.soc.model", &cpu->name)) {
2024-07-25 10:48:42 +08:00
ffStrbufClear(&cpu->vendor); // We usually detect the vendor of CPU core as ARM, but instead we want the vendor of SOC
2026-03-29 09:28:49 +08:00
}
}
2026-03-29 09:28:49 +08:00
if (cpu->vendor.length == 0) {
if (!ffSettingsGetAndroidProperty("ro.soc.manufacturer", &cpu->vendor)) {
if (!ffSettingsGetAndroidProperty("ro.product.product.manufacturer", &cpu->vendor)) {
if (!ffSettingsGetAndroidProperty("ro.product.vendor.manufacturer", &cpu->vendor)) {
if (ffSettingsGetAndroidProperty("ro.mediatek.platform", &cpu->name)) {
ffStrbufSetStatic(&cpu->vendor, "MediaTek");
2026-03-29 09:28:49 +08:00
}
}
}
}
2023-07-27 19:35:55 +08:00
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(&cpu->vendor, "QTI")) {
ffStrbufSetStatic(&cpu->vendor, "Qualcomm");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufIgnCaseEqualS(&cpu->vendor, "MediaTek")) { // sometimes "Mediatek"
ffStrbufSetStatic(&cpu->vendor, "MediaTek");
2026-03-29 09:28:49 +08:00
} else if (cpu->vendor.length > 0) {
cpu->vendor.chars[0] = (char) toupper(cpu->vendor.chars[0]);
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(&cpu->vendor, "Qualcomm") && ffStrbufStartsWithS(&cpu->name, "SM")) {
detectQualcomm(cpu);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&cpu->vendor, "MediaTek") && ffStrbufStartsWithS(&cpu->name, "MT")) {
2025-01-07 16:28:34 +08:00
detectMediaTek(cpu);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&cpu->vendor, "Samsung") && ffStrbufStartsWithS(&cpu->name, "s5e")) {
cpu->name.chars[0] = 'S';
cpu->name.chars[2] = 'E';
detectExynos(cpu);
}
2023-07-27 19:35:55 +08:00
}
#endif
2024-08-15 15:24:13 +08:00
#if __arm__ || __aarch64__
2026-03-29 09:28:49 +08:00
# include "cpu_arm.h"
2022-09-04 22:20:12 +02:00
2026-03-29 09:28:49 +08:00
static void detectArmName(FFstrbuf* cpuinfo, FFCPUResult* cpu, uint32_t implId) {
char* line = NULL;
2022-09-04 22:20:12 +02:00
size_t len = 0;
2024-08-15 14:43:00 +08:00
uint32_t lastPartId = UINT32_MAX;
uint32_t num = 0;
2026-03-29 09:28:49 +08:00
while (ffStrbufGetline(&line, &len, cpuinfo)) {
if (!ffStrStartsWith(line, "CPU part\t: ")) {
continue;
}
uint32_t partId = (uint32_t) strtoul(line + strlen("CPU part\t: "), NULL, 16);
const char* name = NULL;
2026-03-29 09:28:49 +08:00
switch (implId) {
case 0x41:
name = armPartId2name(partId);
break;
case 0x42:
name = brcmPartId2name(partId);
break;
case 0x43:
name = caviumPartId2name(partId);
break;
case 0x44:
name = decPartId2name(partId);
break;
case 0x46:
name = fujitsuPartId2name(partId);
break;
case 0x48:
name = hisiPartId2name(partId);
break;
case 0x4e:
name = nvidiaPartId2name(partId);
break;
case 0x50:
name = apmPartId2name(partId);
break;
case 0x51:
name = qcomPartId2name(partId);
break;
case 0x53:
name = samsungPartId2name(partId);
break;
case 0x56:
name = marvellPartId2name(partId);
break;
case 0x61:
2026-03-29 09:28:49 +08:00
if (partId == 0) {
// https://github.com/Dr-Noob/cpufetch/issues/213#issuecomment-1927782105
ffStrbufSetStatic(&cpu->name, "Virtualized Apple Silicon");
ffStrbufGetlineRestore(&line, &len, cpuinfo);
return;
}
name = applePartId2name(partId);
break;
2026-03-29 09:28:49 +08:00
case 0x66:
name = faradayPartId2name(partId);
break;
case 0x69:
name = intelPartId2name(partId);
break;
case 0x6d:
name = msPartId2name(partId);
break;
case 0x70:
name = ftPartId2name(partId);
break;
case 0xc0:
name = amperePartId2name(partId);
break;
}
if (lastPartId != partId) {
if (lastPartId != UINT32_MAX) {
if (num > 1) {
ffStrbufAppendF(&cpu->name, "*%u", num);
2026-03-29 09:28:49 +08:00
}
ffStrbufAppendS(&cpu->name, " + ");
}
2026-03-29 09:28:49 +08:00
if (name) {
2024-08-15 14:43:00 +08:00
ffStrbufAppendS(&cpu->name, name);
2026-03-29 09:28:49 +08:00
} else if (partId) {
2024-08-15 14:43:00 +08:00
ffStrbufAppendF(&cpu->name, "%s-%X", cpu->vendor.chars, partId);
2026-03-29 09:28:49 +08:00
} else {
2024-08-15 14:43:00 +08:00
ffStrbufAppend(&cpu->name, &cpu->vendor);
2026-03-29 09:28:49 +08:00
}
2024-08-15 14:43:00 +08:00
lastPartId = partId;
num = 1;
2026-03-29 09:28:49 +08:00
} else {
++num;
2026-03-29 09:28:49 +08:00
}
}
2026-03-29 09:28:49 +08:00
if (num > 1) {
ffStrbufAppendF(&cpu->name, "*%u", num);
2026-03-29 09:28:49 +08:00
}
}
#endif
2022-09-04 22:20:12 +02:00
static const char* parseCpuInfo(
FFstrbuf* cpuinfo,
FFCPUResult* cpu,
2024-08-22 09:16:20 +08:00
FF_MAYBE_UNUSED FFstrbuf* physicalCoresBuffer,
FF_MAYBE_UNUSED FFstrbuf* cpuMHz,
FF_MAYBE_UNUSED FFstrbuf* cpuIsa,
FF_MAYBE_UNUSED FFstrbuf* cpuUarch,
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED FFstrbuf* cpuImplementer) {
char* line = NULL;
size_t len = 0;
2024-01-31 13:28:18 +08:00
2026-03-29 09:28:49 +08:00
while (ffStrbufGetline(&line, &len, cpuinfo)) {
// Stop after reasonable information is acquired
if ((*line == '\0' || *line == '\n') && cpu->name.length > 0) {
ffStrbufGetlineRestore(&line, &len, cpuinfo);
2022-09-04 22:20:12 +02:00
break;
}
2022-09-04 22:20:12 +02:00
2026-03-29 09:28:49 +08:00
(void) (
// arm64 doesn't have "model name"; arm32 does have "model name" but its value is not useful.
// "Hardware" should always be used in this case
#if __x86_64__ || __i386__
2024-08-22 09:16:20 +08:00
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
(cpu->vendor.length == 0 && ffParsePropLine(line, "vendor_id :", &cpu->vendor)) ||
(physicalCoresBuffer->length == 0 && ffParsePropLine(line, "cpu cores :", physicalCoresBuffer)) ||
(cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) ||
2026-03-29 09:28:49 +08:00
#elif __arm__ || __aarch64__
(cpuImplementer->length == 0 && ffParsePropLine(line, "CPU implementer :", cpuImplementer)) ||
2026-03-29 09:28:49 +08:00
(cpu->name.length == 0 && ffParsePropLine(line, "Hardware :", &cpu->name)) || // For Android devices
#elif __powerpc__ || __powerpc
(cpuMHz->length == 0 && ffParsePropLine(line, "clock :", cpuMHz)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
2026-03-29 09:28:49 +08:00
#elif __mips__ || __mips
(cpu->name.length == 0 && ffParsePropLine(line, "cpu model :", &cpu->name)) ||
2026-03-29 09:28:49 +08:00
#elif __loongarch__
2025-01-27 13:06:08 +08:00
(cpu->name.length == 0 && ffParsePropLine(line, "Model Name :", &cpu->name)) ||
(cpuMHz->length == 0 && ffParsePropLine(line, "CPU MHz :", cpuMHz)) ||
2026-03-29 09:28:49 +08:00
#elif __riscv__ || __riscv
2025-01-27 13:06:08 +08:00
(cpuIsa->length == 0 && ffParsePropLine(line, "isa :", cpuIsa)) ||
(cpuUarch->length == 0 && ffParsePropLine(line, "uarch :", cpuUarch)) ||
2026-03-29 09:28:49 +08:00
#elif __s390x__
2026-01-05 10:50:55 +08:00
(cpu->name.length == 0 && ffParsePropLine(line, "machine :", &cpu->name)) ||
(cpu->vendor.length == 0 && ffParsePropLine(line, "vendor_id :", &cpu->vendor)) ||
2026-01-05 10:50:55 +08:00
(cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz static :", cpuMHz)) ||
2026-03-29 09:28:49 +08:00
#elif __ia64__
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
(cpu->vendor.length == 0 && ffParsePropLine(line, "vendor :", &cpu->vendor)) ||
(cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) ||
2026-03-29 09:28:49 +08:00
#elif __hppa__
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
2026-03-29 09:28:49 +08:00
#elif __sh__
(cpu->name.length == 0 && ffParsePropLine(line, "cpu type :", &cpu->name)) ||
2026-03-29 09:28:49 +08:00
#else
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "model :", &cpu->name)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu model :", &cpu->name)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "hardware :", &cpu->name)) ||
(cpu->name.length == 0 && ffParsePropLine(line, "processor :", &cpu->name)) ||
2026-03-29 09:28:49 +08:00
#endif
2025-01-27 13:06:08 +08:00
2026-03-29 09:28:49 +08:00
false);
2022-09-04 22:20:12 +02:00
}
2023-06-12 09:56:03 +08:00
return NULL;
2022-09-04 22:20:12 +02:00
}
2026-03-29 09:28:49 +08:00
static uint32_t getFrequency(FFstrbuf* basePath, const char* cpuinfoFileName, const char* scalingFileName, FFstrbuf* buffer) {
uint32_t baseLen = basePath->length;
ffStrbufAppendS(basePath, cpuinfoFileName);
bool ok = ffReadFileBuffer(basePath->chars, buffer);
ffStrbufSubstrBefore(basePath, baseLen);
2026-03-29 09:28:49 +08:00
if (ok) {
2024-07-17 10:56:30 +08:00
return (uint32_t) (ffStrbufToUInt(buffer, 0) / 1000);
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (scalingFileName) {
2024-03-12 23:48:03 +08:00
ffStrbufAppendS(basePath, scalingFileName);
ok = ffReadFileBuffer(basePath->chars, buffer);
ffStrbufSubstrBefore(basePath, baseLen);
2026-03-29 09:28:49 +08:00
if (ok) {
2024-07-17 10:56:30 +08:00
return (uint32_t) (ffStrbufToUInt(buffer, 0) / 1000);
2026-03-29 09:28:49 +08:00
}
2024-03-12 23:48:03 +08:00
}
return 0;
}
2026-03-29 09:28:49 +08:00
static uint8_t getNumCores(FFstrbuf* basePath, FFstrbuf* buffer) {
uint32_t baseLen = basePath->length;
ffStrbufAppendS(basePath, "/affected_cpus");
bool ok = ffReadFileBuffer(basePath->chars, buffer);
ffStrbufSubstrBefore(basePath, baseLen);
2026-03-29 09:28:49 +08:00
if (ok) {
return (uint8_t) (ffStrbufCountC(buffer, ' ') + 1);
2026-03-29 09:28:49 +08:00
}
ffStrbufAppendS(basePath, "/related_cpus");
ok = ffReadFileBuffer(basePath->chars, buffer);
ffStrbufSubstrBefore(basePath, baseLen);
2026-03-29 09:28:49 +08:00
if (ok) {
return (uint8_t) (ffStrbufCountC(buffer, ' ') + 1);
2026-03-29 09:28:49 +08:00
}
return 0;
2022-09-04 22:20:12 +02:00
}
2026-03-29 09:28:49 +08:00
static bool detectFrequency(FFCPUResult* cpu, const FFCPUOptions* options) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/sys/devices/system/cpu/cpufreq/");
FF_AUTO_CLOSE_DIR DIR* dir = opendir(path.chars);
2026-03-29 09:28:49 +08:00
if (!dir) {
return false;
}
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
uint32_t baseLen = path.length;
struct dirent* entry;
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dir)) != NULL) {
if (ffStrStartsWith(entry->d_name, "policy") && ffCharIsDigit(entry->d_name[strlen("policy")])) {
ffStrbufAppendS(&path, entry->d_name);
2024-09-28 16:49:03 +08:00
uint32_t fmax = getFrequency(&path, "/cpuinfo_max_freq", "/scaling_max_freq", &buffer);
2026-03-29 09:28:49 +08:00
if (fmax == 0) {
continue;
}
2024-09-28 16:49:03 +08:00
2026-03-29 09:28:49 +08:00
if (cpu->frequencyMax >= fmax) {
if (!options->showPeCoreCount) {
2024-09-28 16:49:03 +08:00
ffStrbufSubstrBefore(&path, baseLen);
continue;
}
2026-03-29 09:28:49 +08:00
} else {
2024-09-28 16:49:03 +08:00
cpu->frequencyMax = fmax;
2026-03-29 09:28:49 +08:00
}
2024-09-28 16:49:03 +08:00
uint32_t fbase = getFrequency(&path, "/base_frequency", NULL, &buffer);
2026-03-29 09:28:49 +08:00
if (fbase > 0) {
2024-07-17 10:56:30 +08:00
cpu->frequencyBase = cpu->frequencyBase > fbase ? cpu->frequencyBase : fbase;
2026-03-29 09:28:49 +08:00
}
2024-07-17 10:56:30 +08:00
2026-03-29 09:28:49 +08:00
if (options->showPeCoreCount) {
2024-05-21 09:09:08 +08:00
uint32_t freq = fbase == 0 ? fmax : fbase; // seems base frequencies are more stable
uint32_t ifreq = 0;
2026-03-29 09:28:49 +08:00
while (cpu->coreTypes[ifreq].freq != freq && cpu->coreTypes[ifreq].freq > 0) {
2024-05-21 09:09:08 +08:00
++ifreq;
2026-03-29 09:28:49 +08:00
}
if (cpu->coreTypes[ifreq].freq == 0) {
2024-05-21 09:09:08 +08:00
cpu->coreTypes[ifreq].freq = freq;
2026-03-29 09:28:49 +08:00
}
2024-05-21 09:09:08 +08:00
cpu->coreTypes[ifreq].count += getNumCores(&path, &buffer);
}
ffStrbufSubstrBefore(&path, baseLen);
}
}
return true;
2022-09-04 22:20:12 +02:00
}
2025-01-27 13:06:08 +08:00
#if __i386__ || __x86_64__
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED static uint16_t getPackageCount(FFstrbuf* cpuinfo) {
2025-01-27 13:06:08 +08:00
const char* p = cpuinfo->chars;
uint64_t low = 0, high = 0;
2026-03-29 09:28:49 +08:00
while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), "\nphysical id\t:", strlen("\nphysical id\t:")))) {
2025-01-27 13:06:08 +08:00
p += strlen("\nphysical id\t:");
char* pend;
unsigned long long id = strtoul(p, &pend, 10);
2026-03-29 09:28:49 +08:00
if (__builtin_expect(id > 64, false)) { // Do 129-socket boards exist?
high |= 1ULL << (id - 64);
2026-03-29 09:28:49 +08:00
} else {
low |= 1ULL << id;
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
p = pend;
}
return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high));
}
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFCPUResult* cpu) {
2025-01-27 13:06:08 +08:00
FF_STRBUF_AUTO_DESTROY cpuinfo = ffStrbufCreateA(PROC_FILE_BUFFSIZ);
2026-03-29 09:28:49 +08:00
if (!ffReadFileBuffer(FF_CPUINFO_PATH, &cpuinfo) || cpuinfo.length == 0) {
return "ffReadFileBuffer(\"" FF_CPUINFO_PATH "\") failed";
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
FF_STRBUF_AUTO_DESTROY physicalCoresBuffer = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY cpuMHz = ffStrbufCreate();
2026-03-29 09:28:49 +08:00
const char* error = parseCpuInfo(&cpuinfo, cpu, &physicalCoresBuffer, &cpuMHz, NULL, NULL, NULL);
if (error) {
return error;
}
2025-01-27 13:06:08 +08:00
cpu->coresLogical = (uint16_t) get_nprocs_conf();
cpu->coresOnline = (uint16_t) get_nprocs();
cpu->packages = getPackageCount(&cpuinfo);
cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, 0); // physical cores in single package
2026-03-29 09:28:49 +08:00
if (cpu->coresPhysical > 0 && cpu->packages > 1) {
2025-01-27 13:06:08 +08:00
cpu->coresPhysical *= cpu->packages;
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
// Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252
ffCPUDetectByCpuid(cpu);
2026-03-29 09:28:49 +08:00
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0) {
2025-01-27 13:06:08 +08:00
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
2025-11-17 20:15:56 +08:00
detectNumaNodes(cpu);
2025-01-27 13:06:08 +08:00
return NULL;
}
#else
2026-03-29 09:28:49 +08:00
static const char* detectPhysicalCores(FFCPUResult* cpu) {
2025-06-19 18:11:54 +08:00
int dfd = open("/sys/devices/system/cpu/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
2026-03-29 09:28:49 +08:00
if (dfd < 0) {
return "open(\"/sys/devices/system/cpu/\") failed";
}
FF_AUTO_CLOSE_DIR DIR* dir = fdopendir(dfd);
2026-03-29 09:28:49 +08:00
if (!dir) {
return "fdopendir(dfd) failed";
}
uint64_t pkgLow = 0, pkgHigh = 0;
struct dirent* entry;
FF_LIST_AUTO_DESTROY cpuList = ffListCreate(sizeof(uint32_t));
2026-03-29 09:28:49 +08:00
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type != DT_DIR || !ffStrStartsWith(entry->d_name, "cpu") || !ffCharIsDigit(entry->d_name[strlen("cpu")])) {
continue;
2026-03-29 09:28:49 +08:00
}
FF_AUTO_CLOSE_FD int cpuxfd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_DIRECTORY);
2026-03-29 09:28:49 +08:00
if (cpuxfd < 0) {
continue;
2026-03-29 09:28:49 +08:00
}
char buf[128];
// Check if the directory contains a file named "topology/physical_package_id"
// that lists the physical package id of the CPU.
ssize_t len = ffReadFileDataRelative(cpuxfd, "topology/physical_package_id", sizeof(buf) - 1, buf);
2026-03-29 09:28:49 +08:00
if (len > 0) {
buf[len] = '\0';
unsigned long long id = strtoul(buf, NULL, 10);
2026-03-29 09:28:49 +08:00
if (__builtin_expect(id > 64, false)) { // Do 129-socket boards exist?
pkgHigh |= 1ULL << (id - 64);
2026-03-29 09:28:49 +08:00
} else {
pkgLow |= 1ULL << id;
2026-03-29 09:28:49 +08:00
}
}
// Check if the directory contains a file named "topology/core_cpus_list"
// that lists the physical cores in the package.
len = ffReadFileDataRelative(cpuxfd, "topology/core_cpus_list", sizeof(buf) - 1, buf);
2026-03-29 09:28:49 +08:00
if (len > 0) {
buf[len] = '\0'; // low-high or low
2026-03-29 09:28:49 +08:00
for (const char* p = buf; *p;) {
char* pend;
uint32_t coreId = (uint32_t) strtoul(p, &pend, 10);
2026-03-29 09:28:49 +08:00
if (pend == p) {
break;
}
bool found = false;
2026-03-29 09:28:49 +08:00
FF_LIST_FOR_EACH (uint32_t, id, cpuList) {
if (*id == coreId) {
// This core is already counted
found = true;
break;
}
}
2026-03-29 09:28:49 +08:00
if (!found) {
*(uint32_t*) ffListAdd(&cpuList) = coreId;
2026-03-29 09:28:49 +08:00
}
p = strchr(pend, ',');
2026-03-29 09:28:49 +08:00
if (!p) {
break;
}
++p;
}
}
}
cpu->coresPhysical = (uint16_t) cpuList.length;
cpu->packages = (uint16_t) (__builtin_popcountll(pkgLow) + __builtin_popcountll(pkgHigh));
return NULL;
}
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED static void parseIsa(FFstrbuf* cpuIsa) {
2024-08-22 09:16:20 +08:00
// Always use the last part of the ISA string. Ref: #590 #1204
ffStrbufSubstrAfterLastC(cpuIsa, ' ');
2026-03-29 09:28:49 +08:00
if (ffStrbufStartsWithS(cpuIsa, "rv")) {
// RISC-V ISA string example: "rv64imafdch_zicsr_zifencei".
// The _z parts are not important for CPU showcasing, so we remove them.
2026-03-29 09:28:49 +08:00
if (ffStrbufContainC(cpuIsa, '_')) {
ffStrbufSubstrBeforeFirstC(cpuIsa, '_');
2026-03-29 09:28:49 +08:00
}
// Then we replace "imafd" with "g" since "g" is a shorthand.
2026-03-29 09:28:49 +08:00
if (ffStrbufContainS(cpuIsa, "imafd")) {
// Remove 4 of the 5 characters and replace the remaining one with "g".
ffStrbufRemoveSubstr(cpuIsa, 4, 8);
cpuIsa->chars[4] = 'g';
}
// The final ISA output of the above example is "rv64gch".
}
}
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED static void detectSocName(FFCPUResult* cpu) {
if (cpu->name.length > 0) {
2024-09-09 20:48:19 +08:00
return;
2026-03-29 09:28:49 +08:00
}
2024-09-09 20:48:19 +08:00
// [x-vendor,x-model\0]*N
char content[512];
ssize_t length = ffReadFileData("/sys/firmware/devicetree/base/compatible", ARRAY_SIZE(content), content);
2026-03-29 09:28:49 +08:00
if (length < 4) {
return; // v,m\0
}
2024-09-09 20:48:19 +08:00
2026-03-29 09:28:49 +08:00
if (content[length - 1] != '\0') {
return; // must end with \0
}
2024-01-31 13:28:18 +08:00
--length;
char* vendor = NULL;
char* model = NULL;
2026-03-29 09:28:49 +08:00
for (char* p; length > 0; length = p ? (ssize_t) (p - content) - 1 : 0) {
p = memrchr(content, '\0', (size_t) length);
vendor = p /* first entry */ ? p + 1 : content;
size_t partLen = (size_t) (length - (vendor - content));
2026-03-29 09:28:49 +08:00
if (partLen < 3) {
continue;
}
char* comma = memchr(vendor, ',', partLen);
2026-03-29 09:28:49 +08:00
if (!comma) {
continue;
}
size_t vendorLen = (size_t) (comma - vendor);
2026-03-29 09:28:49 +08:00
if (vendorLen == 0) {
continue;
}
model = comma + 1;
size_t modelLen = (size_t) (partLen - (size_t) (model - vendor));
2026-03-29 09:28:49 +08:00
if (modelLen == 0) {
continue;
}
if ((modelLen >= strlen("-platform") && ffStrEndsWith(model, "-platform")) ||
2026-03-29 09:28:49 +08:00
(modelLen >= strlen("-soc") && ffStrEndsWith(model, "-soc"))) {
continue;
2026-03-29 09:28:49 +08:00
}
*comma = '\0';
break;
}
2026-03-29 09:28:49 +08:00
if (!length) {
return;
}
2024-09-30 15:03:25 +08:00
2026-03-29 09:28:49 +08:00
if (false) {
}
# if __aarch64__
else if (ffStrEquals(vendor, "apple")) {
2024-09-30 15:03:25 +08:00
// https://elixir.bootlin.com/linux/v6.11/source/arch/arm64/boot/dts/apple
2026-03-29 09:28:49 +08:00
if (model[0] == 't') {
2024-09-30 15:03:25 +08:00
uint32_t deviceId = (uint32_t) strtoul(model + 1, NULL, 10);
ffStrbufSetStatic(&cpu->name, ffCPUAppleCodeToName(deviceId));
2026-03-29 09:28:49 +08:00
if (!cpu->name.length) {
2024-09-30 15:03:25 +08:00
ffStrbufSetS(&cpu->name, "Apple Silicon ");
ffStrbufAppendS(&cpu->name, model);
}
2026-03-29 09:28:49 +08:00
} else {
2024-09-30 15:03:25 +08:00
ffStrbufSetS(&cpu->name, model);
2026-03-29 09:28:49 +08:00
}
2024-09-30 15:03:25 +08:00
ffStrbufSetStatic(&cpu->vendor, "Apple");
2024-09-09 20:48:19 +08:00
}
2026-03-29 09:28:49 +08:00
# endif
else if (ffStrEquals(vendor, "qcom")) {
2024-09-30 15:03:25 +08:00
// https://elixir.bootlin.com/linux/v6.11/source/arch/arm64/boot/dts/qcom
2026-03-29 09:28:49 +08:00
if (ffStrStartsWith(model, "x")) {
2024-09-09 20:48:19 +08:00
ffStrbufSetS(&cpu->name, "Qualcomm Snapdragon X Elite ");
2026-03-29 09:28:49 +08:00
for (const char* p = model + 1; *p; ++p) {
2024-09-09 20:48:19 +08:00
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
2026-03-29 09:28:49 +08:00
}
} else if (ffStrStartsWith(model, "sc")) {
2024-09-30 15:03:25 +08:00
const char* code = model + 2;
2024-09-09 20:48:19 +08:00
uint32_t deviceId = (uint32_t) strtoul(code, NULL, 10);
ffStrbufSetStatic(&cpu->name, ffCPUQualcommCodeToName(deviceId));
2026-03-29 09:28:49 +08:00
if (!cpu->name.length) {
2024-09-09 20:48:19 +08:00
ffStrbufAppendS(&cpu->name, "Qualcomm Snapdragon SC");
ffStrbufAppendS(&cpu->name, code);
}
2026-03-29 09:28:49 +08:00
} else {
2024-09-30 15:03:25 +08:00
ffStrbufSetS(&cpu->name, model);
2026-03-29 09:28:49 +08:00
}
2024-09-30 15:03:25 +08:00
ffStrbufSetStatic(&cpu->vendor, "Qualcomm");
2026-03-29 09:28:49 +08:00
} else if (ffStrEquals(vendor, "brcm")) {
2024-09-30 15:03:25 +08:00
// Raspberry Pi
ffStrbufSetStatic(&cpu->vendor, "Broadcom");
2026-03-29 09:28:49 +08:00
for (const char* p = model; *p; ++p) {
2024-09-30 15:03:25 +08:00
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
2026-03-29 09:28:49 +08:00
}
} else if (ffStrEquals(vendor, "thead")) {
// Lichee Pi?
ffStrbufSetStatic(&cpu->vendor, "T-Head");
2026-03-29 09:28:49 +08:00
for (const char* p = model; *p; ++p) {
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
2026-03-29 09:28:49 +08:00
}
} else {
2024-09-30 15:03:25 +08:00
ffStrbufSetS(&cpu->name, model);
ffStrbufSetS(&cpu->vendor, vendor);
cpu->vendor.chars[0] = (char) toupper(vendor[0]);
2024-01-31 13:28:18 +08:00
}
}
2026-03-29 09:28:49 +08:00
# ifdef __loongarch__
FF_MAYBE_UNUSED static uint16_t getLoongarchPropCount(FFstrbuf* cpuinfo, const char* key) {
const char* p = cpuinfo->chars;
uint64_t low = 0, high = 0;
uint32_t keylen = (uint32_t) strlen(key);
2026-03-29 09:28:49 +08:00
while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), key, keylen))) {
p += keylen;
char* pend;
unsigned long id = strtoul(p, &pend, 10);
2026-03-29 09:28:49 +08:00
if (__builtin_expect(id > 64, false)) {
high |= 1UL << (id - 64);
2026-03-29 09:28:49 +08:00
} else {
low |= 1UL << id;
2026-03-29 09:28:49 +08:00
}
p = pend;
}
return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high));
}
2026-03-29 09:28:49 +08:00
# endif
2026-03-29 09:28:49 +08:00
FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options, FFCPUResult* cpu) {
cpu->coresLogical = (uint16_t) get_nprocs_conf();
2022-12-28 17:18:08 +08:00
cpu->coresOnline = (uint16_t) get_nprocs();
2022-09-04 22:20:12 +02:00
2026-03-29 09:28:49 +08:00
# if __ANDROID__
2025-01-22 22:19:23 +08:00
detectAndroid(cpu);
2026-03-29 09:28:49 +08:00
# elif !__powerpc__ && !__powerpc
2025-01-22 22:19:23 +08:00
detectSocName(cpu);
2026-03-29 09:28:49 +08:00
# endif
2023-06-12 09:56:03 +08:00
2025-01-27 13:06:08 +08:00
detectFrequency(cpu, options);
2026-03-29 09:28:49 +08:00
if (cpu->name.length == 0) {
2025-01-27 13:06:08 +08:00
FF_STRBUF_AUTO_DESTROY cpuinfo = ffStrbufCreateA(PROC_FILE_BUFFSIZ);
2026-03-29 09:28:49 +08:00
if (!ffReadFileBuffer(FF_CPUINFO_PATH, &cpuinfo) || cpuinfo.length == 0) {
return "ffReadFileBuffer(\"" FF_CPUINFO_PATH "\") failed";
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
FF_STRBUF_AUTO_DESTROY cpuMHz = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY cpuIsa = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY cpuUarch = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY cpuImplementerStr = ffStrbufCreate();
const char* error = parseCpuInfo(&cpuinfo, cpu, NULL, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr);
2026-03-29 09:28:49 +08:00
if (error) {
return error;
}
2025-01-27 13:06:08 +08:00
2026-03-29 09:28:49 +08:00
if (cpu->frequencyBase == 0) {
2025-01-27 13:06:08 +08:00
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
2026-03-29 09:28:49 +08:00
# if __arm__ || __aarch64__
2025-01-27 13:06:08 +08:00
uint32_t cpuImplementer = (uint32_t) strtoul(cpuImplementerStr.chars, NULL, 16);
ffStrbufSetStatic(&cpu->vendor, hwImplId2Vendor(cpuImplementer));
2025-01-22 22:19:23 +08:00
2026-03-29 09:28:49 +08:00
if (cpu->name.length == 0) {
2025-01-27 13:06:08 +08:00
detectArmName(&cpuinfo, cpu, cpuImplementer);
2026-03-29 09:28:49 +08:00
}
# elif __riscv__ || __riscv
if (cpu->name.length == 0) {
if (cpuUarch.length > 0) {
if (cpu->name.length > 0) {
2025-01-27 13:06:08 +08:00
ffStrbufAppendC(&cpu->name, ' ');
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
ffStrbufAppend(&cpu->name, &cpuUarch);
}
2026-03-29 09:28:49 +08:00
if (cpuIsa.length > 0) {
2025-01-27 13:06:08 +08:00
parseIsa(&cpuIsa);
2026-03-29 09:28:49 +08:00
if (cpu->name.length > 0) {
2025-01-27 13:06:08 +08:00
ffStrbufAppendC(&cpu->name, ' ');
2026-03-29 09:28:49 +08:00
}
2025-01-27 13:06:08 +08:00
ffStrbufAppend(&cpu->name, &cpuIsa);
}
2025-01-22 22:19:23 +08:00
}
2026-03-29 09:28:49 +08:00
# elif __loongarch__
cpu->packages = getLoongarchPropCount(&cpuinfo, "\npackage\t\t\t:");
cpu->coresPhysical = getLoongarchPropCount(&cpuinfo, "\ncore\t\t\t:");
2026-03-29 09:28:49 +08:00
if (cpu->packages > 1) {
cpu->coresPhysical *= cpu->packages;
}
# elif __s390x__
if (cpu->name.length) {
ffStrbufPrependS(&cpu->name, "Machine ");
}
# endif
2025-01-22 22:19:23 +08:00
}
2023-09-27 15:11:05 +08:00
2026-03-29 09:28:49 +08:00
if (cpu->coresPhysical == 0) {
detectPhysicalCores(cpu);
2026-03-29 09:28:49 +08:00
}
2025-08-25 14:44:32 +08:00
ffCPUDetectByCpuid(cpu);
2025-11-17 20:15:56 +08:00
detectNumaNodes(cpu);
2025-08-25 14:44:32 +08:00
2023-06-12 09:56:03 +08:00
return NULL;
2022-09-04 22:20:12 +02:00
}
2025-01-27 13:06:08 +08:00
#endif
2026-03-29 09:28:49 +08:00
const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) {
2025-12-10 16:13:22 +08:00
cpu->temperature = options->temp ? detectCPUTemp(options) : FF_CPU_TEMP_UNSET;
2025-01-27 13:06:08 +08:00
2026-03-29 09:28:49 +08:00
#if __x86_64__ || __i386__
2025-01-27 13:06:08 +08:00
return detectCPUX86(options, cpu);
2026-03-29 09:28:49 +08:00
#else
2025-01-27 13:06:08 +08:00
return detectCPUOthers(options, cpu);
2026-03-29 09:28:49 +08:00
#endif
2025-01-27 13:06:08 +08:00
}