diff options
Diffstat (limited to 'v_windows/v/vlib/builtin/builtin_windows.c.v')
-rw-r--r-- | v_windows/v/vlib/builtin/builtin_windows.c.v | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/v_windows/v/vlib/builtin/builtin_windows.c.v b/v_windows/v/vlib/builtin/builtin_windows.c.v new file mode 100644 index 0000000..97592cf --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_windows.c.v @@ -0,0 +1,304 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +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 byte // 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 &byte + 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 &byte, 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 = 0x80000000 +) + +// g_original_codepage - used to restore the original windows console code page when exiting +__global ( + g_original_codepage = u32(0) +) + +// utf8 to stdout needs C.SetConsoleOutputCP(C.CP_UTF8) +fn C.GetConsoleOutputCP() u32 + +fn C.SetConsoleOutputCP(wCodePageID u32) bool + +fn restore_codepage() { + C.SetConsoleOutputCP(g_original_codepage) +} + +fn is_terminal(fd int) int { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) +} + +fn builtin_init() { + g_original_codepage = C.GetConsoleOutputCP() + C.SetConsoleOutputCP(C.CP_UTF8) + C.atexit(restore_codepage) + if is_terminal(1) > 0 { + C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + C.SetConsoleMode(C.GetStdHandle(C.STD_ERROR_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + unsafe { + C.setbuf(C.stdout, 0) + C.setbuf(C.stderr, 0) + } + } + $if !no_backtrace ? { + add_unhandled_exception_handler() + } +} + +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: &byte(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 +} + +// TODO copypaste from os +// we want to be able to use this here without having to `import os` +struct ExceptionRecord { +pub: + // status_ constants + code u32 + flags u32 + record &ExceptionRecord + address voidptr + param_count u32 + // params []voidptr +} + +struct ContextRecord { + // TODO +} + +struct ExceptionPointers { +pub: + exception_record &ExceptionRecord + context_record &ContextRecord +} + +type VectoredExceptionHandler = fn (&ExceptionPointers) int + +fn C.AddVectoredExceptionHandler(int, C.PVECTORED_EXCEPTION_HANDLER) + +fn add_vectored_exception_handler(handler VectoredExceptionHandler) { + C.AddVectoredExceptionHandler(1, C.PVECTORED_EXCEPTION_HANDLER(handler)) +} + +[windows_stdcall] +fn unhandled_exception_handler(e &ExceptionPointers) int { + match e.exception_record.code { + // These are 'used' by the backtrace printer + // so we dont want to catch them... + 0x4001000A, 0x40010006 { + return 0 + } + else { + println('Unhandled Exception 0x${e.exception_record.code:X}') + print_backtrace_skipping_top_frames(5) + } + } + + return 0 +} + +fn add_unhandled_exception_handler() { + add_vectored_exception_handler(VectoredExceptionHandler(voidptr(unhandled_exception_handler))) +} + +fn C.IsDebuggerPresent() bool + +fn C.__debugbreak() + +fn break_if_debugger_attached() { + $if tinyc { + unsafe { + mut ptr := &voidptr(0) + *ptr = voidptr(0) + _ = ptr + } + } $else { + if C.IsDebuggerPresent() { + C.__debugbreak() + } + } +} + +// return an error message generated from WinAPI's `LastError` +pub fn winapi_lasterr_str() string { + err_msg_id := C.GetLastError() + if err_msg_id == 8 { + // handle this case special since `FormatMessage()` might not work anymore + return 'insufficient memory' + } + mut msgbuf := &u16(0) + res := C.FormatMessage(C.FORMAT_MESSAGE_ALLOCATE_BUFFER | C.FORMAT_MESSAGE_FROM_SYSTEM | C.FORMAT_MESSAGE_IGNORE_INSERTS, + C.NULL, err_msg_id, C.MAKELANGID(C.LANG_NEUTRAL, C.SUBLANG_DEFAULT), &msgbuf, + 0, C.NULL) + err_msg := if res == 0 { + 'Win-API error $err_msg_id' + } else { + unsafe { string_from_wide(msgbuf) } + } + return err_msg +} + +// panic with an error message generated from WinAPI's `LastError` +[noreturn] +pub fn panic_lasterr(base string) { + panic(base + winapi_lasterr_str()) +} |