lib/sys/src/observer.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const enabled = true;
4
5 pub const Operation = enum(u8) {
6 map,
7 unmap,
8 protect,
9 discard,
10 decommit,
11 advise,
12 };
13
14 pub const Source = enum(u8) {
15 anonymous,
16 reserve,
17 anonymous_fixed,
18 private_file,
19 shared_file,
20 private_file_fixed,
21 unmap,
22 protect,
23 discard,
24 decommit,
25 huge_pages,
26 small_pages,
27 };
28
29 pub const Event = struct {
30 operation: Operation,
31 source: Source,
32 address: usize,
33 len: usize,
34 return_address: usize,
35 succeeded: bool,
36 };
37
38 pub const Sink = struct {
39 context: *anyopaque,
40 record: *const fn (context: *anyopaque, event: Event) void,
41 };
42
43 pub const Session = struct {
44 sink: if (enabled) ?*const Sink else void =
45 if (enabled) null else {},
46 active: if (enabled) bool else void =
47 if (enabled) false else {},
48
49 pub fn deinit(self: *Session) void {
50 if (comptime !enabled) return;
51 std.debug.assert(callback_depth == 0);
52 std.debug.assert(self.active);
53 const address = @intFromPtr(self.sink.?);
54 const previous = active_sink.cmpxchgStrong(
55 address,
56 0,
57 .acq_rel,
58 .acquire,
59 );
60 std.debug.assert(previous == null);
61 while (active_operations.load(.acquire) != 0) {
62 std.atomic.spinLoopHint();
63 }
64 self.active = false;
65 self.sink = null;
66 }
67 };
68
69 pub const Suppression = struct {
70 active: bool = true,
71
72 pub fn deinit(self: *Suppression) void {
73 if (!self.active) return;
74 std.debug.assert(callback_depth > 0);
75 callback_depth -= 1;
76 self.active = false;
77 }
78 };
79
80 var active_sink = std.atomic.Value(usize).init(0);
81 var active_operations = std.atomic.Value(usize).init(0);
82 threadlocal var callback_depth: u8 = 0;
83
84 pub fn install(sink: *const Sink) error{AlreadyInstalled}!Session {
85 if (comptime !enabled) return .{};
86 std.debug.assert(callback_depth == 0);
87 const address = @intFromPtr(sink);
88 std.debug.assert(address != 0);
89 if (active_sink.cmpxchgStrong(
90 0,
91 address,
92 .acq_rel,
93 .acquire,
94 ) != null) {
95 return error.AlreadyInstalled;
96 }
97 return .{
98 .sink = sink,
99 .active = true,
100 };
101 }
102
103 pub inline fn suppress() Suppression {
104 std.debug.assert(callback_depth < std.math.maxInt(u8));
105 callback_depth += 1;
106 return .{};
107 }
108
109 pub inline fn record(event: Event) void {
110 if (comptime !enabled) return;
111 if (callback_depth != 0) return;
112 _ = active_operations.fetchAdd(1, .acquire);
113 const sink_address = active_sink.load(.acquire);
114 if (sink_address == 0) {
115 _ = active_operations.fetchSub(1, .release);
116 return;
117 }
118 const sink: *const Sink = @ptrFromInt(sink_address);
119 callback_depth += 1;
120 sink.record(sink.context, event);
121 callback_depth -= 1;
122 _ = active_operations.fetchSub(1, .release);
123 }
124
125 test "physical memory observer records owner operations" {
126 if (!enabled) return error.SkipZigTest;
127 const Recorder = struct {
128 event: ?Event = null,
129
130 fn sink(self: *@This()) Sink {
131 return .{
132 .context = self,
133 .record = accept,
134 };
135 }
136
137 fn accept(context: *anyopaque, event: Event) void {
138 const self: *@This() = @ptrCast(@alignCast(context));
139 self.event = event;
140 record(event);
141 }
142 };
143 var recorder = Recorder{};
144 var sink = recorder.sink();
145 var session = try install(&sink);
146 defer session.deinit();
147 const expected = Event{
148 .operation = .map,
149 .source = .anonymous,
150 .address = 0x1000,
151 .len = 4096,
152 .return_address = 0x2000,
153 .succeeded = true,
154 };
155 record(expected);
156 try std.testing.expectEqual(expected, recorder.event.?);
157 }
158
159 test "disabled physical memory observer is inert" {
160 if (enabled) return error.SkipZigTest;
161 var sink = Sink{
162 .context = undefined,
163 .record = undefined,
164 };
165 var session = try install(&sink);
166 defer session.deinit();
167 record(.{
168 .operation = .unmap,
169 .source = .unmap,
170 .address = 0,
171 .len = 0,
172 .return_address = 0,
173 .succeeded = true,
174 });
175 }