#+vet explicit-allocators #+private file package back @require import "base:runtime" @require import "core:strings" @require import "core:sys/posix" when !USE_FALLBACK { foreign import system "system:System.framework" // NOTE: CoreSymbolication is a private framework, Apple is allowed to break it and doesn't provide // headers, although the API has as of my knowledge been the same in the past 10 years at least. @(extra_linker_flags="-iframework /System/Library/PrivateFrameworks") foreign import symbolication "system:CoreSymbolication.framework" @(private="package") _Trace_Entry :: rawptr @(private="package") _trace :: #force_no_inline proc(buf: Trace) -> (n: int) { ctx: unw_context_t cursor: unw_cursor_t ret: i32 ret = unw_getcontext(&ctx) assert(ret == 0) ret = unw_init_local(&cursor, &ctx) assert(ret == 0) // Skip this function's frame and the caller. if unw_step(&cursor) <= 0 { return } pc: uintptr for ; unw_step(&cursor) > 0 && n < len(buf); n += 1 { ret = unw_get_reg(&cursor, .IP, &pc) assert(ret == 0) buf[n] = rawptr(pc) } return } @(private="package") _lines_destroy :: proc(lines: []Line, allocator: runtime.Allocator) { for line in lines { delete(line.location, allocator) delete(line.symbol, allocator) } delete(lines, allocator) } @(private="package") _lines :: proc(bt: Trace, allocator, _: runtime.Allocator) -> (out: []Line, err: Lines_Error) { out = make([]Line, len(bt), allocator) defer if err != nil { _lines_destroy(out, allocator) } symbolicator := CSSymbolicatorCreateWithPid(posix.getpid()) defer CSRelease(symbolicator) for &msg, i in out { symbol := CSSymbolicatorGetSymbolWithAddressAtTime(symbolicator, uintptr(bt[i]), CSNow) info := CSSymbolicatorGetSourceInfoWithAddressAtTime(symbolicator, uintptr(bt[i]), CSNow) msg.symbol = strings.clone_from(CSSymbolGetName(symbol), allocator) // No debug info. if CSIsNull(info) { owner := CSSymbolGetSymbolOwner(symbol) msg.location = strings.clone_from(CSSymbolOwnerGetPath(owner), allocator) } else { path := string(CSSourceInfoGetPath(info)) location := strings.builder_make(allocator) strings.write_string(&location, path) when ODIN_ERROR_POS_STYLE == .Default { strings.write_byte(&location, '(') strings.write_int (&location, int(CSSourceInfoGetLineNumber(info))) strings.write_byte(&location, ')') } else when ODIN_ERROR_POS_STYLE == .Unix { strings.write_byte(&location, ':') strings.write_int (&location, int(CSSourceInfoGetLineNumber(info))) } else { #panic("unhandled ODIN_ERROR_POS_STYLE") } msg.location = strings.to_string(location) } } return } CSTypeRef :: struct { csCppData: rawptr, csCppObj: rawptr, } CSSymbolicatorRef :: distinct CSTypeRef CSSymbolRef :: distinct CSTypeRef CSSourceInfoRef :: distinct CSTypeRef CSSymbolOwnerRef :: distinct CSTypeRef CSNow :: 0x80000000 foreign symbolication { @(link_name="CSIsNull") _CSIsNull :: proc(ref: CSTypeRef) -> bool --- @(link_name="CSRelease") _CSRelease :: proc(ref: CSTypeRef) --- CSSymbolicatorCreateWithPid :: proc(pid: posix.pid_t) -> CSSymbolicatorRef --- CSSymbolicatorGetSymbolWithAddressAtTime :: proc(symbolicator: CSSymbolicatorRef, addr: uintptr, time: u64) -> CSSymbolRef --- CSSymbolicatorGetSourceInfoWithAddressAtTime :: proc(symbolicator: CSSymbolicatorRef, adrr: uintptr, time: u64) -> CSSourceInfoRef --- CSSymbolGetName :: proc(symbol: CSSymbolRef) -> cstring --- CSSymbolGetSymbolOwner :: proc(symbol: CSSymbolRef) -> CSSymbolOwnerRef --- CSSourceInfoGetPath :: proc(info: CSSourceInfoRef) -> cstring --- CSSourceInfoGetLineNumber :: proc(info: CSSourceInfoRef) -> i32 --- CSSourceInfoGetSymbol :: proc(info: CSSourceInfoRef) -> CSSymbolRef --- CSSymbolOwnerGetPath :: proc(owner: CSSymbolOwnerRef) -> cstring --- } CSRelease :: #force_inline proc(ref: $T) { _CSRelease(CSTypeRef(ref)) } CSIsNull :: #force_inline proc(ref: $T) -> bool { return _CSIsNull(CSTypeRef(ref)) } // These could actually be smaller, but then we would have to define and check the size on each // architecture, the sizes here are the largest they can be. _LIBUNWIND_CONTEXT_SIZE :: 167 _LIBUNWIND_CURSOR_SIZE :: 204 unw_context_t :: struct { data: [_LIBUNWIND_CONTEXT_SIZE]u64, } unw_cursor_t :: struct { data: [_LIBUNWIND_CURSOR_SIZE]u64, } // Cross-platform registers, each architecture has additional registers but these are enough for us. Register :: enum i32 { SP = -2, IP = -1, } foreign system { unw_getcontext :: proc(ctx: ^unw_context_t) -> i32 --- unw_init_local :: proc(cursor: ^unw_cursor_t, ctx: ^unw_context_t) -> i32 --- unw_get_reg :: proc(cursor: ^unw_cursor_t, name: Register, reg: ^uintptr) -> i32 --- unw_step :: proc(cursor: ^unw_cursor_t) -> i32 --- } }