123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- #+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
- }
|