-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
builtin: extract backtraces.c.v, backtraces_nix.c.v, backtraces_windo…
…ws.c.v (#19480)
- Loading branch information
Showing
6 changed files
with
306 additions
and
320 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module builtin | ||
|
||
// print_backtrace shows a backtrace of the current call stack on stdout | ||
pub fn print_backtrace() { | ||
// At the time of backtrace_symbols_fd call, the C stack would look something like this: | ||
// * print_backtrace_skipping_top_frames | ||
// * print_backtrace itself | ||
// * the rest of the backtrace frames | ||
// => top 2 frames should be skipped, since they will not be informative to the developer | ||
$if !no_backtrace ? { | ||
$if freestanding { | ||
println(bare_backtrace()) | ||
} $else { | ||
$if tinyc { | ||
C.tcc_backtrace(c'Backtrace') | ||
} $else { | ||
// NOTE: TCC doesn't have the unwind library | ||
$if use_libbacktrace ? { | ||
print_libbacktrace(1) | ||
} $else { | ||
print_backtrace_skipping_top_frames(2) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
module builtin | ||
|
||
fn print_backtrace_skipping_top_frames(xskipframes int) bool { | ||
$if no_backtrace ? { | ||
return false | ||
} $else { | ||
skipframes := xskipframes + 2 | ||
$if macos || freebsd || openbsd || netbsd { | ||
return print_backtrace_skipping_top_frames_bsd(skipframes) | ||
} $else $if linux { | ||
return print_backtrace_skipping_top_frames_linux(skipframes) | ||
} $else { | ||
println('print_backtrace_skipping_top_frames is not implemented. skipframes: ${skipframes}') | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// the functions below are not called outside this file, | ||
// so there is no need to have their twins in builtin_windows.v | ||
fn print_backtrace_skipping_top_frames_bsd(skipframes int) bool { | ||
$if no_backtrace ? { | ||
return false | ||
} $else { | ||
$if macos || freebsd || netbsd { | ||
buffer := [100]voidptr{} | ||
nr_ptrs := C.backtrace(&buffer[0], 100) | ||
if nr_ptrs < 2 { | ||
eprintln('C.backtrace returned less than 2 frames') | ||
return false | ||
} | ||
C.backtrace_symbols_fd(&buffer[skipframes], nr_ptrs - skipframes, 2) | ||
} | ||
return true | ||
} | ||
} | ||
|
||
fn C.tcc_backtrace(fmt &char) int | ||
fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { | ||
$if android { | ||
eprintln('On Android no backtrace is available.') | ||
return false | ||
} | ||
$if !glibc { | ||
eprintln('backtrace_symbols is missing => printing backtraces is not available.') | ||
eprintln('Some libc implementations like musl simply do not provide it.') | ||
return false | ||
} | ||
$if no_backtrace ? { | ||
return false | ||
} $else { | ||
$if linux && !freestanding { | ||
$if tinyc { | ||
C.tcc_backtrace(c'Backtrace') | ||
return false | ||
} $else { | ||
buffer := [100]voidptr{} | ||
nr_ptrs := C.backtrace(&buffer[0], 100) | ||
if nr_ptrs < 2 { | ||
eprintln('C.backtrace returned less than 2 frames') | ||
return false | ||
} | ||
nr_actual_frames := nr_ptrs - skipframes | ||
mut sframes := []string{} | ||
//////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) | ||
csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) | ||
for i in 0 .. nr_actual_frames { | ||
sframes << unsafe { tos2(&u8(csymbols[i])) } | ||
} | ||
for sframe in sframes { | ||
executable := sframe.all_before('(') | ||
addr := sframe.all_after('[').all_before(']') | ||
beforeaddr := sframe.all_before('[') | ||
cmd := 'addr2line -e ${executable} ${addr}' | ||
// taken from os, to avoid depending on the os module inside builtin.v | ||
f := C.popen(&char(cmd.str), c'r') | ||
if f == unsafe { nil } { | ||
eprintln(sframe) | ||
continue | ||
} | ||
buf := [1000]u8{} | ||
mut output := '' | ||
unsafe { | ||
bp := &buf[0] | ||
for C.fgets(&char(bp), 1000, f) != 0 { | ||
output += tos(bp, vstrlen(bp)) | ||
} | ||
} | ||
output = output.trim_space() + ':' | ||
if C.pclose(f) != 0 { | ||
eprintln(sframe) | ||
continue | ||
} | ||
if output in ['??:0:', '??:?:'] { | ||
output = '' | ||
} | ||
// See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators | ||
// Note: it is shortened here to just d. , just so that it fits, and so | ||
// that the common error file:lineno: line format is enforced. | ||
output = output.replace(' (discriminator', ': (d.') | ||
eprintln('${output:-55s} | ${addr:14s} | ${beforeaddr}') | ||
} | ||
} | ||
} | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
module builtin | ||
|
||
// dbghelp.h is already included in cheaders.v | ||
#flag windows -l dbghelp | ||
|
||
// SymbolInfo is used by print_backtrace_skipping_top_frames_msvc | ||
pub struct SymbolInfo { | ||
pub mut: | ||
f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr | ||
f_type_index u32 // Type Index of symbol | ||
f_reserved [2]u64 | ||
f_index u32 | ||
f_size u32 | ||
f_mod_base u64 // Base Address of module comtaining this symbol | ||
f_flags u32 | ||
f_value u64 // Value of symbol, ValuePresent should be 1 | ||
f_address u64 // Address of symbol including base address of module | ||
f_register u32 // register holding value or pointer to value | ||
f_scope u32 // scope of the symbol | ||
f_tag u32 // pdb classification | ||
f_name_len u32 // Actual length of name | ||
f_max_name_len u32 // must be manually set | ||
f_name u8 // must be calloc(f_max_name_len) | ||
} | ||
|
||
pub struct SymbolInfoContainer { | ||
pub mut: | ||
syminfo SymbolInfo | ||
f_name_rest [254]char | ||
} | ||
|
||
pub struct Line64 { | ||
pub mut: | ||
f_size_of_struct u32 | ||
f_key voidptr | ||
f_line_number u32 | ||
f_file_name &u8 = unsafe { nil } | ||
f_address u64 | ||
} | ||
|
||
// returns the current options mask | ||
fn C.SymSetOptions(symoptions u32) u32 | ||
|
||
// returns handle | ||
fn C.GetCurrentProcess() voidptr | ||
|
||
fn C.SymInitialize(h_process voidptr, p_user_search_path &u8, b_invade_process int) int | ||
|
||
fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16 | ||
|
||
fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) int | ||
|
||
fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) int | ||
|
||
// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions | ||
const ( | ||
symopt_undname = 0x00000002 | ||
symopt_deferred_loads = 0x00000004 | ||
symopt_no_cpp = 0x00000008 | ||
symopt_load_lines = 0x00000010 | ||
symopt_include_32bit_modules = 0x00002000 | ||
symopt_allow_zero_address = 0x01000000 | ||
symopt_debug = u32(0x80000000) | ||
) | ||
|
||
fn print_backtrace_skipping_top_frames(skipframes int) bool { | ||
$if msvc { | ||
return print_backtrace_skipping_top_frames_msvc(skipframes) | ||
} | ||
$if tinyc { | ||
return print_backtrace_skipping_top_frames_tcc(skipframes) | ||
} | ||
$if mingw { | ||
return print_backtrace_skipping_top_frames_mingw(skipframes) | ||
} | ||
eprintln('print_backtrace_skipping_top_frames is not implemented') | ||
return false | ||
} | ||
|
||
fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { | ||
$if msvc { | ||
mut offset := u64(0) | ||
backtraces := [100]voidptr{} | ||
sic := SymbolInfoContainer{} | ||
mut si := &sic.syminfo | ||
si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88 | ||
si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1 | ||
fname := &char(&si.f_name) | ||
mut sline64 := Line64{ | ||
f_file_name: &u8(0) | ||
} | ||
sline64.f_size_of_struct = sizeof(Line64) | ||
|
||
handle := C.GetCurrentProcess() | ||
defer { | ||
C.SymCleanup(handle) | ||
} | ||
|
||
C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname) | ||
|
||
syminitok := C.SymInitialize(handle, 0, 1) | ||
if syminitok != 1 { | ||
eprintln('Failed getting process: Aborting backtrace.\n') | ||
return false | ||
} | ||
|
||
frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, &backtraces[0], 0)) | ||
if frames < 2 { | ||
eprintln('C.CaptureStackBackTrace returned less than 2 frames') | ||
return false | ||
} | ||
for i in 0 .. frames { | ||
frame_addr := backtraces[i] | ||
if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 { | ||
nframe := frames - i - 1 | ||
mut lineinfo := '' | ||
if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { | ||
file_name := unsafe { tos3(sline64.f_file_name) } | ||
lnumber := sline64.f_line_number | ||
lineinfo = '${file_name}:${lnumber}' | ||
} else { | ||
// addr: | ||
lineinfo = '?? : address = 0x${(&frame_addr):x}' | ||
} | ||
sfunc := unsafe { tos3(fname) } | ||
eprintln('${nframe:-2d}: ${sfunc:-25s} ${lineinfo}') | ||
} else { | ||
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes | ||
cerr := int(C.GetLastError()) | ||
if cerr == 87 { | ||
eprintln('SymFromAddr failure: ${cerr} = The parameter is incorrect)') | ||
} else if cerr == 487 { | ||
// probably caused because the .pdb isn't in the executable folder | ||
eprintln('SymFromAddr failure: ${cerr} = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') | ||
} else { | ||
eprintln('SymFromAddr failure: ${cerr} (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') | ||
} | ||
} | ||
} | ||
return true | ||
} $else { | ||
eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc') | ||
return false | ||
} | ||
} | ||
|
||
fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool { | ||
eprintln('print_backtrace_skipping_top_frames_mingw is not implemented') | ||
return false | ||
} | ||
|
||
fn C.tcc_backtrace(fmt &char) int | ||
|
||
fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool { | ||
$if tinyc { | ||
$if no_backtrace ? { | ||
eprintln('backtraces are disabled') | ||
return false | ||
} $else { | ||
C.tcc_backtrace(c'Backtrace') | ||
return true | ||
} | ||
} $else { | ||
eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc') | ||
return false | ||
} | ||
// Not reachable, but it looks like it's not detectable by V | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.