| 1 | @[has_globals] |
| 2 | module builtin |
| 3 | |
| 4 | $if openbsd { |
| 5 | // OpenBSD lacks the sysctl path lookup that bundled libbacktrace needs, |
| 6 | // so `-d use_libbacktrace` falls back to the native BSD backtrace path. |
| 7 | fn print_libbacktrace(frames_to_skip int) { |
| 8 | } |
| 9 | |
| 10 | @[noinline] |
| 11 | fn eprint_libbacktrace(frames_to_skip int) { |
| 12 | } |
| 13 | } $else { |
| 14 | #flag -I@VEXEROOT/thirdparty/libbacktrace |
| 15 | #flag @VEXEROOT/thirdparty/libbacktrace/backtrace.o |
| 16 | #include <backtrace.h> |
| 17 | |
| 18 | // NOTE: Don't mark this as a @[typedef] or it may cause compiler errors! |
| 19 | pub struct C.backtrace_state { |
| 20 | // filename &char |
| 21 | } |
| 22 | |
| 23 | type BacktraceErrorCallback = fn (data voidptr, msg &char, errnum int) |
| 24 | |
| 25 | type BacktraceFullCallback = fn (data voidptr, pc voidptr, filename &char, lineno int, func &char) int |
| 26 | |
| 27 | fn C.backtrace_create_state(filename &char, threaded i32, error_callback BacktraceErrorCallback, data voidptr) &C.backtrace_state |
| 28 | fn C.backtrace_full(state &C.backtrace_state, skip i32, cb BacktraceFullCallback, err_cb BacktraceErrorCallback, |
| 29 | data voidptr) i32 |
| 30 | |
| 31 | __global bt_state = init_bt_state() |
| 32 | |
| 33 | fn init_bt_state() &C.backtrace_state { |
| 34 | $if !tinyc { |
| 35 | mut filename := &char(unsafe { nil }) |
| 36 | $if windows { |
| 37 | filename = unsafe { string_from_wide(&&u16(g_main_argv)[0]).str } |
| 38 | } $else { |
| 39 | filename = unsafe { &&char(g_main_argv)[0] } |
| 40 | } |
| 41 | return C.backtrace_create_state(filename, 1, bt_error_handler, 0) |
| 42 | } |
| 43 | return &C.backtrace_state(unsafe { nil }) |
| 44 | } |
| 45 | |
| 46 | // for bt_error_callback |
| 47 | struct BacktraceOptions { |
| 48 | stdin bool = true |
| 49 | } |
| 50 | |
| 51 | fn bt_print_callback(data_ptr voidptr, pc voidptr, filename_ptr &char, line int, fn_name_ptr &char) int { |
| 52 | data := unsafe { &BacktraceOptions(data_ptr) } |
| 53 | filename := if filename_ptr == unsafe { nil } { |
| 54 | '???' |
| 55 | } else { |
| 56 | unsafe { filename_ptr.vstring() } |
| 57 | } |
| 58 | fn_name := if fn_name_ptr == unsafe { nil } { |
| 59 | '???' |
| 60 | } else { |
| 61 | demangle_v_symbol(unsafe { fn_name_ptr.vstring() }) |
| 62 | } |
| 63 | // keep it for later |
| 64 | // pc_64 := u64(pc) |
| 65 | bt_str := '${filename}:${line}: by ${fn_name}' |
| 66 | if data.stdin { |
| 67 | println(bt_str) |
| 68 | } else { |
| 69 | eprintln(bt_str) |
| 70 | } |
| 71 | return 0 |
| 72 | } |
| 73 | |
| 74 | fn bt_error_callback(data voidptr, msg_ptr &char, errnum int) { |
| 75 | // if data != unsafe { nil } && data.state != unsafe { nil } && data.state.filename != unsafe { nil } { |
| 76 | // filename := unsafe{ data.state.filename.vstring() } |
| 77 | // eprint('${filename}: ') |
| 78 | // } |
| 79 | |
| 80 | msg := unsafe { msg_ptr.vstring() } |
| 81 | eprint('libbacktrace: ${msg}') |
| 82 | if errnum > 0 { |
| 83 | eprint(': ${C.strerror(errnum)}') |
| 84 | } |
| 85 | |
| 86 | eprintln('') |
| 87 | } |
| 88 | |
| 89 | // for backtrace_create_state only |
| 90 | fn bt_error_handler(data voidptr, msg &char, errnum int) { |
| 91 | eprint('libbacktrace: ') |
| 92 | eprint(unsafe { msg.vstring() }) |
| 93 | if errnum > 0 { |
| 94 | eprint(': ${C.strerror(errnum)}') |
| 95 | } |
| 96 | eprintln('') |
| 97 | exit(1) |
| 98 | } |
| 99 | |
| 100 | @[noinline] |
| 101 | fn print_libbacktrace(frames_to_skip int) { |
| 102 | $if no_backtrace ? { |
| 103 | return |
| 104 | } |
| 105 | data := &BacktraceOptions{} |
| 106 | C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, data) |
| 107 | } |
| 108 | |
| 109 | @[noinline] |
| 110 | fn eprint_libbacktrace(frames_to_skip int) { |
| 111 | $if no_backtrace ? { |
| 112 | return |
| 113 | } |
| 114 | data := &BacktraceOptions{ |
| 115 | stdin: false |
| 116 | } |
| 117 | C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, data) |
| 118 | } |
| 119 | } |
| 120 | |