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