#+vet explicit-allocators package back import "base:runtime" import "core:fmt" import "core:io" import "core:os" import "core:sync" import "core:text/table" // Size of a constant backtrace, as used by the tracking allocator for example. BACKTRACE_SIZE :: #config(BACKTRACE_SIZE, 16) // For targets that do not have native support (using debug info), // backtraces are done through instrumentation, Odin only allows one enter/exit instrumentation // procedure though, so you can set this to true, add your own instrumentation procs, and have // them call `back.other_instrumentation_enter` and `back.other_instrumentation_exit` to hook // up the backtraces. // The custom proc must have `#force_inline`. OTHER_CUSTOM_INSTRUMENTATION :: #config(BACK_OTHER_CUSTOM_INSTRUMENTATION, false) // Force the fallback instrumentation based implementation instead of debug info based. FORCE_FALLBACK :: #config(BACK_FORCE_FALLBACK, false) // Fallback requires a single module (subtle bugs with multiple modules and instrumentation in Odin), // and at least -o:minimal (#force_inline has to actually inline). _COULD_USE_FALLBACK_WITHOUT_ERROR :: !ODIN_USE_SEPARATE_MODULES && ODIN_OPTIMIZATION_MODE >= .Minimal // Use the fallback (instrumentation based) implementation: // if it is forced, or it can be used without error and debug info is off, or if the target has no debug info based support. USE_FALLBACK :: FORCE_FALLBACK || (_COULD_USE_FALLBACK_WITHOUT_ERROR && !ODIN_DEBUG) || (ODIN_OS != .Darwin && ODIN_OS != .Linux && ODIN_OS != .Windows) ADDR2LINE_PATH :: #config(TRACE_ADDR2LINE_PATH, "addr2line") Trace :: []Trace_Entry Trace_Const :: struct { trace: [BACKTRACE_SIZE]Trace_Entry, len: int, } // Platform specific. Trace_Entry :: _Trace_Entry Line :: struct { location: string, symbol: string, } // TODO: improve errors. Lines_Error :: enum { None, Parse_Address_Fail, Addr2line_Unexpected_EOF, Addr2line_Output_Error, Addr2line_Unresolved, Addr2line_Process_Error, Out_Of_Memory, Info_Not_Found, } // TODO: arbitrary skip (argument). trace :: #force_no_inline proc() -> (bt: Trace_Const) { bt.len = _trace(bt.trace[:]) return } trace_n :: #force_no_inline proc(max_len: i32, allocator := context.allocator) -> Trace { bt := make([]Trace_Entry, max_len, allocator) n := _trace(bt[:]) return bt[:n] } trace_fill :: #force_no_inline proc(buf: Trace) -> int { return _trace(buf) } trace_n_destroy :: proc(b: Trace, allocator := context.allocator) { delete(b, allocator) } // Processes the message trying to get more/useful information. // This adds file and line information if the program is running in debug mode. // // If an error is returned the original message will be the result and is save to use. lines :: proc { lines_n, lines_const, } lines_n :: proc(bt: Trace, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (out: []Line, err: Lines_Error) { return _lines(bt, allocator, temp_allocator) } lines_const :: proc(bt: Trace_Const, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (out: []Line, err: Lines_Error) { bt := bt return _lines(bt.trace[:bt.len], allocator, temp_allocator) } lines_destroy :: proc(lines: []Line, allocator := context.allocator) { _lines_destroy(lines, allocator) } assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! { { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() lines, err := lines(trace(), context.temp_allocator, context.temp_allocator) if err != nil { fmt.eprintf("could not get backtrace for assertion failure: %v\n", err) } else { fmt.eprintln("[back trace]") print(lines, temp_allocator=context.temp_allocator) } } runtime.default_assertion_failure_proc(prefix, message, loc) } register_segfault_handler :: proc() { _register_segfault_handler() } print :: proc(lines: []Line, padding := " ", w: Maybe(io.Writer) = nil, temp_allocator := context.temp_allocator) { w := w.? or_else os.to_writer(os.stderr) tbl := table.init(&table.Table{}, temp_allocator, temp_allocator) for line in lines { table.row(tbl, padding, line.symbol, " - ", line.location) } table.build(tbl, table.unicode_width_proc) for row in 0..