aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-07-31 12:37:09 +0100
committerGitHub <noreply@github.com>2025-07-31 12:37:09 +0100
commit077ba0b6cbf0359a73cb4d50c54258012b5f2a23 (patch)
tree992a85f8650159d374ddba5ae819433376f0dda4 /core
parent9b8223dd69d70c6f44fd9c65194f895cdcae4940 (diff)
parent3e10684630bd6cd0602124cf3cb574345483eb93 (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.odin138
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)