lib/alloc/observe/src/debug.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const adapter = @import("allocator.zig");
 3 const protocol = @import("protocol.zig");
 4 
 5 /// A generic debugging allocator type that forwards configured options to
 6 /// `std.heap.DebugAllocator`, providing common `init`, `allocator`, and
 7 /// `deinit` methods in both enabled and disabled builds. Only `deinit` returns
 8 /// `std.heap.Check`. In enabled builds, virtual table operations forward
 9 /// through an embedded adapter tagged with the `debug` producer kind.
10 /// Observation captures raw allocator virtual table calls only and adds no
11 /// lifecycle records.
12 pub fn Allocator(comptime options: std.heap.DebugAllocatorConfig) type {
13     return if (protocol.enabled) Observed(options) else Unobserved(options);
14 }
15 
16 fn Unobserved(comptime options: std.heap.DebugAllocatorConfig) type {
17     return struct {
18         inner: std.heap.DebugAllocator(options),
19 
20         const Self = @This();
21 
22         /// Initializes the underlying debug allocator with the supplied backing
23         /// allocator, assigning a producer identifier when observation is
24         /// enabled. The constructor forwards configuration options directly to
25         /// the underlying implementation without introducing new tracking
26         /// policies or altering memory validation behavior.
27         pub fn init(backing: std.mem.Allocator) Self {
28             return .{ .inner = .{ .backing_allocator = backing } };
29         }
30 
31         /// Returns an allocator handle borrowing the instance, routing through
32         /// an embedded adapter tagged with the debug producer kind when
33         /// observation is enabled. The owning instance and its backing
34         /// allocator must remain stable and valid throughout use. The method
35         /// provides no thread-safety guarantee for concurrent invocations on
36         /// the same instance.
37         pub fn allocator(self: *Self) std.mem.Allocator {
38             return self.inner.allocator();
39         }
40 
41         /// Deinitializes the underlying debug allocator and returns its
42         /// underlying `std.heap.Check` status. Teardown emits no observation
43         /// lifecycle events. All standard ownership and lifetime prerequisites
44         /// of the underlying debug allocator apply to this call.
45         pub fn deinit(self: *Self) std.heap.Check {
46             return self.inner.deinit();
47         }
48     };
49 }
50 
51 fn Observed(comptime options: std.heap.DebugAllocatorConfig) type {
52     return struct {
53         inner: std.heap.DebugAllocator(options),
54         observed: adapter.ObservedAllocator = undefined,
55         producer_id: u64,
56 
57         const Self = @This();
58 
59         /// Initializes the underlying debug allocator with the supplied backing
60         /// allocator, assigning a producer identifier when observation is
61         /// enabled. The constructor forwards configuration options directly to
62         /// the underlying implementation without introducing new tracking
63         /// policies or altering memory validation behavior.
64         pub fn init(backing: std.mem.Allocator) Self {
65             return .{
66                 .inner = .{ .backing_allocator = backing },
67                 .producer_id = protocol.producerId(),
68             };
69         }
70 
71         /// Returns an allocator handle borrowing the instance, routing through
72         /// an embedded adapter tagged with the debug producer kind when
73         /// observation is enabled. The owning instance and its backing
74         /// allocator must remain stable and valid throughout use. The method
75         /// provides no thread-safety guarantee for concurrent invocations on
76         /// the same instance.
77         pub fn allocator(self: *Self) std.mem.Allocator {
78             self.observed = adapter.ObservedAllocator.init(
79                 self.inner.allocator(),
80                 self.producer_id,
81                 .debug,
82             );
83             return self.observed.allocator();
84         }
85 
86         /// Deinitializes the underlying debug allocator and returns its
87         /// underlying `std.heap.Check` status. Teardown emits no observation
88         /// lifecycle events. All standard ownership and lifetime prerequisites
89         /// of the underlying debug allocator apply to this call.
90         pub fn deinit(self: *Self) std.heap.Check {
91             return self.inner.deinit();
92         }
93     };
94 }