| 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; |
| 3 | |
| 4 | const builtin = @import("builtin"); |
| 5 | const build_options = @import("build_options"); |
| 6 | |
| 7 | pub const enable = if (builtin.is_test) false else build_options.enable_tracy; |
| 8 | pub const enable_allocation = enable and build_options.enable_tracy_allocation; |
| 9 | pub const enable_callstack = enable and build_options.enable_tracy_callstack; |
| 10 | pub const callstack_depth = if (enable_callstack) build_options.tracy_callstack_depth else 0; |
| 11 | |
| 12 | const ___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 | |
| 43 | pub 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 | |
| 75 | pub 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 | |
| 91 | pub 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 | |
| 107 | pub inline fn fiberEnter(fiber: [*:0]const u8) void { |
| 108 | if (!enable) return; |
| 109 | ___tracy_fiber_enter(fiber); |
| 110 | } |
| 111 | |
| 112 | pub inline fn fiberLeave() void { |
| 113 | if (!enable) return; |
| 114 | ___tracy_fiber_leave(); |
| 115 | } |
| 116 | |
| 117 | pub 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 | |
| 129 | pub inline fn plotInt(comptime name: [*:0]const u8, val: i64) void { |
| 130 | if (!enable) return; |
| 131 | ___tracy_emit_plot_int(name, val); |
| 132 | } |
| 133 | |
| 134 | pub 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 |
| 206 | pub 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 |
| 211 | pub 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 | |
| 216 | pub inline fn messageCopy(msg: []const u8) void { |
| 217 | messageColorCopy(msg, 0); |
| 218 | } |
| 219 | |
| 220 | pub 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. |
| 229 | var tracy_arena: std.heap.ArenaAllocator = if (enable) .init(std.heap.page_allocator); |
| 230 | |
| 231 | pub 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 | |
| 238 | pub 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 | |
| 246 | pub 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 | |
| 255 | pub 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 | |
| 274 | extern fn ___tracy_emit_frame_mark_start(name: [*:0]const u8) void; |
| 275 | extern fn ___tracy_emit_frame_mark_end(name: [*:0]const u8) void; |
| 276 | extern fn ___tracy_emit_zone_begin_callstack(srcloc: *const ___tracy_source_location_data, depth: i32, active: i32) ___tracy_c_zone_context; |
| 277 | extern fn ___tracy_emit_zone_text(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void; |
| 278 | extern fn ___tracy_emit_zone_name(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void; |
| 279 | extern fn ___tracy_emit_zone_color(ctx: ___tracy_c_zone_context, color: u32) void; |
| 280 | extern fn ___tracy_emit_zone_value(ctx: ___tracy_c_zone_context, value: u64) void; |
| 281 | extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void; |
| 282 | extern fn ___tracy_emit_memory_alloc_callstack(ptr: *const anyopaque, size: usize, depth: i32, secure: i32) void; |
| 283 | extern fn ___tracy_emit_memory_free_callstack(ptr: *const anyopaque, depth: i32, secure: i32) void; |
| 284 | extern fn ___tracy_emit_logString(severity: MessageSeverity, color: i32, callstack_depth: i32, size: usize, txt: [*]const u8) void; |
| 285 | extern fn ___tracy_emit_logStringL(severity: MessageSeverity, color: i32, callstack_depth: i32, txt: [*:0]const u8) void; |
| 286 | extern fn ___tracy_emit_plot_int(name: [*:0]const u8, val: i64) void; |
| 287 | extern fn ___tracy_emit_plot_config(name: [*:0]const u8, format: PlotConfig.Format, mode: PlotConfig.Mode, fill: i32, color: u32) void; |
| 288 | extern fn ___tracy_fiber_enter(fiber: [*:0]const u8) void; |
| 289 | extern fn ___tracy_fiber_leave() void; |
| 290 | |
| 291 | const ___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 | }; |