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 }