lib/sys/src/host.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 //! Host-wide resource observations, each read from the operating system at the
  2 //! moment of the call.
  3 //!
  4 //! `memory` reports available memory together with a pressure level, while
  5 //! `availableMemoryBytes` reports the byte count alone. On Linux *available
  6 //! memory*, the byte count the host reports as allocatable without swapping,
  7 //! comes from `MemAvailable` in `/proc/meminfo`, and pressure always reports
  8 //! normal. When the process belongs to a control group with a memory limit, the
  9 //! call reports the smaller of the host number and the group's *headroom*, the
 10 //! limit minus current usage, taken across every enclosing group and across
 11 //! both the unified and the version one hierarchies. On Darwin available memory
 12 //! comes from the percent-free level and the total system memory, and
 13 //! *pressure*, the Darwin memory pressure level reported as normal or
 14 //! pressured, comes from a sysctl.
 15 //!
 16 //! A declared control group membership whose mount is missing, and a mount
 17 //! whose memory files cannot be read, both fail with
 18 //! `CgroupTelemetryUnavailable`, so a limit that cannot be read never passes as
 19 //! unlimited headroom. Every file is read into fixed storage, and a file that
 20 //! fills its storage fails with `BufferTooSmall`. Every other platform returns
 21 //! `UnsupportedPlatform`.
 22 
 23 const std = @import("std");
 24 const builtin = @import("builtin");
 25 const capabilities = @import("capabilities.zig");
 26 
 27 pub const required_capabilities = capabilities.host(&.{
 28     .filesystem,
 29     .platform_frameworks,
 30 });
 31 
 32 const meminfo_path = "/proc/meminfo";
 33 const cgroup_path = "/proc/self/cgroup";
 34 const mountinfo_path = "/proc/self/mountinfo";
 35 const meminfo_bytes_max: usize = 16 * 1024;
 36 const cgroup_bytes_max: usize = 16 * 1024;
 37 const mountinfo_bytes_max: usize = 128 * 1024;
 38 const cgroup_value_bytes_max: usize = 256;
 39 
 40 pub const Pressure = enum {
 41     normal,
 42     pressured,
 43 };
 44 
 45 pub const Memory = struct {
 46     available_bytes: u64,
 47     pressure: Pressure,
 48 };
 49 
 50 const CgroupMembership = struct {
 51     v2: ?[]const u8 = null,
 52     v1_memory: ?[]const u8 = null,
 53 };
 54 
 55 const CgroupMount = struct {
 56     root: []const u8,
 57     point: []const u8,
 58 };
 59 
 60 const CgroupMounts = struct {
 61     v2: ?CgroupMount = null,
 62     v1_memory: ?CgroupMount = null,
 63 };
 64 
 65 const HierarchyHeadroom = struct {
 66     bytes: ?u64 = null,
 67 };
 68 
 69 const MemoryLimit = union(enum) {
 70     unlimited,
 71     finite: u64,
 72 };
 73 
 74 extern "c" fn memorystatus_get_level(level_address: usize) c_int;
 75 
 76 pub fn memory(io: std.Io) !Memory {
 77     return switch (comptime builtin.os.tag) {
 78         .linux => linuxMemory(io),
 79         .driverkit,
 80         .ios,
 81         .maccatalyst,
 82         .macos,
 83         .tvos,
 84         .visionos,
 85         .watchos,
 86         => darwinMemory(),
 87         else => error.UnsupportedPlatform,
 88     };
 89 }
 90 
 91 pub fn availableMemoryBytes(io: std.Io) !u64 {
 92     return (try memory(io)).available_bytes;
 93 }
 94 
 95 fn linuxMemory(io: std.Io) !Memory {
 96     const host_available = try linuxHostAvailableMemoryBytes(io);
 97     const cgroup_available = try linuxCgroupHeadroom(io);
 98     return .{
 99         .available_bytes = if (cgroup_available) |available|
100             @min(host_available, available)
101         else
102             host_available,
103         .pressure = .normal,
104     };
105 }
106 
107 fn linuxHostAvailableMemoryBytes(io: std.Io) !u64 {
108     var storage: [meminfo_bytes_max]u8 = undefined;
109     return parseLinuxAvailableMemory(try readBounded(io, meminfo_path, &storage));
110 }
111 
112 fn linuxCgroupHeadroom(io: std.Io) !?u64 {
113     var membership_storage: [cgroup_bytes_max]u8 = undefined;
114     const membership = try parseCgroupMembership(try readBounded(
115         io,
116         cgroup_path,
117         &membership_storage,
118     ));
119     var mountinfo_storage: [mountinfo_bytes_max]u8 = undefined;
120     const mounts = try parseCgroupMounts(try readBounded(
121         io,
122         mountinfo_path,
123         &mountinfo_storage,
124     ));
125     return cgroupHeadroom(io, membership, mounts);
126 }
127 
128 fn cgroupHeadroom(
129     io: std.Io,
130     membership: CgroupMembership,
131     mounts: CgroupMounts,
132 ) !?u64 {
133     var result: ?u64 = null;
134     if (membership.v2) |group| {
135         const mount = mounts.v2 orelse return error.CgroupTelemetryUnavailable;
136         if (try cgroupMountHeadroom(
137             io,
138             group,
139             mount,
140             "memory.max",
141             "memory.current",
142             true,
143         )) |headroom| result = @min(result orelse headroom, headroom);
144     }
145     if (membership.v1_memory) |group| {
146         const mount = mounts.v1_memory orelse return error.CgroupTelemetryUnavailable;
147         if (try cgroupMountHeadroom(
148             io,
149             group,
150             mount,
151             "memory.limit_in_bytes",
152             "memory.usage_in_bytes",
153             false,
154         )) |headroom| result = @min(result orelse headroom, headroom);
155     }
156     return result;
157 }
158 
159 fn parseCgroupMounts(text: []const u8) !CgroupMounts {
160     var result: CgroupMounts = .{};
161     var lines = std.mem.splitScalar(u8, text, '\n');
162     while (lines.next()) |line| {
163         if (line.len == 0) continue;
164         const separator = std.mem.indexOf(u8, line, " - ") orelse
165             return error.InvalidData;
166         var mounted = std.mem.tokenizeScalar(u8, line[0..separator], ' ');
167         _ = mounted.next() orelse return error.InvalidData;
168         _ = mounted.next() orelse return error.InvalidData;
169         _ = mounted.next() orelse return error.InvalidData;
170         const root = mounted.next() orelse return error.InvalidData;
171         const mount_point = mounted.next() orelse return error.InvalidData;
172         var filesystem = std.mem.tokenizeScalar(u8, line[separator + 3 ..], ' ');
173         const kind = filesystem.next() orelse return error.InvalidData;
174         _ = filesystem.next() orelse return error.InvalidData;
175         const options = filesystem.next() orelse return error.InvalidData;
176         if (std.mem.eql(u8, kind, "cgroup2")) {
177             try validateCgroupMount(root, mount_point);
178             if (result.v2 != null) return error.InvalidData;
179             result.v2 = .{ .root = root, .point = mount_point };
180         } else if (std.mem.eql(u8, kind, "cgroup") and
181             controllerListed(options, "memory"))
182         {
183             try validateCgroupMount(root, mount_point);
184             if (result.v1_memory != null) return error.InvalidData;
185             result.v1_memory = .{ .root = root, .point = mount_point };
186         }
187     }
188     return result;
189 }
190 
191 fn validateCgroupMount(root: []const u8, point: []const u8) !void {
192     if (!validCgroupGroup(root) or
193         !std.fs.path.isAbsolute(point) or
194         std.mem.indexOfScalar(u8, point, '\\') != null)
195     {
196         return error.InvalidData;
197     }
198 }
199 
200 fn cgroupMountHeadroom(
201     io: std.Io,
202     group: []const u8,
203     mount: CgroupMount,
204     limit_name: []const u8,
205     current_name: []const u8,
206     allow_inactive_v2: bool,
207 ) !?u64 {
208     const relative_group = try mountedCgroup(mount.root, group);
209     const hierarchy = try cgroupHierarchyHeadroom(
210         io,
211         mount.point,
212         relative_group,
213         limit_name,
214         current_name,
215     );
216     if (hierarchy) |observed| return observed.bytes;
217     if (!allow_inactive_v2 or !std.mem.eql(u8, mount.root, "/")) {
218         return error.CgroupTelemetryUnavailable;
219     }
220     const inactive = v2MemoryControllerInactive(
221         io,
222         mount.point,
223         relative_group,
224     ) catch return error.CgroupTelemetryUnavailable;
225     if (!inactive) return error.CgroupTelemetryUnavailable;
226     return null;
227 }
228 
229 fn mountedCgroup(root: []const u8, group: []const u8) ![]const u8 {
230     if (!validCgroupGroup(root) or !validCgroupGroup(group)) {
231         return error.InvalidData;
232     }
233     if (std.mem.eql(u8, root, "/")) return group;
234     if (!std.mem.startsWith(u8, group, root)) {
235         return error.CgroupTelemetryUnavailable;
236     }
237     if (group.len == root.len) return "/";
238     if (group[root.len] != '/') return error.CgroupTelemetryUnavailable;
239     return group[root.len..];
240 }
241 
242 fn v2MemoryControllerInactive(
243     io: std.Io,
244     base: []const u8,
245     group: []const u8,
246 ) !bool {
247     if (!validCgroupGroup(group)) return error.InvalidData;
248     var controllers_path_storage: [std.fs.max_path_bytes]u8 = undefined;
249     const controllers_path = try hierarchyPath(
250         &controllers_path_storage,
251         base,
252         "",
253         "cgroup.controllers",
254     );
255     var controllers_storage: [cgroup_value_bytes_max]u8 = undefined;
256     if (!wordListed(
257         try readBounded(io, controllers_path, &controllers_storage),
258         "memory",
259     )) return true;
260     var child = std.mem.trim(u8, group, "/");
261     while (cgroupParent(child)) |parent| {
262         var path_storage: [std.fs.max_path_bytes]u8 = undefined;
263         const path = try hierarchyPath(
264             &path_storage,
265             base,
266             parent,
267             "cgroup.subtree_control",
268         );
269         var value_storage: [cgroup_value_bytes_max]u8 = undefined;
270         if (wordListed(try readBounded(io, path, &value_storage), "memory")) {
271             return false;
272         }
273         child = parent;
274     }
275     return true;
276 }
277 
278 fn cgroupHierarchyHeadroom(
279     io: std.Io,
280     base: []const u8,
281     group: []const u8,
282     limit_name: []const u8,
283     current_name: []const u8,
284 ) !?HierarchyHeadroom {
285     if (!validCgroupGroup(group)) return error.InvalidData;
286     var result: HierarchyHeadroom = .{};
287     var observed = false;
288     var current = std.mem.trim(u8, group, "/");
289     while (true) {
290         var limit_path_storage: [std.fs.max_path_bytes]u8 = undefined;
291         const limit_path = try hierarchyPath(
292             &limit_path_storage,
293             base,
294             current,
295             limit_name,
296         );
297         var limit_storage: [cgroup_value_bytes_max]u8 = undefined;
298         const limit_text = readBounded(io, limit_path, &limit_storage) catch |err| switch (err) {
299             error.FileNotFound => {
300                 current = cgroupParent(current) orelse break;
301                 continue;
302             },
303             else => return err,
304         };
305         observed = true;
306         switch (try parseMemoryLimit(limit_text)) {
307             .unlimited => {},
308             .finite => |limit| {
309                 var current_path_storage: [std.fs.max_path_bytes]u8 = undefined;
310                 const current_path = try hierarchyPath(
311                     &current_path_storage,
312                     base,
313                     current,
314                     current_name,
315                 );
316                 var current_storage: [cgroup_value_bytes_max]u8 = undefined;
317                 const usage = try parseMemoryCurrent(try readBounded(
318                     io,
319                     current_path,
320                     &current_storage,
321                 ));
322                 const headroom = remainingMemory(limit, usage);
323                 result.bytes = @min(result.bytes orelse headroom, headroom);
324             },
325         }
326         current = cgroupParent(current) orelse break;
327     }
328     return if (observed) result else null;
329 }
330 
331 fn parseCgroupMembership(text: []const u8) !CgroupMembership {
332     var result: CgroupMembership = .{};
333     var lines = std.mem.splitScalar(u8, text, '\n');
334     while (lines.next()) |line| {
335         if (line.len == 0) continue;
336         var fields = std.mem.splitScalar(u8, line, ':');
337         const hierarchy = fields.next() orelse return error.InvalidData;
338         const controllers = fields.next() orelse return error.InvalidData;
339         const group = fields.next() orelse return error.InvalidData;
340         if (fields.next() != null or !validCgroupGroup(group)) return error.InvalidData;
341         if (std.mem.eql(u8, hierarchy, "0") and controllers.len == 0) {
342             if (result.v2 != null) return error.InvalidData;
343             result.v2 = group;
344         } else if (controllerListed(controllers, "memory")) {
345             if (result.v1_memory != null) return error.InvalidData;
346             result.v1_memory = group;
347         }
348     }
349     return result;
350 }
351 
352 fn controllerListed(controllers: []const u8, expected: []const u8) bool {
353     var values = std.mem.splitScalar(u8, controllers, ',');
354     while (values.next()) |value| {
355         if (std.mem.eql(u8, value, expected)) return true;
356     }
357     return false;
358 }
359 
360 fn wordListed(words: []const u8, expected: []const u8) bool {
361     var values = std.mem.tokenizeAny(u8, words, " \t\r\n");
362     while (values.next()) |value| {
363         if (std.mem.eql(u8, value, expected)) return true;
364     }
365     return false;
366 }
367 
368 fn validCgroupGroup(group: []const u8) bool {
369     if (group.len == 0 or group[0] != '/') return false;
370     var components = std.mem.splitScalar(u8, group[1..], '/');
371     while (components.next()) |component| {
372         if (component.len == 0) return group.len == 1;
373         if (std.mem.eql(u8, component, ".") or std.mem.eql(u8, component, "..")) {
374             return false;
375         }
376     }
377     return true;
378 }
379 
380 fn hierarchyPath(
381     storage: []u8,
382     base: []const u8,
383     group: []const u8,
384     name: []const u8,
385 ) ![]const u8 {
386     return if (group.len == 0)
387         std.fmt.bufPrint(storage, "{s}/{s}", .{ base, name })
388     else
389         std.fmt.bufPrint(storage, "{s}/{s}/{s}", .{ base, group, name });
390 }
391 
392 fn cgroupParent(group: []const u8) ?[]const u8 {
393     if (group.len == 0) return null;
394     const separator = std.mem.lastIndexOfScalar(u8, group, '/') orelse return "";
395     return group[0..separator];
396 }
397 
398 fn parseMemoryLimit(text: []const u8) !MemoryLimit {
399     const value = std.mem.trim(u8, text, " \t\r\n");
400     if (std.mem.eql(u8, value, "max")) return .unlimited;
401     if (value.len == 0) return error.InvalidData;
402     return .{ .finite = try std.fmt.parseUnsigned(u64, value, 10) };
403 }
404 
405 fn parseMemoryCurrent(text: []const u8) !u64 {
406     const value = std.mem.trim(u8, text, " \t\r\n");
407     if (value.len == 0) return error.InvalidData;
408     return std.fmt.parseUnsigned(u64, value, 10);
409 }
410 
411 fn remainingMemory(limit: u64, current: u64) u64 {
412     return limit -| current;
413 }
414 
415 fn readBounded(io: std.Io, path: []const u8, storage: []u8) ![]const u8 {
416     var file = try std.Io.Dir.openFileAbsolute(io, path, .{});
417     defer file.close(io);
418     const count = try file.readPositionalAll(io, storage, 0);
419     if (count == storage.len) return error.BufferTooSmall;
420     return storage[0..count];
421 }
422 
423 fn parseLinuxAvailableMemory(text: []const u8) !u64 {
424     var found: ?u64 = null;
425     var lines = std.mem.splitScalar(u8, text, '\n');
426     while (lines.next()) |line| {
427         var fields = std.mem.tokenizeAny(u8, line, " \t\r:");
428         const name = fields.next() orelse continue;
429         if (!std.mem.eql(u8, name, "MemAvailable")) continue;
430         if (found != null) return error.InvalidData;
431         const kib = try std.fmt.parseUnsigned(u64, fields.next() orelse
432             return error.InvalidData, 10);
433         if (!std.mem.eql(u8, fields.next() orelse return error.InvalidData, "kB") or
434             fields.next() != null)
435         {
436             return error.InvalidData;
437         }
438         found = try std.math.mul(u64, kib, 1024);
439     }
440     return found orelse error.InvalidData;
441 }
442 
443 fn darwinMemory() !Memory {
444     var percent_free: c_uint = 0;
445     if (memorystatus_get_level(@intFromPtr(&percent_free)) != 0) {
446         return error.QueryFailed;
447     }
448     const total = try std.process.totalSystemMemory();
449     const available = try availableFromPercent(total, percent_free);
450     var pressure_level: c_uint = 0;
451     var pressure_size: usize = @sizeOf(c_uint);
452     if (std.c.sysctlbyname(
453         "kern.memorystatus_vm_pressure_level",
454         &pressure_level,
455         &pressure_size,
456         null,
457         0,
458     ) != 0 or pressure_size != @sizeOf(c_uint)) return error.QueryFailed;
459     return .{
460         .available_bytes = available,
461         .pressure = try decodeDarwinPressure(pressure_level),
462     };
463 }
464 
465 fn availableFromPercent(total: u64, percent: c_uint) !u64 {
466     if (percent > 100) return error.InvalidData;
467     return @intCast(@as(u128, total) * percent / 100);
468 }
469 
470 fn decodeDarwinPressure(level: c_uint) !Pressure {
471     return switch (level) {
472         1 => .normal,
473         2, 4 => .pressured,
474         else => error.InvalidData,
475     };
476 }
477 
478 test "Linux available memory parser uses binary kibibytes" {
479     const text =
480         "MemTotal:       32768000 kB\n" ++
481         "MemFree:         1048576 kB\n" ++
482         "MemAvailable:   12582912 kB\n";
483     try std.testing.expectEqual(
484         @as(u64, 12 * 1024 * 1024 * 1024),
485         try parseLinuxAvailableMemory(text),
486     );
487 }
488 
489 test "Linux available memory parser rejects missing duplicate and malformed values" {
490     try std.testing.expectError(
491         error.InvalidData,
492         parseLinuxAvailableMemory("MemFree: 1 kB\n"),
493     );
494     try std.testing.expectError(
495         error.InvalidData,
496         parseLinuxAvailableMemory("MemAvailable: 1 kB\nMemAvailable: 2 kB\n"),
497     );
498     try std.testing.expectError(
499         error.InvalidCharacter,
500         parseLinuxAvailableMemory("MemAvailable: unknown kB\n"),
501     );
502     try std.testing.expectError(
503         error.InvalidData,
504         parseLinuxAvailableMemory("MemAvailable: 1 MB\n"),
505     );
506 }
507 
508 test "Linux cgroup membership distinguishes unified and memory controllers" {
509     const unified = try parseCgroupMembership("0::/executor/slot\n");
510     try std.testing.expectEqualStrings("/executor/slot", unified.v2.?);
511     const legacy = try parseCgroupMembership("4:cpu,memory:/executor/slot\n");
512     try std.testing.expectEqualStrings("/executor/slot", legacy.v1_memory.?);
513     try std.testing.expectError(
514         error.InvalidData,
515         parseCgroupMembership("0::/safe/../escape\n"),
516     );
517 }
518 
519 test "Linux cgroup mount discovery accepts custom controller roots" {
520     const mounts = try parseCgroupMounts(
521         "36 25 0:32 /tenant /custom/unified rw,nosuid - cgroup2 cgroup rw\n" ++
522             "37 25 0:33 /legacy /custom/memory rw,nosuid - cgroup cgroup rw,memory\n",
523     );
524     try std.testing.expectEqualStrings("/tenant", mounts.v2.?.root);
525     try std.testing.expectEqualStrings("/custom/unified", mounts.v2.?.point);
526     try std.testing.expectEqualStrings("/legacy", mounts.v1_memory.?.root);
527     try std.testing.expectEqualStrings("/custom/memory", mounts.v1_memory.?.point);
528 }
529 
530 test "Linux declared cgroup membership requires an observed mount" {
531     try std.testing.expectError(
532         error.CgroupTelemetryUnavailable,
533         cgroupHeadroom(
534             std.testing.io,
535             .{ .v2 = "/executor" },
536             .{},
537         ),
538     );
539 }
540 
541 test "Linux cgroup headroom uses the tightest enclosing limit" {
542     var temporary = std.testing.tmpDir(.{});
543     defer temporary.cleanup();
544     try temporary.dir.createDirPath(std.testing.io, "parent/leaf");
545     try temporary.dir.writeFile(std.testing.io, .{
546         .sub_path = "memory.max",
547         .data = "max\n",
548     });
549     try temporary.dir.writeFile(std.testing.io, .{
550         .sub_path = "parent/memory.max",
551         .data = "800\n",
552     });
553     try temporary.dir.writeFile(std.testing.io, .{
554         .sub_path = "parent/memory.current",
555         .data = "300\n",
556     });
557     try temporary.dir.writeFile(std.testing.io, .{
558         .sub_path = "parent/leaf/memory.max",
559         .data = "1000\n",
560     });
561     try temporary.dir.writeFile(std.testing.io, .{
562         .sub_path = "parent/leaf/memory.current",
563         .data = "400\n",
564     });
565     const base = try temporary.dir.realPathFileAlloc(
566         std.testing.io,
567         ".",
568         std.testing.allocator,
569     );
570     defer std.testing.allocator.free(base);
571     const observed = (try cgroupHierarchyHeadroom(
572         std.testing.io,
573         base,
574         "/parent/leaf",
575         "memory.max",
576         "memory.current",
577     )).?;
578     try std.testing.expectEqual(@as(?u64, 500), observed.bytes);
579 }
580 
581 test "Linux cgroup subtree mounts map membership relative to mount root" {
582     var temporary = std.testing.tmpDir(.{});
583     defer temporary.cleanup();
584     try temporary.dir.createDirPath(std.testing.io, "job");
585     try temporary.dir.writeFile(std.testing.io, .{
586         .sub_path = "memory.max",
587         .data = "max\n",
588     });
589     try temporary.dir.writeFile(std.testing.io, .{
590         .sub_path = "job/memory.max",
591         .data = "1000\n",
592     });
593     try temporary.dir.writeFile(std.testing.io, .{
594         .sub_path = "job/memory.current",
595         .data = "400\n",
596     });
597     const base = try temporary.dir.realPathFileAlloc(
598         std.testing.io,
599         ".",
600         std.testing.allocator,
601     );
602     defer std.testing.allocator.free(base);
603     const observed = try cgroupMountHeadroom(
604         std.testing.io,
605         "/tenant/job",
606         .{ .root = "/tenant", .point = base },
607         "memory.max",
608         "memory.current",
609         true,
610     );
611     try std.testing.expectEqual(@as(?u64, 600), observed);
612 }
613 
614 test "Linux cgroup discovered mount without memory files fails closed" {
615     var temporary = std.testing.tmpDir(.{});
616     defer temporary.cleanup();
617     try temporary.dir.createDirPath(std.testing.io, "executor");
618     const base = try temporary.dir.realPathFileAlloc(
619         std.testing.io,
620         ".",
621         std.testing.allocator,
622     );
623     defer std.testing.allocator.free(base);
624     try std.testing.expectError(
625         error.CgroupTelemetryUnavailable,
626         cgroupMountHeadroom(
627             std.testing.io,
628             "/executor",
629             .{ .root = "/", .point = base },
630             "memory.max",
631             "memory.current",
632             true,
633         ),
634     );
635 }
636 
637 test "Linux cgroup root without a v2 memory controller is unlimited" {
638     var temporary = std.testing.tmpDir(.{});
639     defer temporary.cleanup();
640     try temporary.dir.createDirPath(std.testing.io, "executor");
641     try temporary.dir.writeFile(std.testing.io, .{
642         .sub_path = "cgroup.controllers",
643         .data = "cpuset\n",
644     });
645     const base = try temporary.dir.realPathFileAlloc(
646         std.testing.io,
647         ".",
648         std.testing.allocator,
649     );
650     defer std.testing.allocator.free(base);
651     const observed = try cgroupMountHeadroom(
652         std.testing.io,
653         "/executor",
654         .{ .root = "/", .point = base },
655         "memory.max",
656         "memory.current",
657         true,
658     );
659     try std.testing.expectEqual(@as(?u64, null), observed);
660 }
661 
662 test "Linux cgroup active v2 edge missing memory files fails closed" {
663     var temporary = std.testing.tmpDir(.{});
664     defer temporary.cleanup();
665     try temporary.dir.createDirPath(std.testing.io, "executor");
666     try temporary.dir.writeFile(std.testing.io, .{
667         .sub_path = "cgroup.controllers",
668         .data = "memory\n",
669     });
670     try temporary.dir.writeFile(std.testing.io, .{
671         .sub_path = "cgroup.subtree_control",
672         .data = "memory\n",
673     });
674     const base = try temporary.dir.realPathFileAlloc(
675         std.testing.io,
676         ".",
677         std.testing.allocator,
678     );
679     defer std.testing.allocator.free(base);
680     try std.testing.expectError(
681         error.CgroupTelemetryUnavailable,
682         cgroupMountHeadroom(
683             std.testing.io,
684             "/executor",
685             .{ .root = "/", .point = base },
686             "memory.max",
687             "memory.current",
688             true,
689         ),
690     );
691 }
692 
693 test "Linux cgroup subtree mount without memory files fails closed" {
694     var temporary = std.testing.tmpDir(.{});
695     defer temporary.cleanup();
696     try temporary.dir.createDirPath(std.testing.io, "job");
697     const base = try temporary.dir.realPathFileAlloc(
698         std.testing.io,
699         ".",
700         std.testing.allocator,
701     );
702     defer std.testing.allocator.free(base);
703     try std.testing.expectError(
704         error.CgroupTelemetryUnavailable,
705         cgroupMountHeadroom(
706             std.testing.io,
707             "/tenant/job",
708             .{ .root = "/tenant", .point = base },
709             "memory.max",
710             "memory.current",
711             true,
712         ),
713     );
714 }
715 
716 test "Linux cgroup values reject malformed data and saturate exhausted limits" {
717     try std.testing.expectError(error.InvalidData, parseMemoryLimit(""));
718     try std.testing.expectError(error.InvalidCharacter, parseMemoryLimit("unknown"));
719     try std.testing.expectError(error.InvalidData, parseMemoryCurrent(""));
720     try std.testing.expectError(error.InvalidCharacter, parseMemoryCurrent("unknown"));
721     try std.testing.expectEqual(@as(u64, 0), remainingMemory(100, 101));
722     try std.testing.expectEqual(@as(u64, 1), remainingMemory(100, 99));
723 }
724 
725 test "Darwin pressure sysctl decodes dispatch note flags" {
726     try std.testing.expectEqual(Pressure.normal, try decodeDarwinPressure(1));
727     try std.testing.expectEqual(Pressure.pressured, try decodeDarwinPressure(2));
728     try std.testing.expectEqual(Pressure.pressured, try decodeDarwinPressure(4));
729     try std.testing.expectError(error.InvalidData, decodeDarwinPressure(0));
730     try std.testing.expectError(error.InvalidData, decodeDarwinPressure(3));
731 }
732 
733 test "Darwin percent-free memory conversion is bounded and conservative" {
734     try std.testing.expectEqual(
735         @as(u64, 16 * 1024 * 1024 * 1024),
736         try availableFromPercent(64 * 1024 * 1024 * 1024, 25),
737     );
738     try std.testing.expectEqual(@as(u64, 1), try availableFromPercent(101, 1));
739     try std.testing.expectError(error.InvalidData, availableFromPercent(1, 101));
740 }
741 
742 test "host memory is a bounded observation" {
743     if (comptime builtin.os.tag != .linux and !builtin.os.tag.isDarwin()) {
744         return error.SkipZigTest;
745     }
746     const observed = try memory(std.testing.io);
747     try std.testing.expect(observed.available_bytes > 0);
748     if (comptime builtin.os.tag == .linux) {
749         try std.testing.expectEqual(Pressure.normal, observed.pressure);
750     }
751 }