1const std = @import("std");
2const assert = std.debug.assert;
3
4const builtin = @import("builtin");
5const build_options = @import("build_options");
6
7pub const enable = if (builtin.is_test) false else build_options.enable_tracy;
8pub const enable_allocation = enable and build_options.enable_tracy_allocation;
9pub const enable_callstack = enable and build_options.enable_tracy_callstack;
10pub const callstack_depth = if (enable_callstack) build_options.tracy_callstack_depth else 0;
11
12const ___tracy_c_zone_context = extern struct {
13 id: u32,
14 active: i32,
15
16 pub inline fn end(self: @This()) void {
17 ___tracy_emit_zone_end(self);
18 }
19
20 pub inline fn addText(self: @This(), text: []const u8) void {
21 ___tracy_emit_zone_text(self, text.ptr, text.len);
22 }
23
24 pub inline fn addTextFmt(self: @This(), comptime fmt: []const u8, args: anytype) void {
25 var buf: [512]u8 = undefined;
26 const slice = std.mem.print(&buf, fmt, args) catch &buf;
27 self.addText(slice);
28 }
29
30 pub inline fn setName(self: @This(), name: []const u8) void {
31 ___tracy_emit_zone_name(self, name.ptr, name.len);
32 }
33
34 pub inline fn setColor(self: @This(), color: u32) void {
35 ___tracy_emit_zone_color(self, color);
36 }
37
38 pub inline fn setValue(self: @This(), value: u64) void {
39 ___tracy_emit_zone_value(self, value);
40 }
41};
42
43pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
44 pub inline fn end(self: @This()) void {
45 _ = self;
46 }
47
48 pub inline fn addText(self: @This(), text: []const u8) void {
49 _ = self;
50 _ = text;
51 }
52
53 pub inline fn addTextFmt(self: @This(), comptime fmt: []const u8, args: anytype) void {
54 _ = self;
55 _ = fmt;
56 _ = args;
57 }
58
59 pub inline fn setName(self: @This(), name: []const u8) void {
60 _ = self;
61 _ = name;
62 }
63
64 pub inline fn setColor(self: @This(), color: u32) void {
65 _ = self;
66 _ = color;
67 }
68
69 pub inline fn setValue(self: @This(), value: u64) void {
70 _ = self;
71 _ = value;
72 }
73};
74
75pub inline fn trace(comptime src: std.lang.SourceLocation) Ctx {
76 if (!enable) return .{};
77
78 const global = struct {
79 const loc: ___tracy_source_location_data = .{
80 .name = null,
81 .function = src.fn_name.ptr,
82 .file = src.file.ptr,
83 .line = src.line,
84 .color = 0,
85 };
86 };
87
88 return ___tracy_emit_zone_begin_callstack(&global.loc, callstack_depth, 1);
89}
90
91pub inline fn traceNamed(comptime src: std.lang.SourceLocation, comptime name: [:0]const u8) Ctx {
92 if (!enable) return .{};
93
94 const global = struct {
95 const loc: ___tracy_source_location_data = .{
96 .name = name.ptr,
97 .function = src.fn_name.ptr,
98 .file = src.file.ptr,
99 .line = src.line,
100 .color = 0,
101 };
102 };
103
104 return ___tracy_emit_zone_begin_callstack(&global.loc, callstack_depth, 1);
105}
106
107pub inline fn fiberEnter(fiber: [*:0]const u8) void {
108 if (!enable) return;
109 ___tracy_fiber_enter(fiber);
110}
111
112pub inline fn fiberLeave() void {
113 if (!enable) return;
114 ___tracy_fiber_leave();
115}
116
117pub inline fn plotConfig(comptime name: [*:0]const u8, config: PlotConfig) void {
118 if (!enable) return;
119 ___tracy_emit_plot_config(
120 name,
121 config.format,
122 config.mode,
123 @intFromBool(config.fill),
124 // https://github.com/wolfpld/tracy/issues/1232
125 @byteSwap(config.color),
126 );
127}
128
129pub inline fn plotInt(comptime name: [*:0]const u8, val: i64) void {
130 if (!enable) return;
131 ___tracy_emit_plot_int(name, val);
132}
133
134pub const Allocator = struct {
135 parent_allocator: std.mem.Allocator,
136
137 comptime {
138 assert(enable); // used `tracy.Allocator` with Tracy disabled
139 }
140
141 pub fn interface(self: *Allocator) std.mem.Allocator {
142 return .{
143 .ptr = self,
144 .vtable = &.{
145 .alloc = allocFn,
146 .resize = resizeFn,
147 .remap = remapFn,
148 .free = freeFn,
149 },
150 };
151 }
152
153 fn allocFn(ptr: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
154 const self: *Allocator = @ptrCast(@alignCast(ptr));
155 assert(len > 0);
156 if (self.parent_allocator.rawAlloc(len, alignment, ret_addr)) |memory| {
157 ___tracy_emit_memory_alloc_callstack(memory, len, callstack_depth, 0);
158 return memory;
159 } else {
160 messageColor("allocation failed", 0xFF0000);
161 return null;
162 }
163 }
164
165 fn resizeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
166 const self: *Allocator = @ptrCast(@alignCast(ptr));
167 assert(memory.len > 0);
168 assert(new_len > 0);
169 // We need to mark the free before calling the implementation to avoid a race.
170 ___tracy_emit_memory_free_callstack(memory.ptr, callstack_depth, 0);
171 if (self.parent_allocator.rawResize(memory, alignment, new_len, ret_addr)) {
172 ___tracy_emit_memory_alloc_callstack(memory.ptr, new_len, callstack_depth, 0);
173 return true;
174 } else {
175 // No `messageColor` call here because this case is hit frequently in normal operation.
176 ___tracy_emit_memory_alloc_callstack(memory.ptr, memory.len, callstack_depth, 0);
177 return false;
178 }
179 }
180
181 fn remapFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
182 const self: *Allocator = @ptrCast(@alignCast(ptr));
183 assert(memory.len > 0);
184 assert(new_len > 0);
185 // We need to mark the free before calling the implementation to avoid a race.
186 ___tracy_emit_memory_free_callstack(memory.ptr, callstack_depth, 0);
187 if (self.parent_allocator.rawRemap(memory, alignment, new_len, ret_addr)) |new_memory| {
188 ___tracy_emit_memory_alloc_callstack(new_memory, new_len, callstack_depth, 0);
189 return new_memory;
190 } else {
191 // No `messageColor` call here because this case is hit frequently in normal operation.
192 ___tracy_emit_memory_alloc_callstack(memory.ptr, memory.len, callstack_depth, 0);
193 return null;
194 }
195 }
196
197 fn freeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
198 const self: *Allocator = @ptrCast(@alignCast(ptr));
199 assert(memory.len > 0);
200 ___tracy_emit_memory_free_callstack(memory.ptr, callstack_depth, 0);
201 self.parent_allocator.rawFree(memory, alignment, ret_addr);
202 }
203};
204
205// This function only accepts comptime-known strings, see `messageCopy` for runtime strings
206pub inline fn message(comptime msg: [:0]const u8) void {
207 messageColor(msg, 0);
208}
209
210// This function only accepts comptime-known strings, see `messageColorCopy` for runtime strings
211pub inline fn messageColor(comptime msg: [:0]const u8, color: u24) void {
212 if (!enable) return;
213 ___tracy_emit_logStringL(.Info, color, callstack_depth, msg.ptr);
214}
215
216pub inline fn messageCopy(msg: []const u8) void {
217 messageColorCopy(msg, 0);
218}
219
220pub inline fn messageColorCopy(msg: []const u8, color: u24) void {
221 if (!enable) return;
222 ___tracy_emit_logString(.Info, color, callstack_depth, msg.len, msg.ptr);
223}
224
225/// Used to store strings which Tracy requires to have stable pointers for the program's entire
226/// lifetime. All such strings will be leaked.
227///
228/// The `enable` check ensures that this is not referenced if Tracy is disabled.
229var tracy_arena: std.heap.ArenaAllocator = if (enable) .init(std.heap.page_allocator);
230
231pub inline fn namedFrame(name: []const u8) Frame {
232 if (!enable) return .{ .name = {} };
233 const stable_name = tracy_arena.allocator().dupeSentinel(u8, name, 0) catch @panic("tracy arena OOM");
234 ___tracy_emit_frame_mark_start(stable_name.ptr);
235 return .{ .name = stable_name.ptr };
236}
237
238pub const Frame = struct {
239 name: if (enable) [*:0]const u8 else void,
240 pub inline fn end(frame: Frame) void {
241 if (!enable) return;
242 ___tracy_emit_frame_mark_end(frame.name);
243 }
244};
245
246pub const MessageSeverity = enum(i8) {
247 Trace, // Broadly track variable states and events in the software program.
248 Debug, // Describes variable states and details about specific internal events in the software, that are useful for investigations.
249 Info, // Describes normal events, which inform on the expected progress and state of your software.
250 Warning, // Describes potentially dangerous situations caused by unexpected events and states.
251 Error, // Describes the occurance of unexpected behavior. Does not interrupt the execution of the software.
252 Fatal, // Describes a critical event that will lead to a software failure/crash.
253};
254
255pub const PlotConfig = struct {
256 format: Format,
257 mode: Mode,
258 fill: bool = true,
259 color: u24 = 0,
260
261 pub const Format = enum(i32) {
262 number = 0,
263 memory = 1,
264 percentage = 2,
265 watt = 3,
266 };
267
268 pub const Mode = enum(i32) {
269 line = 0,
270 step = 1,
271 };
272};
273
274extern fn ___tracy_emit_frame_mark_start(name: [*:0]const u8) void;
275extern fn ___tracy_emit_frame_mark_end(name: [*:0]const u8) void;
276extern fn ___tracy_emit_zone_begin_callstack(srcloc: *const ___tracy_source_location_data, depth: i32, active: i32) ___tracy_c_zone_context;
277extern fn ___tracy_emit_zone_text(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void;
278extern fn ___tracy_emit_zone_name(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void;
279extern fn ___tracy_emit_zone_color(ctx: ___tracy_c_zone_context, color: u32) void;
280extern fn ___tracy_emit_zone_value(ctx: ___tracy_c_zone_context, value: u64) void;
281extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
282extern fn ___tracy_emit_memory_alloc_callstack(ptr: *const anyopaque, size: usize, depth: i32, secure: i32) void;
283extern fn ___tracy_emit_memory_free_callstack(ptr: *const anyopaque, depth: i32, secure: i32) void;
284extern fn ___tracy_emit_logString(severity: MessageSeverity, color: i32, callstack_depth: i32, size: usize, txt: [*]const u8) void;
285extern fn ___tracy_emit_logStringL(severity: MessageSeverity, color: i32, callstack_depth: i32, txt: [*:0]const u8) void;
286extern fn ___tracy_emit_plot_int(name: [*:0]const u8, val: i64) void;
287extern fn ___tracy_emit_plot_config(name: [*:0]const u8, format: PlotConfig.Format, mode: PlotConfig.Mode, fill: i32, color: u32) void;
288extern fn ___tracy_fiber_enter(fiber: [*:0]const u8) void;
289extern fn ___tracy_fiber_leave() void;
290
291const ___tracy_source_location_data = extern struct {
292 name: ?[*:0]const u8,
293 function: [*:0]const u8,
294 file: [*:0]const u8,
295 line: u32,
296 color: u32,
297};