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 }