diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-19 21:26:05 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-19 21:26:05 -0300 |
| commit | b05abcba3ea09ea106ad28364c6e40a3ec31b890 (patch) | |
| tree | 9170fac5e7e5d8bde108de34a182aaa9d6844117 /introspect/src/linux/runtime.zig | |
| parent | ae310a207534b33b7321dd2b9f423a73b1969159 (diff) | |
| download | cloud9-b05abcba3ea09ea106ad28364c6e40a3ec31b890.tar.gz cloud9-b05abcba3ea09ea106ad28364c6e40a3ec31b890.zip | |
Add 9player and introspect as programs beside the library
9player/: FUSE mount CLI that mounts a 9P2000 tree into a fresh user+mount
namespace and runs a program in it (no root, no libfuse, no libc).
introspect/: the 9P debug/introspection library (freestanding core, value
renderers, Linux probe with threads/stacks/memory/breakpoints/panics) and
its demo server. Each has its own build fragment; the root build.zig wires
them behind -D9player/-Dintrospect with namespaced steps (9player-itest,
introspect-check-freestanding, programs-test, ...) and exports the
introspect module for dependents. This is the layout for related programs.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'introspect/src/linux/runtime.zig')
| -rw-r--r-- | introspect/src/linux/runtime.zig | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/introspect/src/linux/runtime.zig b/introspect/src/linux/runtime.zig new file mode 100644 index 0000000..503d6c2 --- /dev/null +++ b/introspect/src/linux/runtime.zig @@ -0,0 +1,90 @@ +//! Generators for `Config.runtime`: /runtime/{pid,ppid,uptime,argv,cwd,env,clients}. +//! The core passes every generator `Shared.ctx`; `Fns(Ctx, field)` casts it +//! to `*Ctx` and reads the `Info` stored in `@field(ctx, field)`. +const std = @import("std"); +const linux = std.os.linux; +const Writer = std.Io.Writer; + +/// What the generators report. Texts are borrowed for the server's lifetime. +pub const Info = struct { + /// argv, one argument per line. + argv: []const u8 = "", + /// Environment, one KEY=VALUE per line. + env: []const u8 = "", + cwd: []const u8 = "", + /// Monotonic seconds at startup; /runtime/uptime is the difference. + start_mono: i64 = 0, + /// Live client count, published by the probe. + clients: ?*const std.atomic.Value(u32) = null, + + pub fn now() Info { + return .{ .start_mono = monotonicSecs() }; + } +}; + +pub fn monotonicSecs() i64 { + var ts: linux.timespec = undefined; + _ = linux.clock_gettime(.MONOTONIC, &ts); + return ts.sec; +} + +/// Seconds since the epoch, clamped to u32 (for atime/mtime and `fn/now`). +pub fn realtimeSecs() u32 { + var ts: linux.timespec = undefined; + _ = linux.clock_gettime(.REALTIME, &ts); + return @intCast(std.math.clamp(ts.sec, 0, std.math.maxInt(u32))); +} + +/// The `Config.runtime` type: `Ctx` is the type behind `Shared.ctx`, `field` +/// the name of its `Info` field. +pub fn Fns(comptime Ctx: type, comptime field: []const u8) type { + return struct { + fn info(ctx: *anyopaque) *const Info { + const c: *Ctx = @ptrCast(@alignCast(ctx)); + return &@field(c, field); + } + pub fn pid(_: *anyopaque, w: *Writer) anyerror!void { + try w.print("{d}", .{linux.getpid()}); + } + pub fn ppid(_: *anyopaque, w: *Writer) anyerror!void { + try w.print("{d}", .{linux.getppid()}); + } + pub fn uptime(ctx: *anyopaque, w: *Writer) anyerror!void { + try w.print("{d}", .{monotonicSecs() - info(ctx).start_mono}); + } + pub fn argv(ctx: *anyopaque, w: *Writer) anyerror!void { + try w.writeAll(info(ctx).argv); + } + pub fn cwd(ctx: *anyopaque, w: *Writer) anyerror!void { + try w.writeAll(info(ctx).cwd); + } + pub fn env(ctx: *anyopaque, w: *Writer) anyerror!void { + try w.writeAll(info(ctx).env); + } + pub fn clients(ctx: *anyopaque, w: *Writer) anyerror!void { + const n: u32 = if (info(ctx).clients) |c| c.load(.acquire) else 0; + try w.print("{d}", .{n}); + } + }; +} + +test "runtime generators read Info through the context" { + const Ctx = struct { x: u32, info: Info }; + var count: std.atomic.Value(u32) = .init(3); + var ctx: Ctx = .{ .x = 0, .info = .{ .argv = "a\nb\n", .cwd = "/tmp", .env = "K=V\n", .start_mono = monotonicSecs(), .clients = &count } }; + const F = Fns(Ctx, "info"); + var buf: [64]u8 = undefined; + var w: Writer = .fixed(&buf); + try F.clients(&ctx, &w); + try std.testing.expectEqualStrings("3", w.buffered()); + w = .fixed(&buf); + try F.argv(&ctx, &w); + try std.testing.expectEqualStrings("a\nb\n", w.buffered()); + w = .fixed(&buf); + try F.uptime(&ctx, &w); + try std.testing.expect(w.buffered().len >= 1); + w = .fixed(&buf); + try F.pid(&ctx, &w); + try std.testing.expectEqual(linux.getpid(), try std.fmt.parseInt(i32, w.buffered(), 10)); + try std.testing.expectEqual(@as(usize, 7), @typeInfo(F).@"struct".decls.len); +} |
