diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2025-07-31 12:37:09 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-31 12:37:09 +0100 |
| commit | 077ba0b6cbf0359a73cb4d50c54258012b5f2a23 (patch) | |
| tree | 992a85f8650159d374ddba5ae819433376f0dda4 /core | |
| parent | 9b8223dd69d70c6f44fd9c65194f895cdcae4940 (diff) | |
| parent | 3e10684630bd6cd0602124cf3cb574345483eb93 (diff) | |
Merge pull request #5513 from colrdavidson/spall_v3
update to spall format v3
Diffstat (limited to 'core')
| -rw-r--r-- | core/prof/spall/spall.odin | 138 |
1 files changed, 98 insertions, 40 deletions
diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 12f082b2c..16b809359 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -8,29 +8,36 @@ import "base:intrinsics" MANUAL_MAGIC :: u64le(0x0BADF00D) -Manual_Header :: struct #packed { +Manual_Stream_Header :: struct #packed { magic: u64le, version: u64le, timestamp_scale: f64le, reserved: u64le, } +Manual_Buffer_Header :: struct #packed { + size: u32le, + tid: u32le, + pid: u32le, + first_ts: u64le, +} + Manual_Event_Type :: enum u8 { - Invalid = 0, + Invalid = 0, + + Begin = 3, + End = 4, + Instant = 5, - Begin = 3, - End = 4, - Instant = 5, + Pad_Skip = 7, - Pad_Skip = 7, + Name_Process = 8, + Name_Thread = 9, } Begin_Event :: struct #packed { type: Manual_Event_Type, - category: u8, - pid: u32le, - tid: u32le, - ts: f64le, + ts: u64le, name_len: u8, args_len: u8, } @@ -38,9 +45,7 @@ BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255 End_Event :: struct #packed { type: Manual_Event_Type, - pid: u32le, - tid: u32le, - ts: f64le, + ts: u64le, } Pad_Skip :: struct #packed { @@ -48,6 +53,12 @@ Pad_Skip :: struct #packed { size: u32le, } +Name_Event :: struct #packed { + type: Manual_Event_Type, + name_len: u8, +} +NAME_EVENT_MAX :: size_of(Name_Event) + 255 + // User Interface Context :: struct { @@ -61,6 +72,7 @@ Buffer :: struct { head: int, tid: u32, pid: u32, + first_ts: u64, } BUFFER_DEFAULT_SIZE :: 0x10_0000 @@ -76,8 +88,8 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam ctx.precise_time = precise_time ctx.timestamp_scale = timestamp_scale - temp := [size_of(Manual_Header)]u8{} - _build_header(temp[:], ctx.timestamp_scale) + temp := [size_of(Manual_Stream_Header)]u8{} + _build_stream_header(temp[:], ctx.timestamp_scale) os.write(ctx.fd, temp[:]) ok = true return @@ -85,7 +97,7 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> (ctx: Context, ok: bool) #optional_ok { freq, freq_ok := time.tsc_frequency(sleep) - timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1 + timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000_000) if freq_ok else 1 return context_create_with_scale(filename, freq_ok, timestamp_scale) } @@ -102,23 +114,36 @@ context_destroy :: proc(ctx: ^Context) { buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok { assert(len(data) >= 1024) - buffer.data = data - buffer.tid = tid - buffer.pid = pid - buffer.head = 0 + buffer.data = data + buffer.tid = tid + buffer.pid = pid + buffer.first_ts = 0 + buffer.head = size_of(Manual_Buffer_Header) ok = true return } @(no_instrumentation) buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ { + if len(buffer.data) == 0 { + return + } + + buffer_size := buffer.head - size_of(Manual_Buffer_Header) + hdr := (^Manual_Buffer_Header)(raw_data(buffer.data)) + hdr.size = u32le(buffer_size) + hdr.pid = u32le(buffer.pid) + hdr.tid = u32le(buffer.tid) + hdr.first_ts = u64le(buffer.first_ts) + start := _trace_now(ctx) write(ctx.fd, buffer.data[:buffer.head]) - buffer.head = 0 + buffer.head = size_of(Manual_Buffer_Header) end := _trace_now(ctx) - buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start, buffer.tid, buffer.pid) - buffer.head += _build_end(buffer.data[buffer.head:], end, buffer.tid, buffer.pid) + buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start) + buffer.head += _build_end(buffer.data[buffer.head:], end) + buffer.first_ts = end } buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) { @@ -141,24 +166,24 @@ _scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #c } @(no_instrumentation) -_trace_now :: proc "contextless" (ctx: ^Context) -> f64 { +_trace_now :: proc "contextless" (ctx: ^Context) -> u64 { if !ctx.precise_time { - return f64(tick_now()) / 1_000 + return u64(tick_now()) } - return f64(intrinsics.read_cycle_counter()) + return u64(intrinsics.read_cycle_counter()) } @(no_instrumentation) -_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { - header_size = size_of(Manual_Header) +_build_stream_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { + header_size = size_of(Manual_Stream_Header) if header_size > len(buffer) { return 0, false } - hdr := (^Manual_Header)(raw_data(buffer)) + hdr := (^Manual_Stream_Header)(raw_data(buffer)) hdr.magic = MANUAL_MAGIC - hdr.version = 1 + hdr.version = 3 hdr.timestamp_scale = f64le(timestamp_scale) hdr.reserved = 0 ok = true @@ -166,7 +191,7 @@ _build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (hea } @(no_instrumentation) -_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { +_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: u64) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { ev := (^Begin_Event)(raw_data(buffer)) name_len := min(len(name), 255) args_len := min(len(args), 255) @@ -177,9 +202,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar } ev.type = .Begin - ev.pid = u32le(pid) - ev.tid = u32le(tid) - ev.ts = f64le(ts) + ev.ts = u64le(ts) ev.name_len = u8(name_len) ev.args_len = u8(args_len) intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len) @@ -190,7 +213,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar } @(no_instrumentation) -_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok { +_build_end :: proc "contextless" (buffer: []u8, ts: u64) -> (event_size: int, ok: bool) #optional_ok { ev := (^End_Event)(raw_data(buffer)) event_size = size_of(End_Event) if event_size > len(buffer) { @@ -198,9 +221,28 @@ _build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> } ev.type = .End - ev.pid = u32le(pid) - ev.tid = u32le(tid) - ev.ts = f64le(ts) + ev.ts = u64le(ts) + ok = true + + return +} + +@(no_instrumentation) +_build_name_event :: #force_inline proc "contextless" (buffer: []u8, name: string, type: Manual_Event_Type) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { + ev := (^Name_Event)(raw_data(buffer)) + name_len := min(len(name), 255) + + event_size = size_of(Name_Event) + name_len + if event_size > len(buffer) { + return 0, false + } + if type != .Name_Process && type != .Name_Thread { + return 0, false + } + + ev.type = type + ev.name_len = u8(name_len) + intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Name_Event):]), raw_data(name), name_len) ok = true return @@ -212,7 +254,7 @@ _buffer_begin :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: strin 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.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx)) } @(no_instrumentation) @@ -223,7 +265,23 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch buffer_flush(ctx, buffer) } - buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid) + buffer.head += _build_end(buffer.data[buffer.head:], ts) +} + +@(no_instrumentation) +_buffer_name_thread :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ { + if buffer.head + NAME_EVENT_MAX > len(buffer.data) { + buffer_flush(ctx, buffer) + } + buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Thread) +} + +@(no_instrumentation) +_buffer_name_process :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ { + if buffer.head + NAME_EVENT_MAX > len(buffer.data) { + buffer_flush(ctx, buffer) + } + buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Process) } @(no_instrumentation) |