From d41ff62c57cc53a4f064e46d5415db9798f0b8cd Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 05:27:25 -0400 Subject: [PATCH 1/9] fix: Get meminfo from `/proc/meminfo` over `sysinfo()` --- core/sys/info/platform_linux.odin | 75 ++++++++++++++++++++++++++----- core/sys/info/sysinfo.odin | 2 +- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 7886b6243..9fc794296 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -2,6 +2,7 @@ package sysinfo import "base:intrinsics" import "base:runtime" +import "core:strconv" import "core:strings" import "core:sys/linux" @@ -79,17 +80,71 @@ _os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> ( } @(private) -_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { - // Retrieve RAM info using `sysinfo` - sys_info: linux.Sys_Info - errno := linux.sysinfo(&sys_info) - assert_contextless(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!") +_ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { + fd, errno := linux.open("/proc/meminfo", {}) + if errno != .NONE { + // This should never happen since something would be wrong with the system + // if /proc/meminfo wasn't able to be opened for any reason + return 0, 0, 0, 0, false + } - total_ram = i64(sys_info.totalram) * i64(sys_info.mem_unit) - free_ram = i64(sys_info.freeram) * i64(sys_info.mem_unit) - total_swap = i64(sys_info.totalswap) * i64(sys_info.mem_unit) - free_swap = i64(sys_info.freeswap) * i64(sys_info.mem_unit) - ok = true + defer linux.close(fd) + + // We need a relatively large size to store all the info + meminfo_buf: [4096]u8 + n, read_errno := linux.read(fd, meminfo_buf[:]) + if read_errno != .NONE { + return 0, 0, 0, 0, false + } + meminfo := string(meminfo_buf[:n]) + + // Stuff needed as a failsafe for later in the event MemAvailable is being broken + mem_free, buffers, cached, shmem, s_reclaimable: i64 + + for line in strings.split_lines_iterator(&meminfo) { + if len(line) == 0 do continue + + colon_idx := strings.index(line, ":") + if colon_idx < 0 do continue + + key := strings.trim_space(line[:colon_idx]) + value_str := strings.trim_space(strings.trim_suffix(line[colon_idx + 1:], "kB")) + + value, conv_ok := strconv.parse_i64(value_str, 10) + if !conv_ok do continue + + switch key { + case "MemTotal": + total_ram = value + case "MemFree": + mem_free = value + case "MemAvailable": + free_ram = value + case "SwapTotal": + total_swap = value + case "SwapFree": + free_swap = value + case "Buffers": + buffers = value + case "Cached": + cached = value + case "Shmem": + shmem = value + case "SReclaimable": + s_reclaimable = value + } + } + + if free_ram == 0 || free_ram > total_ram { + free_ram = mem_free + buffers + cached + s_reclaimable - shmem + } + + mem_unit :: 1024 + total_ram *= mem_unit + free_ram *= mem_unit + total_swap *= mem_unit + free_swap *= mem_unit + ok = true return } \ No newline at end of file diff --git a/core/sys/info/sysinfo.odin b/core/sys/info/sysinfo.odin index ad120b264..5be40a3a8 100644 --- a/core/sys/info/sysinfo.odin +++ b/core/sys/info/sysinfo.odin @@ -59,7 +59,7 @@ Returns: - free_swap: Free SWAP reported by the operating system, in bytes - ok: `true` when we could retrieve RAM statistics, `false` otherwise */ -ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { +ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { return _ram_stats() } From 55fd98194125a6406cc426330989663e2a768412 Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 12:09:46 -0400 Subject: [PATCH 2/9] refactor: Remove usage of `do` I knew there was something I was forgetting with code in the core packages. I wasn't applying the right arguments and style checks. Oops. --- core/sys/info/platform_linux.odin | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 9fc794296..191b71f05 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -102,16 +102,22 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo mem_free, buffers, cached, shmem, s_reclaimable: i64 for line in strings.split_lines_iterator(&meminfo) { - if len(line) == 0 do continue + if len(line) == 0 { + continue + } colon_idx := strings.index(line, ":") - if colon_idx < 0 do continue + if colon_idx < 0 { + continue + } key := strings.trim_space(line[:colon_idx]) value_str := strings.trim_space(strings.trim_suffix(line[colon_idx + 1:], "kB")) value, conv_ok := strconv.parse_i64(value_str, 10) - if !conv_ok do continue + if !conv_ok { + continue + } switch key { case "MemTotal": From 32de482025409492bf28037ab68ea591295a503d Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 16:44:31 -0400 Subject: [PATCH 3/9] docs: Add small comment to explain purpose of approach --- core/sys/info/platform_linux.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 191b71f05..e4617ca72 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -81,6 +81,8 @@ _os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> ( @(private) _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { + // The approach is to read /proc/meminfo for the memory information _over_ sysinfo(), + // since sysinfo() just returns MemFree over the value we actually want, MemAvailable. fd, errno := linux.open("/proc/meminfo", {}) if errno != .NONE { // This should never happen since something would be wrong with the system From 3d3aa45e02e127bb910ed49b07350eab760d7c1d Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 16:45:06 -0400 Subject: [PATCH 4/9] refactor: Update fallback to just return MemFree --- core/sys/info/platform_linux.odin | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index e4617ca72..7ef58cbb6 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -100,8 +100,8 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo } meminfo := string(meminfo_buf[:n]) - // Stuff needed as a failsafe for later in the event MemAvailable is being broken - mem_free, buffers, cached, shmem, s_reclaimable: i64 + // Fallback in the event MemAvailable is not found or is invalid in its value + mem_free: i64 for line in strings.split_lines_iterator(&meminfo) { if len(line) == 0 { @@ -132,19 +132,14 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo total_swap = value case "SwapFree": free_swap = value - case "Buffers": - buffers = value - case "Cached": - cached = value - case "Shmem": - shmem = value - case "SReclaimable": - s_reclaimable = value } } if free_ram == 0 || free_ram > total_ram { - free_ram = mem_free + buffers + cached + s_reclaimable - shmem + // We opt to return MemFree here if MemAvailable is not found or is broken to come degree. + // This will act as a predictable fallback, but shouldn't ever really occur unless the user + // is on Linux < 3.14 + free_ram = mem_free } mem_unit :: 1024 @@ -152,6 +147,7 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo free_ram *= mem_unit total_swap *= mem_unit free_swap *= mem_unit + ok = true return From 962f8dfd1b13f12d21013a55d820b93318ac84d9 Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 16:47:37 -0400 Subject: [PATCH 5/9] refactor: Add fallback to `sysinfo()` if `/proc/meminfo` can't be read This is likely to never _actually_ execute, and the possibility of this failing is extremely slim, but if this _does_ fail, then this fallback should catch it, as long as I'm understanding things correctly. If the fallback fails, we just go back to the original assert that existed before. --- core/sys/info/platform_linux.odin | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 7ef58cbb6..a693e92fc 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -86,8 +86,21 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo fd, errno := linux.open("/proc/meminfo", {}) if errno != .NONE { // This should never happen since something would be wrong with the system - // if /proc/meminfo wasn't able to be opened for any reason - return 0, 0, 0, 0, false + // if /proc/meminfo wasn't able to be opened for any reason. But, in the + // event that this _does_ happen, let's just try to recover through the + // syscall + sys_info: linux.Sys_Info + sysinfo_errno := linux.sysinfo(&sys_info) + assert_contextless(sysinfo_errno == .NONE, "If this has failed, there is no recovery from this") + + total_ram = i64(sys_info.totalram) * i64(sys_info.mem_unit) + free_ram = i64(sys_info.freeram) * i64(sys_info.mem_unit) + total_swap = i64(sys_info.totalswap) * i64(sys_info.mem_unit) + free_swap = i64(sys_info.freeswap) * i64(sys_info.mem_unit) + + ok = true + + return } defer linux.close(fd) @@ -96,7 +109,18 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo meminfo_buf: [4096]u8 n, read_errno := linux.read(fd, meminfo_buf[:]) if read_errno != .NONE { - return 0, 0, 0, 0, false + sys_info: linux.Sys_Info + sysinfo_errno := linux.sysinfo(&sys_info) + assert_contextless(sysinfo_errno == .NONE, "If this has failed, there is no recovery from this") + + total_ram = i64(sys_info.totalram) * i64(sys_info.mem_unit) + free_ram = i64(sys_info.freeram) * i64(sys_info.mem_unit) + total_swap = i64(sys_info.totalswap) * i64(sys_info.mem_unit) + free_swap = i64(sys_info.freeswap) * i64(sys_info.mem_unit) + + ok = true + + return } meminfo := string(meminfo_buf[:n]) From 26f824eb8e45352ba47d238dad0acf4f1d7568ca Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 16:52:58 -0400 Subject: [PATCH 6/9] docs: Updated comment to explain approach more accurately --- core/sys/info/platform_linux.odin | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index a693e92fc..b7a03efb8 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -81,8 +81,10 @@ _os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> ( @(private) _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { - // The approach is to read /proc/meminfo for the memory information _over_ sysinfo(), - // since sysinfo() just returns MemFree over the value we actually want, MemAvailable. + // The approach is to read /proc/meminfo for the memory information. We do this over + // reading sysinfo() since sysinfo() only returns MemFree, which is based on the amount + // of free pages. The value we actually want is MemAvailable inside meminfo since it is + // estimated around being about to evict things out of the page cache. fd, errno := linux.open("/proc/meminfo", {}) if errno != .NONE { // This should never happen since something would be wrong with the system From c9621748a43ece987ce75d09cd6650335cdf4cb4 Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Wed, 3 Jun 2026 16:55:28 -0400 Subject: [PATCH 7/9] typo oops --- core/sys/info/platform_linux.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index b7a03efb8..5901abd67 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -162,7 +162,7 @@ _ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bo } if free_ram == 0 || free_ram > total_ram { - // We opt to return MemFree here if MemAvailable is not found or is broken to come degree. + // We opt to return MemFree here if MemAvailable is not found or is broken to some degree. // This will act as a predictable fallback, but shouldn't ever really occur unless the user // is on Linux < 3.14 free_ram = mem_free From 9942625c63e6757b9a67d12e59726a46df86734e Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Thu, 4 Jun 2026 13:17:42 -0400 Subject: [PATCH 8/9] refactor: Use `default_context()` over removing `"contextless"` --- core/sys/info/platform_linux.odin | 5 ++++- core/sys/info/sysinfo.odin | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 5901abd67..ed476a9cd 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -80,7 +80,10 @@ _os_version :: proc (allocator: runtime.Allocator, loc := #caller_location) -> ( } @(private) -_ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { +_ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { + // This is here for some of the strings procedures + context = runtime.default_context() + // The approach is to read /proc/meminfo for the memory information. We do this over // reading sysinfo() since sysinfo() only returns MemFree, which is based on the amount // of free pages. The value we actually want is MemAvailable inside meminfo since it is diff --git a/core/sys/info/sysinfo.odin b/core/sys/info/sysinfo.odin index 5be40a3a8..ad120b264 100644 --- a/core/sys/info/sysinfo.odin +++ b/core/sys/info/sysinfo.odin @@ -59,7 +59,7 @@ Returns: - free_swap: Free SWAP reported by the operating system, in bytes - ok: `true` when we could retrieve RAM statistics, `false` otherwise */ -ram_stats :: proc() -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { +ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_swap: i64, ok: bool) { return _ram_stats() } From 05f053996050f7b69e16f8b9a4c4c63f19bf0f41 Mon Sep 17 00:00:00 2001 From: "Alexander Cusaac (MightyChubz)" Date: Thu, 4 Jun 2026 13:21:27 -0400 Subject: [PATCH 9/9] refactor: Move `mem_unit` before loop and apply it in switch statement --- core/sys/info/platform_linux.odin | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index ed476a9cd..eef77afa0 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -132,6 +132,7 @@ _ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_sw // Fallback in the event MemAvailable is not found or is invalid in its value mem_free: i64 + mem_unit :: 1024 for line in strings.split_lines_iterator(&meminfo) { if len(line) == 0 { continue @@ -152,15 +153,15 @@ _ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_sw switch key { case "MemTotal": - total_ram = value + total_ram = value * mem_unit case "MemFree": - mem_free = value + mem_free = value * mem_unit case "MemAvailable": - free_ram = value + free_ram = value * mem_unit case "SwapTotal": - total_swap = value + total_swap = value * mem_unit case "SwapFree": - free_swap = value + free_swap = value * mem_unit } } @@ -171,12 +172,6 @@ _ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_sw free_ram = mem_free } - mem_unit :: 1024 - total_ram *= mem_unit - free_ram *= mem_unit - total_swap *= mem_unit - free_swap *= mem_unit - ok = true return