master
c 330 lines 9.42 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 static OS_SYSTEM_MEMORY os_system_memory_last = {
6 0, 0,
7 };
8
9 OS_SYSTEM_MEMORY os_last_reported_system_memory(void) {
10 return os_system_memory_last;
11 }
12
13 // Windows
14 #if defined(OS_WINDOWS)
15 #include <windows.h>
16
17 OS_SYSTEM_MEMORY os_system_memory(bool query_total_ram __maybe_unused) {
18 OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
19
20 MEMORYSTATUSEX statex;
21 statex.dwLength = sizeof(statex);
22 if (GlobalMemoryStatusEx(&statex)) {
23 sm.ram_total_bytes = statex.ullTotalPhys;
24 sm.ram_available_bytes = statex.ullAvailPhys;
25 os_system_memory_last = sm;
26 }
27
28 return sm;
29 }
30 #endif
31
32 // Linux
33 #if defined(OS_LINUX)
34
35 static OS_SYSTEM_MEMORY os_system_memory_cgroup_v1(bool query_total_ram __maybe_unused) {
36 static OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
37 char buf[4096];
38 uint64_t used = 0, inactive = 0;
39
40 if(query_total_ram || sm.ram_total_bytes == 0) {
41 if (read_txt_file("/sys/fs/cgroup/memory/memory.limit_in_bytes", buf, sizeof(buf)) != 0)
42 goto failed;
43
44 sm.ram_total_bytes = str2ull(buf, NULL);
45 if(!sm.ram_total_bytes)
46 goto failed;
47 }
48
49 buf[0] = '\0';
50 if (read_txt_file("/sys/fs/cgroup/memory/memory.usage_in_bytes", buf, sizeof(buf)) != 0)
51 goto failed;
52
53 used = str2ull(buf, NULL);
54 if(!used || used > sm.ram_total_bytes)
55 goto failed;
56
57 if (read_txt_file("/sys/fs/cgroup/memory/memory.stat", buf, sizeof(buf)) != 0)
58 goto done;
59
60 const char *inactive_str = strstr(buf, "total_inactive_file ");
61 if(!inactive_str)
62 goto done;
63
64 inactive_str += 20;
65
66 inactive = str2ull(inactive_str, NULL);
67 if(!inactive || inactive > used) {
68 inactive = 0;
69 goto done;
70 }
71
72 done:
73 sm.ram_available_bytes = sm.ram_total_bytes - (used - inactive);
74 return sm;
75
76 failed:
77 sm = OS_SYSTEM_MEMORY_EMPTY;
78 return sm;
79 }
80
81 static OS_SYSTEM_MEMORY os_system_memory_cgroup_v2(bool query_total_ram __maybe_unused) {
82 static OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
83 char buf[4096];
84 uint64_t used = 0, inactive = 0;
85
86 if(query_total_ram || sm.ram_total_bytes == 0) {
87 if (read_txt_file("/sys/fs/cgroup/memory.max", buf, sizeof(buf)) != 0)
88 goto failed;
89
90 if(strcmp(buf, "max") == 0)
91 sm.ram_total_bytes = UINT64_MAX;
92 else
93 sm.ram_total_bytes = str2ull(buf, NULL);
94
95 if(!sm.ram_total_bytes)
96 goto failed;
97 }
98
99 buf[0] = '\0';
100 if (read_txt_file("/sys/fs/cgroup/memory.current", buf, sizeof(buf)) != 0)
101 goto failed;
102
103 used = str2ull(buf, NULL);
104 if(!used || used > sm.ram_total_bytes)
105 goto failed;
106
107 if (read_txt_file("/sys/fs/cgroup/memory.stat", buf, sizeof(buf)) != 0)
108 goto done;
109
110 const char *inactive_str = strstr(buf, "inactive_file ");
111 if(!inactive_str)
112 goto done;
113
114 inactive_str += 14;
115
116 inactive = str2ull(inactive_str, NULL);
117 if(!inactive || inactive > used) {
118 inactive = 0;
119 goto done;
120 }
121
122 done:
123 sm.ram_available_bytes = sm.ram_total_bytes - (used - inactive);
124 return sm;
125
126 failed:
127 sm = OS_SYSTEM_MEMORY_EMPTY;
128 return sm;
129 }
130
131 #define MEMINFO_MEMTOTAL "MemTotal:"
132 #define MEMINFO_MEMAVAILABLE "MemAvailable:"
133
134 static OS_SYSTEM_MEMORY os_system_memory_meminfo(bool query_total_ram __maybe_unused) {
135 static OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
136
137 char buf[4096];
138 if (read_txt_file("/proc/meminfo", buf, sizeof(buf)) != 0)
139 goto failed;
140
141 char *s = strstr(buf, MEMINFO_MEMTOTAL);
142 if(!s) goto failed;
143 s += sizeof(MEMINFO_MEMTOTAL) - 1;
144 while(isspace((uint8_t)*s)) s++;
145 sm.ram_total_bytes = str2ull(s, NULL) * 1024;
146
147 s = strstr(buf, MEMINFO_MEMAVAILABLE);
148 if(!s) goto failed;
149 s += sizeof(MEMINFO_MEMAVAILABLE) - 1;
150 while(isspace((uint8_t)*s)) s++;
151 sm.ram_available_bytes = str2ull(s, NULL) * 1024;
152
153 return sm;
154
155 failed:
156 sm.ram_total_bytes = 0;
157 sm.ram_available_bytes = 0;
158 return sm;
159 }
160
161 typedef enum {
162 OS_MEM_SRC_UNKNOWN,
163 OS_MEM_SRC_CGROUP_V1,
164 OS_MEM_SRC_CGROUP_V2,
165 OS_MEM_SRC_MEMINFO,
166 } OS_MEM_SRC;
167
168 OS_SYSTEM_MEMORY os_system_memory(bool query_total_ram __maybe_unused) {
169 static OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
170 static usec_t last_ut = 0, last_total_ut = 0;
171 static OS_MEM_SRC src = OS_MEM_SRC_UNKNOWN;
172
173 usec_t now_ut = now_monotonic_usec();
174 if(sm.ram_total_bytes && sm.ram_available_bytes && last_ut + USEC_PER_MS > now_ut)
175 return sm;
176
177 last_ut = now_ut;
178
179 if(query_total_ram)
180 // let it auto-detect
181 src = OS_MEM_SRC_UNKNOWN;
182
183 if(last_total_ut + USEC_PER_SEC > now_ut)
184 // query also the total ram
185 query_total_ram = true;
186
187 switch(src) {
188 case OS_MEM_SRC_MEMINFO:
189 sm = os_system_memory_meminfo(query_total_ram);
190 break;
191
192 case OS_MEM_SRC_CGROUP_V2:
193 sm = os_system_memory_cgroup_v2(query_total_ram);
194 break;
195
196 case OS_MEM_SRC_CGROUP_V1:
197 sm = os_system_memory_cgroup_v1(query_total_ram);
198 break;
199
200 default:
201 case OS_MEM_SRC_UNKNOWN: {
202 OS_SYSTEM_MEMORY mi = os_system_memory_meminfo(true);
203 OS_SYSTEM_MEMORY v1 = os_system_memory_cgroup_v1(true);
204 OS_SYSTEM_MEMORY v2 = os_system_memory_cgroup_v2(true);
205
206 if(v2.ram_total_bytes && v2.ram_available_bytes && v2.ram_total_bytes <= mi.ram_total_bytes && v2.ram_available_bytes < mi.ram_available_bytes) {
207 sm = v2;
208 src = OS_MEM_SRC_CGROUP_V2;
209 }
210 else {
211 // if(v2.ram_total_bytes || v2.ram_available_bytes)
212 // nd_log(NDLS_DAEMON, NDLP_ERR, "SYSTEM_MEMORY: cgroup v2 reports more memory than meminfo. Ignoring cgroup v2.");
213
214 if (v1.ram_total_bytes && v1.ram_available_bytes && v1.ram_total_bytes <= mi.ram_total_bytes &&
215 v1.ram_available_bytes < mi.ram_available_bytes) {
216 sm = v1;
217 src = OS_MEM_SRC_CGROUP_V1;
218 }
219 else {
220 // if(v1.ram_total_bytes || v1.ram_available_bytes)
221 // nd_log(NDLS_DAEMON, NDLP_ERR, "SYSTEM_MEMORY: cgroup v1 reports more memory than meminfo. Ignoring cgroup v1.");
222
223 sm = mi;
224 src = OS_MEM_SRC_MEMINFO;
225 }
226 }
227 }
228 }
229
230 os_system_memory_last = sm;
231 return sm;
232 }
233 #endif
234
235 // FreeBSD
236 #if defined(OS_FREEBSD)
237 #include <sys/types.h>
238 #include <sys/sysctl.h>
239
240 OS_SYSTEM_MEMORY os_system_memory(bool query_total_ram) {
241 static OS_SYSTEM_MEMORY sm = OS_SYSTEM_MEMORY_EMPTY;
242
243 // Query the total RAM only if needed or if it hasn't been cached
244 if (query_total_ram || sm.ram_total_bytes == 0) {
245 uint64_t total_pages = 0;
246 size_t size = sizeof(total_pages);
247 if (sysctlbyname("vm.stats.vm.v_page_count", &total_pages, &size, NULL, 0) != 0)
248 goto failed;
249
250 unsigned long page_size = 0;
251 size = sizeof(page_size);
252 if (sysctlbyname("hw.pagesize", &page_size, &size, NULL, 0) != 0)
253 goto failed;
254
255 sm.ram_total_bytes = total_pages * page_size;
256 }
257
258 // Query the available RAM (free + inactive pages)
259 uint64_t free_pages = 0, inactive_pages = 0;
260 size_t size = sizeof(free_pages);
261 if (sysctlbyname("vm.stats.vm.v_free_count", &free_pages, &size, NULL, 0) != 0 ||
262 sysctlbyname("vm.stats.vm.v_inactive_count", &inactive_pages, &size, NULL, 0) != 0)
263 goto failed;
264
265 unsigned long page_size = 0;
266 size = sizeof(page_size);
267 if (sysctlbyname("hw.pagesize", &page_size, &size, NULL, 0) != 0)
268 goto failed;
269
270 sm.ram_available_bytes = (free_pages + inactive_pages) * page_size;
271
272 os_system_memory_last = sm;
273 return sm;
274
275 failed:
276 sm = OS_SYSTEM_MEMORY_EMPTY;
277 return sm;
278 }
279 #endif
280
281 // macOS
282 #if defined(OS_MACOS)
283 #include <mach/mach.h>
284 #include <sys/sysctl.h>
285
286 OS_SYSTEM_MEMORY os_system_memory(bool query_total_ram) {
287 static uint64_t total_ram = 0;
288 static uint64_t page_size = 0;
289
290 if (page_size == 0) {
291 size_t len = sizeof(page_size);
292 if (sysctlbyname("hw.pagesize", &page_size, &len, NULL, 0) != 0)
293 return OS_SYSTEM_MEMORY_EMPTY;
294 }
295
296 if (query_total_ram || total_ram == 0) {
297 size_t len = sizeof(total_ram);
298 if (sysctlbyname("hw.memsize", &total_ram, &len, NULL, 0) != 0)
299 return OS_SYSTEM_MEMORY_EMPTY;
300 }
301
302 uint64_t ram_available = 0;
303 if (page_size > 0) {
304 vm_statistics64_data_t vm_info;
305 mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
306 mach_port_t mach_port = mach_host_self();
307
308 if (host_statistics64(mach_port, HOST_VM_INFO64, (host_info_t)&vm_info, &count) != KERN_SUCCESS) {
309 mach_port_deallocate(mach_task_self(), mach_port);
310 return OS_SYSTEM_MEMORY_EMPTY;
311 }
312
313 ram_available = (vm_info.free_count + vm_info.inactive_count + vm_info.purgeable_count) * page_size;
314 mach_port_deallocate(mach_task_self(), mach_port);
315 }
316
317 os_system_memory_last = (OS_SYSTEM_MEMORY){
318 .ram_total_bytes = total_ram,
319 .ram_available_bytes = ram_available,
320 };
321 return os_system_memory_last;
322 }
323 #endif
324
325 double os_system_memory_available_percent(OS_SYSTEM_MEMORY mem) {
326 if (!OS_SYSTEM_MEMORY_OK(mem))
327 return 100.0;
328
329 return 100.0 * (double)mem.ram_available_bytes / (double)mem.ram_total_bytes;
330 }