Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build.zig",
"build.zig.zon",
"src",
"example",
"examples",
"README.md",
"LICENSE",
},
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion example/build.zig.zon → examples/basic/build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"src",
},
.dependencies = .{
.tracy = .{ .path = ".." },
.tracy = .{ .path = "../.." },
},
}
21 changes: 16 additions & 5 deletions example/src/main.zig → examples/basic/src/main.zig
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
const std = @import("std");
const builtin = @import("builtin");
const tracy = @import("tracy");
const windows = std.os.windows;

var finalise_threads: std.Thread.ResetEvent = .{};

fn handleSigInt(_: c_int) callconv(.C) void {
finalise_threads.set();
}

fn handleCtrlEvent(_: windows.DWORD) callconv(windows.WINAPI) windows.BOOL {
finalise_threads.set();
return windows.TRUE;
}

pub fn main() !void {
tracy.setThreadName("Main");
defer tracy.message("Graceful main thread exit");

std.posix.sigaction(std.posix.SIG.INT, &.{
.handler = .{ .handler = handleSigInt },
.mask = std.posix.empty_sigset,
.flags = 0,
}, null);
if (builtin.os.tag == .windows) {
_ = windows.kernel32.SetConsoleCtrlHandler(handleCtrlEvent, windows.TRUE);
} else {
std.posix.sigaction(std.posix.SIG.INT, &.{
.handler = .{ .handler = handleSigInt },
.mask = std.posix.empty_sigset,
.flags = 0,
}, null);
}

const other_thread = try std.Thread.spawn(.{}, otherThread, .{});
defer other_thread.join();
Expand Down
36 changes: 36 additions & 0 deletions examples/mutex/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// Keep tracy enabled by default for the demo; real projects should opt-in explicitly.
const tracy_enable = b.option(bool, "tracy_enable", "Enable profiling") orelse true;

const tracy = b.dependency("tracy", .{
.target = target,
.optimize = optimize,
.tracy_enable = tracy_enable,
});

const exe = b.addExecutable(.{
.name = "tracy-mutex-demo",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("tracy", tracy.module("tracy"));
exe.linkLibrary(tracy.artifact("tracy"));
exe.linkLibCpp();
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());

if (b.args) |args| {
run_cmd.addArgs(args);
}

const run_step = b.step("run", "Run the mutex demo");
run_step.dependOn(&run_cmd.step);
}
14 changes: 14 additions & 0 deletions examples/mutex/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = .zig_tracy_mutex_example,
.fingerprint = 0xd6986380c2c18822,
.version = "0.0.1",
.minimum_zig_version = "0.14.1",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
.dependencies = .{
.tracy = .{ .path = "../.." },
},
}
93 changes: 93 additions & 0 deletions examples/mutex/src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const std = @import("std");
const builtin = @import("builtin");
const tracy = @import("tracy");
const windows = std.os.windows;

var finalise_threads: std.Thread.ResetEvent = .{};
const worker_count = 6;

fn handleSigInt(_: c_int) callconv(.C) void {
finalise_threads.set();
}

fn handleCtrlEvent(_: windows.DWORD) callconv(windows.WINAPI) windows.BOOL {
finalise_threads.set();
return windows.TRUE;
}

pub fn main() !void {
tracy.setThreadName("Main");
defer tracy.message("Graceful main thread exit");

if (builtin.os.tag == .windows) {
_ = windows.kernel32.SetConsoleCtrlHandler(handleCtrlEvent, windows.TRUE);
} else {
std.posix.sigaction(std.posix.SIG.INT, &.{
.handler = .{ .handler = handleSigInt },
.mask = std.posix.empty_sigset,
.flags = 0,
}, null);
}

var shared = SharedState.init();
defer shared.deinit();

var threads: [worker_count]std.Thread = undefined;
var started: usize = 0;
errdefer {
finalise_threads.set();
for (threads[0..started]) |thread| thread.join();
}

for (&threads, 0..) |*thread, idx| {
thread.* = try std.Thread.spawn(.{}, worker, .{ &finalise_threads, &shared, idx });
started += 1;
}

finalise_threads.wait();

for (threads[0..started]) |thread| thread.join();
}

const SharedState = struct {
mutex: tracy.TracingMutex,
counter: u64 = 0,

fn init() SharedState {
return .{
.mutex = tracy.TracingMutex.init(@src(), .{
.name = "Traced mutex",
.color = 0xFF8844,
}),
};
}

fn deinit(self: *SharedState) void {
self.mutex.deinit();
}

fn blockingIncrement(self: *SharedState) u64 {
const zone = tracy.initZone(@src(), .{ .name = "Mutex critical section" });
defer zone.deinit();

self.mutex.lock(@src());
defer self.mutex.unlock();

self.counter += 1;
zone.value(self.counter);
// Hold the lock briefly to force contention to show up in Tracy.
std.time.sleep(50 * std.time.ns_per_ms);
return self.counter;
}
};

fn worker(finalise: *std.Thread.ResetEvent, shared: *SharedState, idx: usize) void {
tracy.setThreadName("Mutex worker");

while (!finalise.isSet()) {
_ = shared.blockingIncrement();

const pause_ns: u64 = (@as(u64, @intCast(idx)) + 1) * 200 * std.time.ns_per_ms;
std.time.sleep(pause_ns);
}
}
106 changes: 103 additions & 3 deletions src/tracy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,12 @@ const ZoneContext = if (options.tracy_enable) extern struct {
pub inline fn value(_: *const ZoneContext, _: u64) void {}
};

pub inline fn initZone(comptime src: std.builtin.SourceLocation, comptime opts: ZoneOptions) ZoneContext {
if (!options.tracy_enable) return .{};
const active: c_int = @intFromBool(opts.active);
pub const SrcLocOptions = struct {
name: ?[]const u8 = null,
color: ?u32 = null,
};

inline fn getSrcLoc(comptime src: std.builtin.SourceLocation, comptime opts: SrcLocOptions) type {
const static = struct {
var src_loc = c.___tracy_source_location_data{
.name = if (opts.name) |name| name.ptr else null,
Expand All @@ -147,6 +149,18 @@ pub inline fn initZone(comptime src: std.builtin.SourceLocation, comptime opts:
// src.line magically is not comptime https://github.com/ziglang/zig/pull/12016#issuecomment-1178092847
static.src_loc.line = src.line;

return static;
}

pub inline fn initZone(comptime src: std.builtin.SourceLocation, comptime opts: ZoneOptions) ZoneContext {
if (!options.tracy_enable) return .{};
const active: c_int = @intFromBool(opts.active);

const static = getSrcLoc(src, .{
.name = opts.name,
.color = opts.color,
});

if (!options.tracy_no_callstack) {
if (options.tracy_callstack) |depth| {
return .{
Expand Down Expand Up @@ -374,3 +388,89 @@ pub const TracingAllocator = struct {
self.parent_allocator.rawFree(buf, buf_align, ret_addr);
}
};

const TracingMutexImpl = struct {
mutex: std.Thread.Mutex,
tracy_lock_ctx: c.TracyCLockCtx,

pub const TracingMutexOptions = struct {
name: ?[]const u8 = null,
color: ?u32 = null,
};

pub fn init(comptime src: std.builtin.SourceLocation, comptime opts: TracingMutexOptions) TracingMutexImpl {
const static = getSrcLoc(src, .{
.name = opts.name,
.color = opts.color,
});

const m: TracingMutexImpl = .{
.mutex = .{},
.tracy_lock_ctx = c.___tracy_announce_lockable_ctx(&static.src_loc),
};

if (opts.name) |name| {
c.___tracy_custom_name_lockable_ctx(m.tracy_lock_ctx, name.ptr, name.len);
}

return m;
}

pub fn deinit(self: *TracingMutex) void {
c.___tracy_terminate_lockable_ctx(self.tracy_lock_ctx);
}

pub fn lock(self: *TracingMutex, comptime src: std.builtin.SourceLocation) void {
_ = c.___tracy_before_lock_lockable_ctx(self.tracy_lock_ctx);
self.mutex.lock();
c.___tracy_after_lock_lockable_ctx(self.tracy_lock_ctx);

const static = getSrcLoc(src, .{});
c.___tracy_mark_lockable_ctx(self.tracy_lock_ctx, &static.src_loc);
}

pub fn tryLock(self: *TracingMutex, comptime src: std.builtin.SourceLocation) bool {
_ = c.___tracy_before_lock_lockable_ctx(self.tracy_lock_ctx);
const result = self.mutex.tryLock();
c.___tracy_after_try_lock_lockable_ctx(self.tracy_lock_ctx, if (result) 1 else 0);

const static = getSrcLoc(src, .{});
c.___tracy_mark_lockable_ctx(self.tracy_lock_ctx, &static.src_loc);

return result;
}

pub fn unlock(self: *TracingMutex) void {
self.mutex.unlock();
c.___tracy_after_unlock_lockable_ctx(self.tracy_lock_ctx);
}
};

pub const TracingMutex = if (options.tracy_enable) TracingMutexImpl else struct {
mutex: std.Thread.Mutex,

pub inline fn init(comptime src: std.builtin.SourceLocation, comptime opts: TracingMutexImpl.TracingMutexOptions) TracingMutex {
_ = src;
_ = opts;

return .{
.mutex = .{},
};
}

pub inline fn deinit(_: *TracingMutex) void {}

pub inline fn lock(self: *TracingMutex, comptime src: std.builtin.SourceLocation) void {
_ = src;
self.mutex.lock();
}

pub inline fn tryLock(self: *TracingMutex, comptime src: std.builtin.SourceLocation) bool {
_ = src;
return self.mutex.tryLock();
}

pub inline fn unlock(self: *TracingMutex) void {
self.mutex.unlock();
}
};