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}