1const std = @import("std");
2const tracer = @import("./mod.zig");
3const alloc = std.heap.c_allocator;
4const log = std.log.scoped(.tracer);
5const root = @import("root");
6const nfs = @import("nfs");
7const nio = @import("nio");
8const time = @import("time");
9const linux = @import("sys-linux");
10
11var pid: linux.pid_t = undefined;
12threadlocal var tid: linux.pid_t = undefined;
13threadlocal var path: [:0]const u8 = undefined;
14threadlocal var file: nfs.File = undefined;
15threadlocal var buffered_writer: nio.BufferedWriter(4096, nfs.File) = undefined;
16
17pub fn init(args: struct {}) !void {
18 _ = args;
19 pid = linux.getpid();
20}
21
22pub fn deinit() void {
23 //
24}
25
26pub fn init_thread(args: struct { nfs.Dir }) !void {
27 const dir = args[0];
28 tid = linux.gettid();
29
30 path = try nio.fmt.allocPrintZ(alloc, "{d}.{d}.spall", .{ pid, tid });
31 file = try dir.createFile(path, .{});
32 buffered_writer = .init(file);
33
34 try buffered_writer.writeStruct(Header{});
35}
36
37pub fn deinit_thread() void {
38 defer alloc.free(path);
39 defer file.close();
40
41 buffered_writer.flush() catch {};
42}
43
44pub fn trace_begin(src: std.builtin.SourceLocation, comptime ifmt: []const u8, iargs: anytype) void {
45 const fmt = "{s}:{d}:{d} ({s})" ++ ifmt;
46 const args = .{ src.file, src.line, src.column, src.fn_name };
47 buffered_writer.writeStruct(BeginEvent{
48 .pid = @intCast(pid),
49 .tid = @intCast(tid),
50 .time = @floatFromInt(time.microTimestamp()),
51 .name_len = @truncate(std.fmt.count(fmt, args ++ iargs)),
52 .args_len = 0,
53 }) catch return;
54 buffered_writer.print(fmt, args ++ iargs) catch return;
55}
56
57pub fn trace_end(ctx: tracer.Ctx) void {
58 _ = ctx;
59 buffered_writer.writeStruct(EndEvent{
60 .pid = @intCast(pid),
61 .tid = @intCast(tid),
62 .time = @floatFromInt(time.microTimestamp()),
63 }) catch return;
64}
65
66pub const Data = void;
67
68// https://github.com/colrdavidson/spall-web/blob/1d4610a1fe9aaaf2e071327a1142a498f3436bdc/formats/spall/spall.odin
69// https://github.com/colrdavidson/spall-web/blob/1d4610a1fe9aaaf2e071327a1142a498f3436bdc/tools/json2bin/json2bin.odin
70// https://github.com/colrdavidson/spall-web/blob/1d4610a1fe9aaaf2e071327a1142a498f3436bdc/tools/upconvert/main.odin
71
72// package spall
73
74// MAGIC :: u64(0x0BADF00D)
75const magic: u64 = 0x0BADF00D;
76
77// V1_Header :: struct #packed {
78// magic: u64,
79// version: u64,
80// timestamp_unit: f64,
81// must_be_0: u64,
82// }
83const Header = extern struct {
84 magic: u64 align(1) = magic,
85 version: u64 align(1) = 1,
86 timestamp_unit: f64 align(1) = 1.0,
87 must_be_0: u64 align(1) = 0,
88};
89
90// V1_Event_Type :: enum u8 {
91// Invalid = 0,
92// Custom_Data = 1, // Basic readers can skip this.
93// StreamOver = 2,
94// Begin = 3,
95// End = 4,
96// Instant = 5,
97// Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency.
98// }
99const EventType = enum(u8) {
100 invalid = 0,
101 custom_data = 1,
102 stream_over = 2,
103 begin = 3,
104 end = 4,
105 instant = 5,
106 overwrite_timestamp = 6,
107};
108
109// V1_Begin_Event :: struct #packed {
110// type: V1_Event_Type,
111// category: u8,
112// pid: u32,
113// tid: u32,
114// time: f64,
115// name_len: u8,
116// args_len: u8,
117// }
118const BeginEvent = extern struct {
119 type: EventType align(1) = .begin,
120 category: u8 align(1) = 0,
121 pid: u32 align(1),
122 tid: u32 align(1),
123 time: f64 align(1),
124 name_len: u8 align(1),
125 args_len: u8 align(1),
126};
127
128// V1_End_Event :: struct #packed {
129// type: V1_Event_Type,
130// pid: u32,
131// tid: u32,
132// time: f64,
133// }
134const EndEvent = extern struct {
135 type: EventType align(1) = .end,
136 pid: u32 align(1),
137 tid: u32 align(1),
138 time: f64 align(1),
139};