1const std = @import("std");
2const builtin = @import("builtin");
3
4pub const enable = if (builtin.is_test) false else @import("build_options").enable_tracy;
5pub const enable_allocation = enable and @import("build_options").enable_tracy_allocation;
6pub const enable_callstack = enable and @import("build_options").enable_tracy_callstack;
7
8// TODO: make this configurable
9const callstack_depth = 10;
10
11const ___tracy_c_zone_context = extern struct {
12    id: u32,
13    active: c_int,
14
15    pub inline fn end(self: @This()) void {
16        ___tracy_emit_zone_end(self);
17    }
18
19    pub inline fn addText(self: @This(), text: []const u8) void {
20        ___tracy_emit_zone_text(self, text.ptr, text.len);
21    }
22
23    pub inline fn setName(self: @This(), name: []const u8) void {
24        ___tracy_emit_zone_name(self, name.ptr, name.len);
25    }
26
27    pub inline fn setColor(self: @This(), color: u32) void {
28        ___tracy_emit_zone_color(self, color);
29    }
30
31    pub inline fn setValue(self: @This(), value: u64) void {
32        ___tracy_emit_zone_value(self, value);
33    }
34};
35
36pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
37    pub inline fn end(self: @This()) void {
38        _ = self;
39    }
40
41    pub inline fn addText(self: @This(), text: []const u8) void {
42        _ = self;
43        _ = text;
44    }
45
46    pub inline fn setName(self: @This(), name: []const u8) void {
47        _ = self;
48        _ = name;
49    }
50
51    pub inline fn setColor(self: @This(), color: u32) void {
52        _ = self;
53        _ = color;
54    }
55
56    pub inline fn setValue(self: @This(), value: u64) void {
57        _ = self;
58        _ = value;
59    }
60};
61
62pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
63    if (!enable) return .{};
64
65    if (enable_callstack) {
66        return ___tracy_emit_zone_begin_callstack(&.{
67            .name = null,
68            .function = src.fn_name.ptr,
69            .file = src.file.ptr,
70            .line = src.line,
71            .color = 0,
72        }, callstack_depth, 1);
73    } else {
74        return ___tracy_emit_zone_begin(&.{
75            .name = null,
76            .function = src.fn_name.ptr,
77            .file = src.file.ptr,
78            .line = src.line,
79            .color = 0,
80        }, 1);
81    }
82}
83
84pub inline fn traceNamed(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) Ctx {
85    if (!enable) return .{};
86
87    if (enable_callstack) {
88        return ___tracy_emit_zone_begin_callstack(&.{
89            .name = name.ptr,
90            .function = src.fn_name.ptr,
91            .file = src.file.ptr,
92            .line = src.line,
93            .color = 0,
94        }, callstack_depth, 1);
95    } else {
96        return ___tracy_emit_zone_begin(&.{
97            .name = name.ptr,
98            .function = src.fn_name.ptr,
99            .file = src.file.ptr,
100            .line = src.line,
101            .color = 0,
102        }, 1);
103    }
104}
105
106pub fn tracyAllocator(allocator: std.mem.Allocator) TracyAllocator(null) {
107    return TracyAllocator(null).init(allocator);
108}
109
110pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
111    return struct {
112        parent_allocator: std.mem.Allocator,
113
114        const Self = @This();
115
116        pub fn init(parent_allocator: std.mem.Allocator) Self {
117            return .{
118                .parent_allocator = parent_allocator,
119            };
120        }
121
122        pub fn allocator(self: *Self) std.mem.Allocator {
123            return std.mem.Allocator.init(self, allocFn, resizeFn, freeFn);
124        }
125
126        fn allocFn(self: *Self, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) std.mem.Allocator.Error![]u8 {
127            const result = self.parent_allocator.rawAlloc(len, ptr_align, len_align, ret_addr);
128            if (result) |data| {
129                if (data.len != 0) {
130                    if (name) |n| {
131                        allocNamed(data.ptr, data.len, n);
132                    } else {
133                        alloc(data.ptr, data.len);
134                    }
135                }
136            } else |_| {
137                messageColor("allocation failed", 0xFF0000);
138            }
139            return result;
140        }
141
142        fn resizeFn(self: *Self, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize {
143            if (self.parent_allocator.rawResize(buf, buf_align, new_len, len_align, ret_addr)) |resized_len| {
144                if (name) |n| {
145                    freeNamed(buf.ptr, n);
146                    allocNamed(buf.ptr, resized_len, n);
147                } else {
148                    free(buf.ptr);
149                    alloc(buf.ptr, resized_len);
150                }
151
152                return resized_len;
153            }
154
155            // during normal operation the compiler hits this case thousands of times due to this
156            // emitting messages for it is both slow and causes clutter
157            return null;
158        }
159
160        fn freeFn(self: *Self, buf: []u8, buf_align: u29, ret_addr: usize) void {
161            self.parent_allocator.rawFree(buf, buf_align, ret_addr);
162            // this condition is to handle free being called on an empty slice that was never even allocated
163            // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}`
164            if (buf.len != 0) {
165                if (name) |n| {
166                    freeNamed(buf.ptr, n);
167                } else {
168                    free(buf.ptr);
169                }
170            }
171        }
172    };
173}
174
175// This function only accepts comptime known strings, see `messageCopy` for runtime strings
176pub inline fn message(comptime msg: [:0]const u8) void {
177    if (!enable) return;
178    ___tracy_emit_messageL(msg.ptr, if (enable_callstack) callstack_depth else 0);
179}
180
181// This function only accepts comptime known strings, see `messageColorCopy` for runtime strings
182pub inline fn messageColor(comptime msg: [:0]const u8, color: u32) void {
183    if (!enable) return;
184    ___tracy_emit_messageLC(msg.ptr, color, if (enable_callstack) callstack_depth else 0);
185}
186
187pub inline fn messageCopy(msg: []const u8) void {
188    if (!enable) return;
189    ___tracy_emit_message(msg.ptr, msg.len, if (enable_callstack) callstack_depth else 0);
190}
191
192pub inline fn messageColorCopy(msg: [:0]const u8, color: u32) void {
193    if (!enable) return;
194    ___tracy_emit_messageC(msg.ptr, msg.len, color, if (enable_callstack) callstack_depth else 0);
195}
196
197pub inline fn frameMark() void {
198    if (!enable) return;
199    ___tracy_emit_frame_mark(null);
200}
201
202pub inline fn frameMarkNamed(comptime name: [:0]const u8) void {
203    if (!enable) return;
204    ___tracy_emit_frame_mark(name.ptr);
205}
206
207pub inline fn namedFrame(comptime name: [:0]const u8) Frame(name) {
208    frameMarkStart(name);
209    return .{};
210}
211
212pub fn Frame(comptime name: [:0]const u8) type {
213    return struct {
214        pub fn end(_: @This()) void {
215            frameMarkEnd(name);
216        }
217    };
218}
219
220inline fn frameMarkStart(comptime name: [:0]const u8) void {
221    if (!enable) return;
222    ___tracy_emit_frame_mark_start(name.ptr);
223}
224
225inline fn frameMarkEnd(comptime name: [:0]const u8) void {
226    if (!enable) return;
227    ___tracy_emit_frame_mark_end(name.ptr);
228}
229
230extern fn ___tracy_emit_frame_mark_start(name: [*:0]const u8) void;
231extern fn ___tracy_emit_frame_mark_end(name: [*:0]const u8) void;
232
233inline fn alloc(ptr: [*]u8, len: usize) void {
234    if (!enable) return;
235
236    if (enable_callstack) {
237        ___tracy_emit_memory_alloc_callstack(ptr, len, callstack_depth, 0);
238    } else {
239        ___tracy_emit_memory_alloc(ptr, len, 0);
240    }
241}
242
243inline fn allocNamed(ptr: [*]u8, len: usize, comptime name: [:0]const u8) void {
244    if (!enable) return;
245
246    if (enable_callstack) {
247        ___tracy_emit_memory_alloc_callstack_named(ptr, len, callstack_depth, 0, name.ptr);
248    } else {
249        ___tracy_emit_memory_alloc_named(ptr, len, 0, name.ptr);
250    }
251}
252
253inline fn free(ptr: [*]u8) void {
254    if (!enable) return;
255
256    if (enable_callstack) {
257        ___tracy_emit_memory_free_callstack(ptr, callstack_depth, 0);
258    } else {
259        ___tracy_emit_memory_free(ptr, 0);
260    }
261}
262
263inline fn freeNamed(ptr: [*]u8, comptime name: [:0]const u8) void {
264    if (!enable) return;
265
266    if (enable_callstack) {
267        ___tracy_emit_memory_free_callstack_named(ptr, callstack_depth, 0, name.ptr);
268    } else {
269        ___tracy_emit_memory_free_named(ptr, 0, name.ptr);
270    }
271}
272
273extern fn ___tracy_emit_zone_begin(
274    srcloc: *const ___tracy_source_location_data,
275    active: c_int,
276) ___tracy_c_zone_context;
277extern fn ___tracy_emit_zone_begin_callstack(
278    srcloc: *const ___tracy_source_location_data,
279    depth: c_int,
280    active: c_int,
281) ___tracy_c_zone_context;
282extern fn ___tracy_emit_zone_text(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void;
283extern fn ___tracy_emit_zone_name(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void;
284extern fn ___tracy_emit_zone_color(ctx: ___tracy_c_zone_context, color: u32) void;
285extern fn ___tracy_emit_zone_value(ctx: ___tracy_c_zone_context, value: u64) void;
286extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
287extern fn ___tracy_emit_memory_alloc(ptr: *const anyopaque, size: usize, secure: c_int) void;
288extern fn ___tracy_emit_memory_alloc_callstack(ptr: *const anyopaque, size: usize, depth: c_int, secure: c_int) void;
289extern fn ___tracy_emit_memory_free(ptr: *const anyopaque, secure: c_int) void;
290extern fn ___tracy_emit_memory_free_callstack(ptr: *const anyopaque, depth: c_int, secure: c_int) void;
291extern fn ___tracy_emit_memory_alloc_named(ptr: *const anyopaque, size: usize, secure: c_int, name: [*:0]const u8) void;
292extern fn ___tracy_emit_memory_alloc_callstack_named(ptr: *const anyopaque, size: usize, depth: c_int, secure: c_int, name: [*:0]const u8) void;
293extern fn ___tracy_emit_memory_free_named(ptr: *const anyopaque, secure: c_int, name: [*:0]const u8) void;
294extern fn ___tracy_emit_memory_free_callstack_named(ptr: *const anyopaque, depth: c_int, secure: c_int, name: [*:0]const u8) void;
295extern fn ___tracy_emit_message(txt: [*]const u8, size: usize, callstack: c_int) void;
296extern fn ___tracy_emit_messageL(txt: [*:0]const u8, callstack: c_int) void;
297extern fn ___tracy_emit_messageC(txt: [*]const u8, size: usize, color: u32, callstack: c_int) void;
298extern fn ___tracy_emit_messageLC(txt: [*:0]const u8, color: u32, callstack: c_int) void;
299extern fn ___tracy_emit_frame_mark(name: ?[*:0]const u8) void;
300
301const ___tracy_source_location_data = extern struct {
302    name: ?[*:0]const u8,
303    function: [*:0]const u8,
304    file: [*:0]const u8,
305    line: u32,
306    color: u32,
307};
308