1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

builtin: add libbacktrace as option for generating stack traces (#14277)

This commit is contained in:
Ned
2022-05-05 02:17:05 +08:00
committed by GitHub
parent 4242e7610f
commit 76a7354506
12 changed files with 10720 additions and 3 deletions

View File

@@ -54,7 +54,11 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
}
C.exit(1)
}
print_backtrace_skipping_top_frames(1)
$if use_libbacktrace ? {
print_libbacktrace(1)
} $else {
print_backtrace_skipping_top_frames(1)
}
$if panics_break_into_debugger ? {
break_if_debugger_attached()
}
@@ -101,7 +105,11 @@ pub fn panic(s string) {
}
C.exit(1)
}
print_backtrace_skipping_top_frames(1)
$if use_libbacktrace ? {
print_libbacktrace(1)
} $else {
print_backtrace_skipping_top_frames(1)
}
$if panics_break_into_debugger ? {
break_if_debugger_attached()
}
@@ -557,8 +565,23 @@ pub fn print_backtrace() {
$if tinyc {
C.tcc_backtrace(c'Backtrace')
} $else {
print_backtrace_skipping_top_frames(2)
// NOTE: TCC doesn't have the unwind library
$if use_libbacktrace ? {
print_libbacktrace(1)
} $else {
print_backtrace_skipping_top_frames(2)
}
}
}
}
}
// NOTE: g_main_argc and g_main_argv are filled in right after C's main start.
// They are used internally by V's builtin; for user code, it is much
// more convenient to just use `os.args` instead.
[markused]
__global g_main_argc = int(0)
[markused]
__global g_main_argv = voidptr(0)

View File

@@ -0,0 +1,86 @@
[has_globals]
module builtin
#flag -I@VEXEROOT/thirdparty/libbacktrace
#flag @VEXEROOT/thirdparty/libbacktrace/backtrace.o
#include <backtrace.h>
// NOTE: Don't mark this as a [typedef] or it may cause compiler errors!
struct C.backtrace_state {
// filename &char
}
type BacktraceErrorCallback = fn (data voidptr, msg &char, errnum int) voidptr
type BacktraceFullCallback = fn (data voidptr, pc voidptr, filename &char, lineno int, func &char) &int
fn C.backtrace_create_state(filename &char, threaded int, error_callback BacktraceErrorCallback, data voidptr) &C.backtrace_state
fn C.backtrace_full(state &C.backtrace_state, skip int, cb BacktraceFullCallback, err_cb BacktraceErrorCallback, data voidptr) int
__global bt_state = init_bt_state()
fn init_bt_state() &C.backtrace_state {
$if !tinyc {
mut filename := &char(0)
$if windows {
filename = unsafe { string_from_wide(&&u16(g_main_argv)[0]).str }
} $else {
filename = unsafe { &&char(g_main_argv)[0] }
}
return C.backtrace_create_state(filename, 1, bt_error_handler, 0)
}
return &C.backtrace_state(0)
}
// for bt_error_callback
// struct BacktraceData {
// state &C.backtrace_state
// }
fn bt_print_callback(data voidptr, pc voidptr, filename_ptr &char, line int, fn_name_ptr &char) int {
filename := if isnil(filename_ptr) { '???' } else { unsafe { filename_ptr.vstring() } }
fn_name := if isnil(fn_name_ptr) {
'???'
} else {
(unsafe { fn_name_ptr.vstring() }).replace('__', '.')
}
// keep it for later
// pc_64 := u64(pc)
println('$filename:$line: by $fn_name')
return 0
}
fn bt_error_callback(data voidptr, msg_ptr &char, errnum int) {
// if !isnil(data) && !isnil(data.state) && !isnil(data.state.filename) {
// filename := unsafe{ data.state.filename.vstring() }
// eprint('$filename: ')
// }
msg := unsafe { msg_ptr.vstring() }
eprint('libbacktrace: $msg')
if errnum > 0 {
eprint(': ${C.strerror(errnum)}')
}
eprintln('')
}
// for backtrace_create_state only
fn bt_error_handler(data voidptr, msg &char, errnum int) {
eprint('libbacktrace: ')
eprint(unsafe { msg.vstring() })
if errnum > 0 {
eprint(': ${C.strerror(errnum)}')
}
eprintln('')
exit(1)
}
[noinline]
fn print_libbacktrace(frames_to_skip int) {
$if no_backtrace ? {
return
}
// data := &BacktraceData{bt_state}
C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, 0)
}

View File

@@ -0,0 +1,4 @@
module builtin
fn print_libbacktrace(frames_to_skip int) {
}

View File

@@ -0,0 +1,14 @@
fn test_g_main_argc() {
assert g_main_argc > 0
}
fn test_g_main_argv() {
assert g_main_argv != 0
mut first_arg := ''
$if windows {
first_arg = unsafe { string_from_wide(&&u16(g_main_argv)[0]) }
} $else {
first_arg = unsafe { cstring_to_vstring(&&char(g_main_argv)[0]) }
}
assert first_arg.contains('builtin_test')
}

View File

@@ -72,6 +72,8 @@ fn (mut g Gen) gen_c_main_function_header() {
} else {
g.writeln('int main(int ___argc, char** ___argv){')
}
g.writeln('\tg_main_argc = ___argc;')
g.writeln('\tg_main_argv = ___argv;')
}
fn (mut g Gen) gen_c_main_header() {