Files
Odin/core/debug/trace/trace_cpp.odin

195 lines
5.2 KiB
Odin

#+private file
#+build linux, darwin
package debug_trace
import "base:intrinsics"
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 { 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 "contextless" (ctx: ^Context, skip: uint, frames_buffer: []Frame) -> (frames: []Frame) {
Backtrace_Context :: struct {
ctx: ^Context,
frames: []Frame,
frame_count: int,
}
btc := &Backtrace_Context{
ctx = ctx,
frames = frames_buffer,
}
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
}
if btc.frame_count == len(btc.frames) {
return 1
}
btc.frames[btc.frame_count] = address
btc.frame_count += 1
return 0
},
nil,
btc,
)
if btc.frame_count > 0 {
frames = btc.frames[:btc.frame_count]
}
return
}
@(private="package")
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> Frame_Location {
intrinsics.atomic_store(&ctx.in_resolve, true)
defer intrinsics.atomic_store(&ctx.in_resolve, false)
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
}