1// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. 2// Use of this source code is governed by an MIT license 3// that can be found in the LICENSE file. 4 5module builtin 6 7// dbghelp.h is already included in cheaders.v 8#flag windows -l dbghelp 9 10pub struct SymbolInfo { 11pub mut: 12 f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr 13 f_type_index u32 // Type Index of symbol 14 f_reserved [2]u64 15 f_index u32 16 f_size u32 17 f_mod_base u64 // Base Address of module comtaining this symbol 18 f_flags u32 19 f_value u64 // Value of symbol, ValuePresent should be 1 20 f_address u64 // Address of symbol including base address of module 21 f_register u32 // register holding value or pointer to value 22 f_scope u32 // scope of the symbol 23 f_tag u32 // pdb classification 24 f_name_len u32 // Actual length of name 25 f_max_name_len u32 // must be manually set 26 f_name byte // must be calloc(f_max_name_len) 27} 28 29pub struct SymbolInfoContainer { 30pub mut: 31 syminfo SymbolInfo 32 f_name_rest [254]char 33} 34 35pub struct Line64 { 36pub mut: 37 f_size_of_struct u32 38 f_key voidptr 39 f_line_number u32 40 f_file_name byteptr 41 f_address u64 42} 43 44fn C.SymSetOptions(symoptions u32) u32 // returns the current options mask 45fn C.GetCurrentProcess() voidptr // returns handle 46fn C.SymInitialize(h_process voidptr, p_user_search_path byteptr, b_invade_process int) int 47fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16 48fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) int 49fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) int 50 51// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions 52const ( 53 symopt_undname = 0x00000002 54 symopt_deferred_loads = 0x00000004 55 symopt_no_cpp = 0x00000008 56 symopt_load_lines = 0x00000010 57 symopt_include_32bit_modules = 0x00002000 58 symopt_allow_zero_address = 0x01000000 59 symopt_debug = 0x80000000 60) 61 62fn builtin_init() { 63 if is_atty(1) > 0 { 64 C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | 0x0004) // enable_virtual_terminal_processing 65 unsafe { 66 C.setbuf(C.stdout, 0) 67 } 68 } 69 add_unhandled_exception_handler() 70} 71 72fn print_backtrace_skipping_top_frames(skipframes int) bool { 73 $if msvc { 74 return print_backtrace_skipping_top_frames_msvc(skipframes) 75 } 76 $if tinyc { 77 return print_backtrace_skipping_top_frames_tcc(skipframes) 78 } 79 $if mingw { 80 return print_backtrace_skipping_top_frames_mingw(skipframes) 81 } 82 eprintln('print_backtrace_skipping_top_frames is not implemented') 83 return false 84} 85 86fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { 87$if msvc { 88 mut offset := u64(0) 89 backtraces := [100]voidptr 90 sic := SymbolInfoContainer{} 91 mut si := &sic.syminfo 92 si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88 93 si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1 94 fname := charptr( &si.f_name ) 95 mut sline64 := Line64{} 96 sline64.f_size_of_struct = sizeof(Line64) 97 98 handle := C.GetCurrentProcess() 99 defer { C.SymCleanup(handle) } 100 101 C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname) 102 103 syminitok := C.SymInitialize( handle, 0, 1) 104 if syminitok != 1 { 105 eprintln('Failed getting process: Aborting backtrace.\n') 106 return false 107 } 108 109 frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, backtraces, 0)) 110 if frames < 2 { 111 eprintln('C.CaptureStackBackTrace returned less than 2 frames') 112 return false 113 } 114 for i in 0..frames { 115 frame_addr := backtraces[i] 116 if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 { 117 nframe := frames - i - 1 118 mut lineinfo := '' 119 if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { 120 file_name := tos3(sline64.f_file_name) 121 lineinfo = '${file_name}:${sline64.f_line_number}' 122 } else { 123 addr : 124 lineinfo = '?? : address = 0x${(&frame_addr):x}' 125 } 126 sfunc := tos3(fname) 127 eprintln('${nframe:-2d}: ${sfunc:-25s} $lineinfo') 128 } else { 129 // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes 130 cerr := int(C.GetLastError()) 131 if cerr == 87 { 132 eprintln('SymFromAddr failure: $cerr = The parameter is incorrect)') 133 } else if cerr == 487 { 134 // probably caused because the .pdb isn't in the executable folder 135 eprintln('SymFromAddr failure: $cerr = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') 136 } else { 137 eprintln('SymFromAddr failure: $cerr (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') 138 } 139 } 140 } 141 return true 142} $else { 143 eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc') 144 return false 145} 146} 147 148fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool { 149 eprintln('print_backtrace_skipping_top_frames_mingw is not implemented') 150 return false 151} 152 153fn C.tcc_backtrace(fmt charptr, other ...charptr) int 154fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool { 155 $if tinyc { 156 C.tcc_backtrace("Backtrace") 157 return false 158 } $else { 159 eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc') 160 return false 161 } 162} 163 164//TODO copypaste from os 165// we want to be able to use this here without having to `import os` 166struct ExceptionRecord { 167pub: 168 // status_ constants 169 code u32 170 flags u32 171 172 record &ExceptionRecord 173 address voidptr 174 param_count u32 175 // params []voidptr 176} 177 178 struct ContextRecord { 179 // TODO 180} 181 182struct ExceptionPointers { 183pub: 184 exception_record &ExceptionRecord 185 context_record &ContextRecord 186} 187 188type VectoredExceptionHandler fn(&ExceptionPointers)u32 189 190fn C.AddVectoredExceptionHandler(u32, VectoredExceptionHandler) 191fn add_vectored_exception_handler(handler VectoredExceptionHandler) { 192 C.AddVectoredExceptionHandler(1, handler) 193} 194 195[windows_stdcall] 196fn unhandled_exception_handler(e &ExceptionPointers) u32 { 197 match e.exception_record.code { 198 // These are 'used' by the backtrace printer 199 // so we dont want to catch them... 200 0x4001000A, 0x40010006 { 201 return 0 202 } 203 else { 204 println('Unhandled Exception 0x${e.exception_record.code:X}') 205 print_backtrace_skipping_top_frames(5) 206 } 207 } 208 209 return 0 210} 211 212fn add_unhandled_exception_handler() { 213 add_vectored_exception_handler(unhandled_exception_handler) 214} 215 216fn C.IsDebuggerPresent() bool 217fn C.__debugbreak() 218 219fn break_if_debugger_attached() { 220 $if tinyc { 221 unsafe { 222 mut ptr := &voidptr(0) 223 *ptr = 0 224 } 225 } $else { 226 if C.IsDebuggerPresent() { 227 C.__debugbreak() 228 } 229 } 230} 231