master ohmu/fastfetch-openbsd / src / detection / cpucache / cpucache_linux.c
ohmu  ·  2026-10-15
  1#include "cpucache.h"
  2#include "common/debug.h"
  3#include "common/io.h"
  4#include "common/strutil.h"
  5
  6#include <errno.h>
  7#include <string.h>
  8
  9static const char* parseCpuCacheIndex(FFstrbuf* path, FFCPUCacheResult* result, FFstrbuf* buffer, FFstrbuf* added) {
 10    uint32_t baseLen = path->length;
 11    ffStrbufAppendS(path, "/level");
 12    if (!ffReadFileBuffer(path->chars, buffer)) {
 13        FF_DEBUG("Failed to read \"%s\"", path->chars);
 14        return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/level\") == nullptr";
 15    }
 16
 17    uint32_t level = (uint32_t) ffStrbufToUInt(buffer, 0);
 18    if (level < 1 || level > 4) {
 19        return "level < 1 || level > 4";
 20    }
 21
 22    ffStrbufSubstrBefore(path, baseLen);
 23    ffStrbufAppendS(path, "/size");
 24    if (!ffReadFileBuffer(path->chars, buffer)) {
 25        FF_DEBUG("Failed to read \"%s\"", path->chars);
 26        return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/size\") == nullptr";
 27    }
 28
 29    uint32_t sizeKb = (uint32_t) ffStrbufToUInt(buffer, 0);
 30    if (sizeKb == 0) {
 31        return "size == 0";
 32    }
 33
 34    ffStrbufSubstrBefore(path, baseLen);
 35    ffStrbufAppendS(path, "/type");
 36    if (!ffReadFileBuffer(path->chars, buffer)) {
 37        FF_DEBUG("Failed to read \"%s\"", path->chars);
 38        return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/type\") == nullptr";
 39    }
 40    ffStrbufTrimRightSpace(buffer);
 41
 42    FFCPUCacheType cacheType = 0;
 43    switch (buffer->chars[0]) {
 44        case 'I':
 45            cacheType = FF_CPU_CACHE_TYPE_INSTRUCTION;
 46            break;
 47        case 'D':
 48            cacheType = FF_CPU_CACHE_TYPE_DATA;
 49            break;
 50        case 'U':
 51            cacheType = FF_CPU_CACHE_TYPE_UNIFIED;
 52            break;
 53        case 'T':
 54            cacheType = FF_CPU_CACHE_TYPE_TRACE;
 55            break;
 56        default:
 57            FF_DEBUG("Unknown cache type \"%c\"", buffer->chars[0]);
 58            return "unknown cache type";
 59    }
 60
 61    uint32_t lineSize = 0;
 62    ffStrbufSubstrBefore(path, baseLen);
 63    ffStrbufAppendS(path, "/coherency_line_size");
 64    if (ffReadFileBuffer(path->chars, buffer)) {
 65        lineSize = (uint32_t) ffStrbufToUInt(buffer, 0);
 66    }
 67
 68    ffStrbufSubstrBefore(path, baseLen);
 69    ffStrbufAppendS(path, "/shared_cpu_list");
 70    ffStrbufClear(buffer);
 71    ffStrbufAppendC(buffer, '[');
 72    if (!ffAppendFileBuffer(path->chars, buffer)) {
 73        FF_DEBUG("Failed to read \"%s\"", path->chars);
 74        return "ffAppendFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == nullptr";
 75    }
 76    ffStrbufTrimRightSpace(buffer);
 77
 78    // deduplicate shared caches
 79    ffStrbufAppendF(buffer, "_%u_%u_%u_%u]", level, sizeKb, lineSize, cacheType);
 80
 81    if (ffStrbufContain(added, buffer)) {
 82        return nullptr;
 83    }
 84    ffStrbufAppend(added, buffer);
 85    ffCPUCacheAddItem(result, level, sizeKb * 1024, lineSize, cacheType);
 86    return nullptr;
 87}
 88
 89static const char* parseCpuCache(FFstrbuf* path, FFCPUCacheResult* result, FFstrbuf* buffer, FFstrbuf* added) {
 90    ffStrbufAppendS(path, "/cache/");
 91    uint32_t baseLen = path->length;
 92    FF_AUTO_CLOSE_DIR DIR* pathCacheDir = opendir(path->chars);
 93    if (!pathCacheDir) {
 94        FF_DEBUG("opendir(\"%s\") failed: %s", path->chars, strerror(errno));
 95        return "opendir(\"/sys/devices/system/cpu/cpuX/cache/\") == nullptr";
 96    }
 97
 98    struct dirent* pathCacheEntry;
 99    while ((pathCacheEntry = readdir(pathCacheDir)) != nullptr) {
100        if (!ffStrStartsWith(pathCacheEntry->d_name, "index") || !ffCharIsDigit(pathCacheEntry->d_name[strlen("index")])) {
101            continue;
102        }
103
104        ffStrbufAppendS(path, pathCacheEntry->d_name);
105        const char* error = parseCpuCacheIndex(path, result, buffer, added);
106        if (error) {
107            return error;
108        }
109        ffStrbufSubstrBefore(path, baseLen);
110    }
111
112    return nullptr;
113}
114
115const char* ffDetectCPUCache(FFCPUCacheResult* result) {
116    // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
117    FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/sys/devices/system/cpu/");
118    uint32_t baseLen = path.length;
119    FF_AUTO_CLOSE_DIR DIR* pathCpuDir = opendir(path.chars);
120    if (!pathCpuDir) {
121        FF_DEBUG("opendir(\"%s\") failed: %s", path.chars, strerror(errno));
122        return "opendir(\"/sys/devices/system/cpu/\") == nullptr";
123    }
124
125    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
126    FF_STRBUF_AUTO_DESTROY added = ffStrbufCreate();
127
128    struct dirent* pathCpuEntry;
129    while ((pathCpuEntry = readdir(pathCpuDir)) != nullptr) {
130        if (!ffStrStartsWith(pathCpuEntry->d_name, "cpu") ||
131            !ffCharIsDigit(pathCpuEntry->d_name[strlen("cpu")])) {
132            continue;
133        }
134
135        ffStrbufAppendS(&path, pathCpuEntry->d_name);
136        const char* error = parseCpuCache(&path, result, &buffer, &added);
137        if (error) {
138            return error;
139        }
140        ffStrbufSubstrBefore(&path, baseLen);
141    }
142    return nullptr;
143}