diff options
| author | gingerBill <bill@gingerbill.org> | 2024-04-28 12:43:27 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-04-28 12:43:27 +0100 |
| commit | 6c185a5dca739b4aab034449f0627925e0137173 (patch) | |
| tree | ab4cc374aabc0657bf64559b77cc4da4e8c0283d /core/debug | |
| parent | f428f26c8e18a4f74b396adf6f4d21526c3c9d5e (diff) | |
Add `core:debug/trace` for Linux
Diffstat (limited to 'core/debug')
| -rw-r--r-- | core/debug/trace/trace_linux.odin | 191 | ||||
| -rw-r--r-- | core/debug/trace/trace_nil.odin | 2 |
2 files changed, 192 insertions, 1 deletions
diff --git a/core/debug/trace/trace_linux.odin b/core/debug/trace/trace_linux.odin new file mode 100644 index 000000000..529a83470 --- /dev/null +++ b/core/debug/trace/trace_linux.odin @@ -0,0 +1,191 @@ +//+private file +//+build linux +package debug_trace + +import "base:runtime" +import "core:strings" +import "core:fmt" +import "core:c" + +// NOTE: Relies on C++23 which adds <stacktrace> and becomes ABI and that can be used +foreign import stdcpplibbacktrace "system:stdc++_libbacktrace" + +foreign import libdl "system:dl" + +backtrace_state :: struct {} +backtrace_error_callback :: proc "c" (data: rawptr, msg: cstring, errnum: c.int) +backtrace_simple_callback :: proc "c" (data: rawptr, pc: uintptr) -> c.int +backtrace_full_callback :: proc "c" (data: rawptr, pc: uintptr, filename: cstring, lineno: c.int, function: cstring) -> c.int +backtrace_syminfo_callback :: proc "c" (data: rawptr, pc: uintptr, symname: cstring, symval: uintptr, symsize: uintptr) + +@(default_calling_convention="c", link_prefix="__glibcxx_") +foreign stdcpplibbacktrace { + backtrace_create_state :: proc( + filename: cstring, + threaded: c.int, + error_callback: backtrace_error_callback, + data: rawptr, + ) -> ^backtrace_state --- + backtrace_simple :: proc( + state: ^backtrace_state, + skip: c.int, + callback: backtrace_simple_callback, + error_callback: backtrace_error_callback, + data: rawptr, + ) -> c.int --- + backtrace_pcinfo :: proc( + state: ^backtrace_state, + pc: uintptr, + callback: backtrace_full_callback, + error_callback: backtrace_error_callback, + data: rawptr, + ) -> c.int --- + backtrace_syminfo :: proc( + state: ^backtrace_state, + addr: uintptr, + callback: backtrace_syminfo_callback, + error_callback: backtrace_error_callback, + data: rawptr, + ) -> c.int --- + + // NOTE(bill): this is technically an internal procedure, but it is exposed + backtrace_free :: proc( + state: ^backtrace_state, + p: rawptr, + size: c.size_t, // unused + error_callback: backtrace_error_callback, // unused + data: rawptr, // unused + ) --- +} + +Dl_info :: struct { + dli_fname: cstring, + dli_fbase: rawptr, + dli_sname: cstring, + dli_saddr: rawptr, +} + +@(default_calling_convention="c") +foreign libdl { + dladdr :: proc(addr: rawptr, info: ^Dl_info) -> c.int --- +} + +@(private="package") +_Context :: struct { + state: ^backtrace_state, +} + +@(private="package") +_init :: proc(ctx: ^Context) -> (ok: bool) { + defer if !ok do destroy(ctx) + + ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx) + return ctx.impl.state != nil +} + +@(private="package") +_destroy :: proc(ctx: ^Context) -> bool { + if ctx != nil { + backtrace_free(ctx.impl.state, nil, 0, nil, nil) + } + return true +} + +@(private="package") +_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> (frames: []Frame) { + Backtrace_Context :: struct { + ctx: ^Context, + rt_ctx: runtime.Context, + frames: [MAX_FRAMES]Frame, + frame_count: int, + } + + btc := &Backtrace_Context{ + ctx = ctx, + rt_ctx = context, + } + backtrace_simple( + ctx.impl.state, + c.int(skip + 2), + proc "c" (user: rawptr, address: uintptr) -> c.int { + btc := (^Backtrace_Context)(user) + address := Frame(address) + if address == 0 { + return 1 + } + btc.frames[btc.frame_count] = address + btc.frame_count += 1 + return 0 + }, + nil, + btc, + ) + + res := btc.frames[:btc.frame_count] + if len(res) > 0 { + frames = make([]Frame, btc.frame_count, allocator) + copy(frames, res) + } + return +} + +@(private="package") +_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> Frame_Location { + Backtrace_Context :: struct { + rt_ctx: runtime.Context, + allocator: runtime.Allocator, + frame: Frame_Location, + } + + btc := &Backtrace_Context{ + rt_ctx = context, + allocator = allocator, + } + done := backtrace_pcinfo( + ctx.impl.state, + uintptr(frame), + proc "c" (data: rawptr, address: uintptr, file: cstring, line: c.int, symbol: cstring) -> c.int { + btc := (^Backtrace_Context)(data) + context = btc.rt_ctx + + frame := &btc.frame + + if file != nil { + frame.file_path = strings.clone_from_cstring(file, btc.allocator) + } else if info: Dl_info; dladdr(rawptr(address), &info) != 0 && info.dli_fname != "" { + frame.file_path = strings.clone_from_cstring(info.dli_fname, btc.allocator) + } + if symbol != nil { + frame.procedure = strings.clone_from_cstring(symbol, btc.allocator) + } else if info: Dl_info; dladdr(rawptr(address), &info) != 0 && info.dli_sname != "" { + frame.procedure = strings.clone_from_cstring(info.dli_sname, btc.allocator) + } else { + frame.procedure = fmt.aprintf("(procedure: 0x%x)", allocator=btc.allocator) + } + frame.line = i32(line) + return 0 + }, + nil, + btc, + ) + if done != 0 { + return btc.frame + } + + // NOTE(bill): pcinfo cannot resolve, but it might be possible to get the procedure name at least + backtrace_syminfo( + ctx.impl.state, + uintptr(frame), + proc "c" (data: rawptr, address: uintptr, symbol: cstring, _ignore0, _ignore1: uintptr) { + if symbol != nil { + btc := (^Backtrace_Context)(data) + context = btc.rt_ctx + btc.frame.procedure = strings.clone_from_cstring(symbol, btc.allocator) + } + }, + nil, + btc, + ) + + return btc.frame +}
\ No newline at end of file diff --git a/core/debug/trace/trace_nil.odin b/core/debug/trace/trace_nil.odin index ab08aee04..926bd18cc 100644 --- a/core/debug/trace/trace_nil.odin +++ b/core/debug/trace/trace_nil.odin @@ -1,4 +1,4 @@ -//+build !windows +//+build !windows !linux package debug_trace _Context :: struct { |