| 1 | module builtin |
| 2 | |
| 3 | // dbghelp.h is already included in cheaders.v |
| 4 | #flag windows -l dbghelp |
| 5 | |
| 6 | // SymbolInfo is used by print_backtrace_skipping_top_frames_msvc. |
| 7 | pub struct SymbolInfo { |
| 8 | pub mut: |
| 9 | f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr |
| 10 | f_type_index u32 // Type Index of symbol |
| 11 | f_reserved [2]u64 |
| 12 | f_index u32 |
| 13 | f_size u32 |
| 14 | f_mod_base u64 // Base Address of module containing this symbol |
| 15 | f_flags u32 |
| 16 | f_value u64 // Value of symbol, ValuePresent should be 1 |
| 17 | f_address u64 // Address of symbol including base address of module |
| 18 | f_register u32 // register holding value or pointer to value |
| 19 | f_scope u32 // scope of the symbol |
| 20 | f_tag u32 // pdb classification |
| 21 | f_name_len u32 // Actual length of name |
| 22 | f_max_name_len u32 // must be manually set |
| 23 | f_name u8 // must be calloc(f_max_name_len) |
| 24 | } |
| 25 | |
| 26 | pub struct SymbolInfoContainer { |
| 27 | pub mut: |
| 28 | syminfo SymbolInfo |
| 29 | f_name_rest [254]char |
| 30 | } |
| 31 | |
| 32 | pub struct Line64 { |
| 33 | pub mut: |
| 34 | f_size_of_struct u32 |
| 35 | f_key voidptr |
| 36 | f_line_number u32 |
| 37 | f_file_name &u8 = unsafe { nil } |
| 38 | f_address u64 |
| 39 | } |
| 40 | |
| 41 | // returns the current options mask |
| 42 | fn C.SymSetOptions(symoptions u32) u32 |
| 43 | |
| 44 | // returns handle |
| 45 | fn C.GetCurrentProcess() voidptr |
| 46 | |
| 47 | fn C.SymInitialize(h_process voidptr, p_user_search_path &u8, b_invade_process i32) i32 |
| 48 | |
| 49 | fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16 |
| 50 | |
| 51 | fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) i32 |
| 52 | |
| 53 | fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) i32 |
| 54 | |
| 55 | // Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions |
| 56 | const symopt_undname = 0x00000002 |
| 57 | const symopt_deferred_loads = 0x00000004 |
| 58 | const symopt_no_cpp = 0x00000008 |
| 59 | const symopt_load_lines = 0x00000010 |
| 60 | const symopt_include_32bit_modules = 0x00002000 |
| 61 | const symopt_allow_zero_address = 0x01000000 |
| 62 | const symopt_debug = u32(0x80000000) |
| 63 | |
| 64 | // print_backtrace_skipping_top_frames prints the backtrace skipping N top frames. |
| 65 | pub fn print_backtrace_skipping_top_frames(skipframes int) bool { |
| 66 | $if msvc { |
| 67 | return print_backtrace_skipping_top_frames_msvc(skipframes) |
| 68 | } |
| 69 | $if tinyc { |
| 70 | return print_backtrace_skipping_top_frames_tcc(skipframes) |
| 71 | } |
| 72 | $if mingw { |
| 73 | return print_backtrace_skipping_top_frames_mingw(skipframes) |
| 74 | } |
| 75 | eprintln('print_backtrace_skipping_top_frames is not implemented') |
| 76 | return false |
| 77 | } |
| 78 | |
| 79 | @[direct_array_access] |
| 80 | fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { |
| 81 | $if msvc { |
| 82 | mut offset := u64(0) |
| 83 | backtraces := [100]voidptr{} |
| 84 | sic := SymbolInfoContainer{} |
| 85 | mut si := &sic.syminfo |
| 86 | si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88 |
| 87 | si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1 |
| 88 | fname := &char(&si.f_name) |
| 89 | mut sline64 := Line64{ |
| 90 | f_file_name: &u8(unsafe { nil }) |
| 91 | } |
| 92 | sline64.f_size_of_struct = sizeof(Line64) |
| 93 | |
| 94 | handle := C.GetCurrentProcess() |
| 95 | defer { |
| 96 | C.SymCleanup(handle) |
| 97 | } |
| 98 | |
| 99 | C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname) |
| 100 | |
| 101 | syminitok := C.SymInitialize(handle, 0, 1) |
| 102 | if syminitok != 1 { |
| 103 | eprintln('Failed getting process: Aborting backtrace.\n') |
| 104 | return false |
| 105 | } |
| 106 | |
| 107 | frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, &backtraces[0], 0)) |
| 108 | if frames < 2 { |
| 109 | eprintln('C.CaptureStackBackTrace returned less than 2 frames') |
| 110 | return false |
| 111 | } |
| 112 | for i in 0 .. frames { |
| 113 | frame_addr := backtraces[i] |
| 114 | if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 { |
| 115 | nframe := frames - i - 1 |
| 116 | mut lineinfo := '' |
| 117 | if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { |
| 118 | file_name := unsafe { tos3(sline64.f_file_name) } |
| 119 | lnumber := sline64.f_line_number |
| 120 | lineinfo = file_name + ':' + i64(lnumber).str() |
| 121 | } else { |
| 122 | // addr: |
| 123 | lineinfo = '?? : address = 0x' + ptr_str(frame_addr) |
| 124 | } |
| 125 | sfunc := demangle_v_symbol(unsafe { tos3(fname) }) |
| 126 | snframe := i64(nframe).str() |
| 127 | eprint_space_padding(snframe, 2) |
| 128 | eprint(': ') |
| 129 | eprint(sfunc) |
| 130 | eprint_space_padding(sfunc, 25) |
| 131 | eprint(' ') |
| 132 | eprintln(lineinfo) |
| 133 | } else { |
| 134 | // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes |
| 135 | cerr := int(C.GetLastError()) |
| 136 | eprint('SymFromAddr failure: ') |
| 137 | eprint(i64(cerr).str()) |
| 138 | if cerr == 87 { |
| 139 | eprintln(' = The parameter is incorrect)') |
| 140 | } else if cerr == 487 { |
| 141 | // probably caused because the .pdb isn't in the executable folder |
| 142 | eprintln(' = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') |
| 143 | } else { |
| 144 | eprintln(' (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | return true |
| 149 | } $else { |
| 150 | eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc') |
| 151 | return false |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool { |
| 156 | eprintln('print_backtrace_skipping_top_frames_mingw is not implemented') |
| 157 | return false |
| 158 | } |
| 159 | |
| 160 | fn C.tcc_backtrace(fmt &char) i32 |
| 161 | |
| 162 | fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool { |
| 163 | $if tinyc { |
| 164 | $if no_backtrace ? { |
| 165 | eprintln('backtraces are disabled') |
| 166 | return false |
| 167 | } $else { |
| 168 | C.tcc_backtrace(c'Backtrace') |
| 169 | return true |
| 170 | } |
| 171 | } $else { |
| 172 | eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc') |
| 173 | return false |
| 174 | } |
| 175 | // Not reachable, but it looks like it's not detectable by V |
| 176 | return false |
| 177 | } |
| 178 | |