From 9c7656d59a658a04141e14b54adf865f33bf8b16 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 26 Feb 2023 14:00:39 +0000 Subject: [PATCH] Add `core:prof/spall` --- core/prof/spall/spall.odin | 208 +++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 core/prof/spall/spall.odin diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin new file mode 100644 index 000000000..ff8c69222 --- /dev/null +++ b/core/prof/spall/spall.odin @@ -0,0 +1,208 @@ +package prof_spall + +import "core:os" +import "core:time" +import "core:intrinsics" +import "core:mem" + +// File Format + +MANUAL_MAGIC :: u64le(0x0BADF00D) + +Manual_Header :: struct #packed { + magic: u64le, + version: u64le, + timestamp_scale: f64le, + reserved: u64le, +} + +Manual_Event_Type :: enum u8 { + Invalid = 0, + + Begin = 3, + End = 4, + Instant = 5, + + Pad_Skip = 7, +} + +Begin_Event :: struct #packed { + type: Manual_Event_Type, + category: u8, + pid: u32le, + tid: u32le, + ts: f64le, + name_len: u8, + args_len: u8, +} +BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255 + +End_Event :: struct #packed { + type: Manual_Event_Type, + pid: u32le, + tid: u32le, + ts: f64le, +} + +Pad_Skip :: struct #packed { + type: Manual_Event_Type, + size: u32le, +} + +// User Interface + +Context :: struct { + precise_time: bool, + timestamp_scale: f64, + fd: os.Handle, +} + +Buffer :: struct { + data: []u8, + head: int, + tid: u32, + pid: u32, +} + +BUFFER_DEFAULT_SIZE :: 0x10_0000 + + +context_create :: proc(filename: string) -> (ctx: Context, ok: bool) #optional_ok { + fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600) + if err != os.ERROR_NONE { + return + } + ctx.fd = fd + + freq, freq_ok := time.tsc_frequency() + ctx.precise_time = freq_ok + ctx.timestamp_scale = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1 + + temp := [size_of(Manual_Header)]u8{} + _build_header(temp[:], ctx.timestamp_scale) + os.write(ctx.fd, temp[:]) + ok = true + return +} + +context_destroy :: proc(ctx: ^Context) { + if ctx == nil { + return + } + + os.close(ctx.fd) + ctx^ = Context{} +} + +buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok { + assert(len(data) > 0) + buffer.data = data + buffer.tid = tid + buffer.pid = pid + buffer.head = 0 + ok = true + return +} + +buffer_flush :: proc(ctx: ^Context, buffer: ^Buffer) { + os.write(ctx.fd, buffer.data[:buffer.head]) + buffer.head = 0 +} + +buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) { + buffer_flush(ctx, buffer) + + buffer^ = Buffer{} +} + + + +@(deferred_in=_scoped_buffer_end) +SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool { + _buffer_begin(ctx, buffer, name, args, location) + return true +} + +@(private) +_scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) { + _buffer_end(ctx, buffer) +} + + +_trace_now :: proc "contextless" (ctx: ^Context) -> f64 { + if !ctx.precise_time { + return f64(time.tick_now()._nsec) / 1_000 + } + + return f64(intrinsics.read_cycle_counter()) +} + +_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { + header_size = size_of(Manual_Header) + if header_size > len(buffer) { + return 0, false + } + + hdr := (^Manual_Header)(raw_data(buffer)) + hdr.magic = MANUAL_MAGIC + hdr.version = 1 + hdr.timestamp_scale = f64le(timestamp_scale) + hdr.reserved = 0 + ok = true + return +} + +_build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok { + ev := (^Begin_Event)(raw_data(buffer)) + name_len := min(len(name), 255) + args_len := min(len(args), 255) + + event_size = size_of(Begin_Event) + name_len + args_len + if event_size > len(buffer) { + return 0, false + } + + ev.type = .Begin + ev.pid = u32le(pid) + ev.tid = u32le(tid) + ev.ts = f64le(ts) + ev.name_len = u8(name_len) + ev.args_len = u8(args_len) + mem.copy(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len) + mem.copy(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len) + ok = true + return +} + +_build_end :: proc(buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok { + ev := (^End_Event)(raw_data(buffer)) + event_size = size_of(End_Event) + if event_size > len(buffer) { + return 0, false + } + + ev.type = .End + ev.pid = u32le(pid) + ev.tid = u32le(tid) + ev.ts = f64le(ts) + ok = true + return +} + +_buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) { + if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) { + buffer_flush(ctx, buffer) + } + name := location.procedure if name == "" else name + buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid) +} + +_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) { + ts := _trace_now(ctx) + + if buffer.head + size_of(End_Event) > len(buffer.data) { + buffer_flush(ctx, buffer) + } + + buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid) +}