diff --git a/src/broadcaster.zig b/src/broadcaster.zig index 109a3fd..1356d33 100644 --- a/src/broadcaster.zig +++ b/src/broadcaster.zig @@ -628,6 +628,8 @@ pub fn formatPrometheusMetrics(stats: *const Stats, cache_entries: usize, migrat return fbs.getWritten(); } +const malloc_h = @cImport(@cInclude("malloc.h")); + fn appendProcMetrics(w: anytype) void { // RSS from /proc/self/statm (field[1] * page_size) if (std.fs.openFileAbsolute("/proc/self/statm", .{})) |f| { @@ -650,27 +652,56 @@ fn appendProcMetrics(w: anytype) void { } } else |_| {} - // thread count from /proc/self/status + // thread count + memory detail from /proc/self/status if (std.fs.openFileAbsolute("/proc/self/status", .{})) |f| { defer f.close(); var status_buf: [4096]u8 = undefined; const n = f.readAll(&status_buf) catch 0; if (n > 0) { const content = status_buf[0..n]; - if (std.mem.indexOf(u8, content, "Threads:")) |pos| { - const rest = content[pos + "Threads:".len ..]; - const trimmed = std.mem.trimLeft(u8, rest, " \t"); - const end = std.mem.indexOfScalar(u8, trimmed, '\n') orelse trimmed.len; - if (std.fmt.parseInt(u64, trimmed[0..end], 10)) |threads| { - std.fmt.format(w, - \\# TYPE relay_threads_total gauge - \\relay_threads_total {d} - \\ - , .{threads}) catch {}; - } else |_| {} + const fields = .{ + .{ "Threads:", "relay_threads_total" }, + .{ "VmHWM:", "relay_vm_hwm_kb" }, + .{ "RssAnon:", "relay_rss_anon_kb" }, + }; + inline for (fields) |entry| { + if (std.mem.indexOf(u8, content, entry[0])) |pos| { + const rest = content[pos + entry[0].len ..]; + const trimmed = std.mem.trimLeft(u8, rest, " \t"); + const end = std.mem.indexOfScalar(u8, trimmed, ' ') orelse + (std.mem.indexOfScalar(u8, trimmed, '\n') orelse trimmed.len); + if (std.fmt.parseInt(u64, trimmed[0..end], 10)) |val| { + std.fmt.format(w, + \\# TYPE {s} gauge + \\{s} {d} + \\ + , .{ entry[1], entry[1], val }) catch {}; + } else |_| {} + } } } } else |_| {} + + // glibc malloc arena stats — distinguishes in-use heap from fragmentation + const mi = malloc_h.mallinfo2(); + std.fmt.format(w, + \\# TYPE relay_malloc_arena_bytes gauge + \\# HELP relay_malloc_arena_bytes total bytes claimed from OS by malloc + \\relay_malloc_arena_bytes {d} + \\ + \\# TYPE relay_malloc_in_use_bytes gauge + \\# HELP relay_malloc_in_use_bytes bytes actively allocated (in-use heap) + \\relay_malloc_in_use_bytes {d} + \\ + \\# TYPE relay_malloc_free_bytes gauge + \\# HELP relay_malloc_free_bytes free bytes held by malloc (fragmentation) + \\relay_malloc_free_bytes {d} + \\ + \\# TYPE relay_malloc_mmap_bytes gauge + \\# HELP relay_malloc_mmap_bytes bytes allocated via mmap (large blocks) + \\relay_malloc_mmap_bytes {d} + \\ + , .{ mi.arena, mi.uordblks, mi.fordblks, mi.hblkhd }) catch {}; } const posix_vfs = @cImport(@cInclude("sys/statvfs.h"));