lib/tracy/src/instrumentation.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const build_options = @import("build_options");
3 const allocator_mod = @import("allocator.zig");
4 const runtime = @import("runtime.zig");
5 const summary = @import("summary.zig");
6
7 const SourceLocation = std.builtin.SourceLocation;
8 const ContextSwitchOptions = runtime.ContextSwitchOptions;
9 const CpuTopology = runtime.CpuTopology;
10 const Fiber = runtime.Fiber;
11 const GpuContext = runtime.GpuContext;
12 const GpuContextOptions = runtime.GpuContextOptions;
13 const HardwareSampleKind = runtime.HardwareSampleKind;
14 const Lock = runtime.Lock;
15 const PlotConfig = runtime.PlotConfig;
16 const SampleFrame = runtime.SampleFrame;
17 const SampleKind = runtime.SampleKind;
18 const StartOptions = runtime.StartOptions;
19 const ThreadWakeupOptions = runtime.ThreadWakeupOptions;
20 const TracedAllocator = allocator_mod.TracedAllocator;
21 const Zone = runtime.Zone;
22
23 pub fn start(writer: *std.Io.Writer, options: StartOptions) !bool {
24 return try runtime.start(writer, options);
25 }
26
27 pub fn stop() void {
28 runtime.stop();
29 }
30
31 pub fn tracedAllocator(backing: std.mem.Allocator) TracedAllocator {
32 return allocator_mod.trace(backing);
33 }
34
35 pub fn namedAllocator(backing: std.mem.Allocator, name: []const u8) TracedAllocator {
36 return allocator_mod.traceNamed(backing, name);
37 }
38
39 pub inline fn zone(comptime name: [:0]const u8) Zone {
40 return zoneAt(name, @src());
41 }
42
43 pub inline fn zoneAt(comptime name: [:0]const u8, comptime src: SourceLocation) Zone {
44 return runtime.zoneAt(name, src, null);
45 }
46
47 pub inline fn zoneColor(comptime name: [:0]const u8, comptime color: u32) Zone {
48 return zoneColorAt(name, color, @src());
49 }
50
51 pub inline fn zoneColorAt(comptime name: [:0]const u8, comptime color: u32, comptime src: SourceLocation) Zone {
52 return runtime.zoneAt(name, src, color);
53 }
54
55 pub inline fn lockable(comptime name: [:0]const u8) Lock {
56 return lockableAt(name, @src());
57 }
58
59 pub inline fn lockableAt(comptime name: [:0]const u8, comptime src: SourceLocation) Lock {
60 return runtime.lockableAt(name, src);
61 }
62
63 pub inline fn sharedLockable(comptime name: [:0]const u8) Lock {
64 return sharedLockableAt(name, @src());
65 }
66
67 pub inline fn sharedLockableAt(comptime name: [:0]const u8, comptime src: SourceLocation) Lock {
68 return runtime.sharedLockableAt(name, src);
69 }
70
71 pub inline fn frameMark() void {
72 runtime.frame(null);
73 }
74
75 pub inline fn frameMarkNamed(comptime name: [:0]const u8) void {
76 runtime.frame(name);
77 }
78
79 pub inline fn sample(comptime name: [:0]const u8) void {
80 runtime.sampleAt(name, .manual, @src());
81 }
82
83 pub inline fn sampleKind(comptime name: [:0]const u8, kind: SampleKind) void {
84 runtime.sampleAt(name, kind, @src());
85 }
86
87 pub inline fn sampleWeighted(comptime name: [:0]const u8, kind: SampleKind, weight: u64) void {
88 runtime.sampleWeightedAt(name, kind, weight, @src());
89 }
90
91 pub inline fn sampleStack(name: []const u8, kind: SampleKind, frames: []const SampleFrame) void {
92 runtime.sampleStack(name, kind, frames);
93 }
94
95 pub inline fn sampleStackWeighted(name: []const u8, kind: SampleKind, frames: []const SampleFrame, weight: u64) void {
96 runtime.sampleStackWeighted(name, kind, frames, weight);
97 }
98
99 pub inline fn newFiber(comptime name: [:0]const u8) Fiber {
100 return runtime.fiber(name, 0);
101 }
102
103 pub inline fn newFiberHint(comptime name: [:0]const u8, comptime group_hint: i32) Fiber {
104 return runtime.fiber(name, group_hint);
105 }
106
107 pub inline fn newFiberNamed(name: []const u8) Fiber {
108 return runtime.fiber(name, 0);
109 }
110
111 pub inline fn newFiberNamedHint(name: []const u8, group_hint: i32) Fiber {
112 return runtime.fiber(name, group_hint);
113 }
114
115 pub inline fn gpuContext(options: GpuContextOptions) GpuContext {
116 return runtime.gpuContext(options);
117 }
118
119 pub inline fn contextSwitch(options: ContextSwitchOptions) void {
120 runtime.contextSwitch(options);
121 }
122
123 pub inline fn threadWakeup(options: ThreadWakeupOptions) void {
124 runtime.threadWakeup(options);
125 }
126
127 pub inline fn threadContext(thread: u64, name: ?[]const u8) void {
128 runtime.threadContext(thread, name);
129 }
130
131 pub inline fn threadPid(thread: u64, pid: u64) void {
132 runtime.threadPid(thread, pid);
133 }
134
135 pub inline fn cpuTopology(topology: CpuTopology) void {
136 runtime.cpuTopology(topology);
137 }
138
139 pub inline fn systemTime(value: f64) void {
140 runtime.systemTime(value);
141 }
142
143 pub inline fn systemPower(name: []const u8, delta_uj: u64) void {
144 runtime.systemPower(name, delta_uj);
145 }
146
147 pub inline fn hardwareSample(kind: HardwareSampleKind, address: u64) void {
148 runtime.hardwareSample(kind, address);
149 }
150
151 pub inline fn message(text: []const u8) void {
152 runtime.message(text);
153 }
154
155 pub inline fn appInfo(text: []const u8) void {
156 runtime.appInfo(text);
157 }
158
159 pub inline fn plotConfig(comptime name: [:0]const u8, config: PlotConfig) void {
160 runtime.plotConfig(name, config);
161 }
162
163 pub inline fn plot(comptime name: [:0]const u8, value: f64) void {
164 runtime.plotFloat(name, value);
165 }
166
167 pub inline fn plotFloat(comptime name: [:0]const u8, value: f32) void {
168 runtime.plotFloat(name, @floatCast(value));
169 }
170
171 pub inline fn plotInt(comptime name: [:0]const u8, value: i64) void {
172 runtime.plotInt(name, value);
173 }
174
175 pub inline fn alloc(ptr: ?*const anyopaque, size: usize) void {
176 runtime.alloc(ptr, size);
177 }
178
179 pub inline fn allocNamed(ptr: ?*const anyopaque, size: usize, name: []const u8) void {
180 runtime.allocNamed(ptr, size, name);
181 }
182
183 pub inline fn free(ptr: ?*const anyopaque) void {
184 runtime.free(ptr);
185 }
186
187 pub inline fn freeNamed(ptr: ?*const anyopaque, name: []const u8) void {
188 runtime.freeNamed(ptr, name);
189 }
190
191 pub inline fn freeSized(ptr: ?*const anyopaque, size: usize) void {
192 runtime.freeSized(ptr, size);
193 }
194
195 pub inline fn freeSizedNamed(ptr: ?*const anyopaque, size: usize, name: []const u8) void {
196 runtime.freeSizedNamed(ptr, size, name);
197 }
198
199 pub inline fn setThreadName(comptime name: [:0]const u8) void {
200 runtime.threadName(name);
201 }
202
203 pub inline fn isConnected() bool {
204 return runtime.isActive();
205 }
206
207 test "instrumentation calls compile" {
208 setThreadName("tracy-test");
209 appInfo("tracy package test");
210 message("tracy message");
211 plotConfig("tracy.plot", .{ .unit = .nanoseconds, .step = true });
212 plot("tracy.plot", 1.25);
213 plotFloat("tracy.plot_float", 1.5);
214 plotInt("tracy.plot_int", 2);
215 frameMark();
216 frameMarkNamed("tracy.frame");
217 sample("tracy.sample");
218 sampleKind("tracy.cpu.sample", .cpu);
219 sampleWeighted("tracy.wall.sample", .wall, 3);
220 sampleStack("tracy.stack.sample", .manual, &.{
221 .{ .name = "root", .function = "root", .file = "root.zig", .line = 1 },
222 .{ .name = "leaf", .function = "leaf", .file = "leaf.zig", .line = 2 },
223 });
224 const profiled_fiber = newFiberHint("tracy.fiber", 7);
225 profiled_fiber.enter();
226 message("tracy fiber message");
227 profiled_fiber.leave();
228 const dynamic_fiber = newFiberNamed("tracy.dynamic_fiber");
229 dynamic_fiber.enter();
230 dynamic_fiber.leave();
231 const profiled_gpu = gpuContext(.{ .name = "tracy.gpu", .context_type = .vulkan, .period = 1.0, .gpu_time = 0 });
232 profiled_gpu.setName("tracy dynamic gpu");
233 profiled_gpu.sync(0);
234 profiled_gpu.calibrate(1, 1);
235 profiled_gpu.annotationName(7, "occupancy");
236 profiled_gpu.annotate(7, 0.5);
237 const gpu_query = profiled_gpu.nextQuery();
238 const gpu_zone = profiled_gpu.zoneQuery("tracy.gpu.zone", gpu_query);
239 gpu_zone.annotate(7, 0.75);
240 gpu_zone.endQueryWithTimes(profiled_gpu.nextQuery(), 1, 3);
241 contextSwitch(.{ .cpu = 0, .prev_thread = 1, .next_thread = 2, .reason = "WrPreempted", .state = "Ready" });
242 threadWakeup(.{ .thread = 1, .cpu = 0, .reason = "Unwait" });
243 threadContext(2, "worker");
244 threadPid(2, 900);
245 cpuTopology(.{ .cpu = 0, .package = 0, .die = 0, .core = 0 });
246 systemTime(0.25);
247 systemPower("package", 100);
248 hardwareSample(.cache_miss, 0x1234);
249
250 const active_zone = zone("tracy.zone");
251 active_zone.setName("tracy dynamic zone");
252 active_zone.setText("tracy zone text");
253 active_zone.setColor(0x44aa88);
254 active_zone.setValue(42);
255 active_zone.end();
256
257 const profiled_lock = lockable("tracy.lock");
258 profiled_lock.wait();
259 profiled_lock.obtain();
260 profiled_lock.markAt(@src());
261 profiled_lock.setName("tracy dynamic lock");
262 profiled_lock.release();
263 profiled_lock.terminate();
264
265 const profiled_shared_lock = sharedLockable("tracy.shared_lock");
266 profiled_shared_lock.sharedWait();
267 profiled_shared_lock.sharedObtain();
268 profiled_shared_lock.sharedRelease();
269 profiled_shared_lock.terminate();
270
271 alloc(null, 0);
272 allocNamed(null, 0, "tracy.named.alloc");
273 free(null);
274 freeNamed(null, "tracy.named.free");
275 freeSized(null, 0);
276 freeSizedNamed(null, 0, "tracy.named.free_sized");
277 _ = isConnected();
278 }
279
280 test "enabled trace records analyzable zones" {
281 if (!build_options.enabled) return;
282
283 var out = std.Io.Writer.Allocating.init(std.testing.allocator);
284 defer out.deinit();
285
286 try std.testing.expect(try start(&out.writer, .{ .name = "root test" }));
287 defer stop();
288 const active_zone = zone("root.test.zone");
289 active_zone.setValue(12);
290 active_zone.end();
291 const profiled_lock = lockable("root.test.lock");
292 profiled_lock.wait();
293 profiled_lock.obtain();
294 profiled_lock.release();
295 profiled_lock.terminate();
296 message("root test message");
297 sample("root.test.sample");
298 const profiled_fiber = newFiberHint("root.test.fiber", 2);
299 profiled_fiber.enter();
300 message("root fiber message");
301 profiled_fiber.leave();
302 contextSwitch(.{ .cpu = 0, .prev_thread = 1, .next_thread = 2, .reason = "WrPreempted", .state = "Ready" });
303 threadWakeup(.{ .thread = 1, .cpu = 0, .reason = "Unwait" });
304 plotInt("root.test.plot", 7);
305 stop();
306
307 var analyzer = summary.Analyzer.init(std.testing.allocator);
308 defer analyzer.deinit();
309 try analyzer.ingestJsonlBytes(out.written());
310 try std.testing.expectEqual(@as(u64, 1), analyzer.counters.completed_zones);
311 try std.testing.expectEqual(@as(u64, 2), analyzer.counters.messages);
312 try std.testing.expectEqual(@as(u64, 1), analyzer.samples.counters.samples);
313 try std.testing.expectEqual(@as(u64, 1), analyzer.fibers.counters.names);
314 try std.testing.expectEqual(@as(u64, 1), analyzer.fibers.counters.enters);
315 try std.testing.expectEqual(@as(u64, 1), analyzer.fibers.counters.leaves);
316 try std.testing.expectEqual(@as(u64, 1), analyzer.context.counters.switches);
317 try std.testing.expectEqual(@as(u64, 1), analyzer.context.counters.wakeups);
318 try std.testing.expectEqual(@as(u64, 1), analyzer.counters.plots);
319 try std.testing.expectEqual(@as(usize, 1), analyzer.locks.locks.count());
320 try std.testing.expectEqual(@as(u64, 1), analyzer.locks.counters.completed_waits);
321 try std.testing.expectEqual(@as(u64, 1), analyzer.locks.counters.completed_holds);
322 }
323
324 test {
325 std.testing.refAllDecls(@This());
326 }