From 69a142545f22c0c44b08790bec7ffbd5ebe686d8 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sat, 11 Dec 2021 19:50:51 +0800 Subject: [PATCH 1/5] Add crash handler on Windows --- spec/std/exception/call_stack_spec.cr | 2 +- spec/std/exception_spec.cr | 18 +- spec/std/kernel_spec.cr | 10 +- src/exception/call_stack/libunwind.cr | 4 + src/exception/call_stack/stackwalk.cr | 168 +++++++++++++++--- src/kernel.cr | 2 +- .../x86_64-windows-msvc/c/errhandlingapi.cr | 8 + .../c/processthreadsapi.cr | 5 + src/lib_c/x86_64-windows-msvc/c/winnt.cr | 15 ++ 9 files changed, 193 insertions(+), 39 deletions(-) diff --git a/spec/std/exception/call_stack_spec.cr b/spec/std/exception/call_stack_spec.cr index b0a33e5a467c..5181aa316a62 100644 --- a/spec/std/exception/call_stack_spec.cr +++ b/spec/std/exception/call_stack_spec.cr @@ -55,7 +55,7 @@ describe "Backtrace" do error.to_s.should contain("IndexError") end - pending_win32 "prints crash backtrace to stderr" do + it "prints crash backtrace to stderr" do sample = datapath("crash_backtrace_sample") _, output, error = compile_and_run_file(sample) diff --git a/spec/std/exception_spec.cr b/spec/std/exception_spec.cr index fc4aa1ae19eb..9d000d866306 100644 --- a/spec/std/exception_spec.cr +++ b/spec/std/exception_spec.cr @@ -35,16 +35,14 @@ describe "Exception" do ex.inspect_with_backtrace.should contain("inner") end - {% unless flag?(:win32) %} - it "collect memory within ensure block" do - sample = datapath("collect_within_ensure") + it "collect memory within ensure block" do + sample = datapath("collect_within_ensure") - _, output, error = compile_and_run_file(sample, ["--release"]) + _, output, error = compile_and_run_file(sample, ["--release"]) - output.to_s.empty?.should be_true - error.to_s.should contain("Unhandled exception: Oh no! (Exception)") - error.to_s.should_not contain("Invalid memory access") - error.to_s.should_not contain("Illegal instruction") - end - {% end %} + output.to_s.empty?.should be_true + error.to_s.should contain("Unhandled exception: Oh no! (Exception)") + error.to_s.should_not contain("Invalid memory access") + error.to_s.should_not contain("Illegal instruction") + end end diff --git a/spec/std/kernel_spec.cr b/spec/std/kernel_spec.cr index 0c0cc7ed8798..ff45dcd259b7 100644 --- a/spec/std/kernel_spec.cr +++ b/spec/std/kernel_spec.cr @@ -210,8 +210,8 @@ describe "at_exit" do end end -pending_win32 describe: "seg fault" do - it "reports SIGSEGV" do +describe "hardware exception" do + it "reports invalid memory access" do status, _, error = compile_and_run_source <<-'CODE' puts Pointer(Int64).null.value CODE @@ -232,7 +232,7 @@ pending_win32 describe: "seg fault" do # will address this. status, _, error = compile_and_run_source <<-'CODE' def foo - y = StaticArray(Int8,512).new(0) + y = StaticArray(Int8, 512).new(0) foo end foo @@ -243,10 +243,10 @@ pending_win32 describe: "seg fault" do end {% end %} - it "detects stack overflow on a fiber stack" do + pending_win32 "detects stack overflow on a fiber stack" do status, _, error = compile_and_run_source <<-'CODE' def foo - y = StaticArray(Int8,512).new(0) + y = StaticArray(Int8, 512).new(0) foo end diff --git a/src/exception/call_stack/libunwind.cr b/src/exception/call_stack/libunwind.cr index b05ddafbcf6b..186c6f9b9c65 100644 --- a/src/exception/call_stack/libunwind.cr +++ b/src/exception/call_stack/libunwind.cr @@ -32,6 +32,10 @@ struct Exception::CallStack end {% end %} + def self.setup_crash_handler + Signal.setup_segfault_handler + end + protected def self.unwind callstack = [] of Void* backtrace_fn = ->(context : LibUnwind::Context, data : Void*) do diff --git a/src/exception/call_stack/stackwalk.cr b/src/exception/call_stack/stackwalk.cr index c42977f9fa5f..89b387e7192b 100644 --- a/src/exception/call_stack/stackwalk.cr +++ b/src/exception/call_stack/stackwalk.cr @@ -31,7 +31,42 @@ struct Exception::CallStack LibC.SymSetOptions(LibC.SymGetOptions | LibC::SYMOPT_UNDNAME | LibC::SYMOPT_LOAD_LINES | LibC::SYMOPT_FAIL_CRITICAL_ERRORS | LibC::SYMOPT_NO_PROMPTS) end - def self.unwind + def self.setup_crash_handler + LibC.AddVectoredExceptionHandler(1, ->(exception_info) do + case status = exception_info.value.exceptionRecord.value.exceptionCode + when LibC::EXCEPTION_ACCESS_VIOLATION + addr = exception_info.value.exceptionRecord.value.exceptionInformation[1] + Crystal::System.print_error "Invalid memory access (C0000005) at address 0x%llx\n", addr + print_backtrace(exception_info) + LibC._exit(1) + when LibC::EXCEPTION_STACK_OVERFLOW + Crystal::System.print_error "Stack overflow (e.g., infinite or very deep recursion)\n" + print_backtrace(exception_info) + LibC._exit(1) + else + LibC::EXCEPTION_CONTINUE_SEARCH + end + end) + end + + protected def self.unwind + # TODO: use stack if possible (must be 16-byte aligned) + context = Pointer(LibC::CONTEXT).malloc(1) + context.value.contextFlags = LibC::CONTEXT_FULL + LibC.RtlCaptureContext(context) + + stack = [] of Void* + each_frame(context, LibC.GetCurrentThread) do |frame| + (frame.count + 1).times do + stack << frame.ip + end + end + stack + end + + private def self.each_frame(context, thread, &) + # unlike DWARF, this is required on Windows to even be able to produce + # correct stack traces, so we do it here but not in `libunwind.cr` load_debug_info machine_type = {% if flag?(:x86_64) %} @@ -43,11 +78,6 @@ struct Exception::CallStack {% raise "architecture not supported" %} {% end %} - # TODO: use stack if possible (must be 16-byte aligned) - context = Pointer(LibC::CONTEXT).malloc(1) - context.value.contextFlags = LibC::CONTEXT_FULL - LibC.RtlCaptureContext(context) - stack_frame = LibC::STACKFRAME64.new stack_frame.addrPC.mode = LibC::ADDRESS_MODE::AddrModeFlat stack_frame.addrFrame.mode = LibC::ADDRESS_MODE::AddrModeFlat @@ -57,13 +87,13 @@ struct Exception::CallStack stack_frame.addrFrame.offset = context.value.rbp stack_frame.addrStack.offset = context.value.rsp - stack = [] of Void* + last_frame = nil while true ret = LibC.StackWalk64( machine_type, LibC.GetCurrentProcess, - LibC.GetCurrentThread, + thread, pointerof(stack_frame), context, nil, @@ -72,10 +102,79 @@ struct Exception::CallStack nil ) break if ret == 0 - stack << Pointer(Void).new(stack_frame.addrPC.offset) + + ip = Pointer(Void).new(stack_frame.addrPC.offset) + if last_frame + if ip != last_frame.ip + yield last_frame + last_frame = RepeatedFrame.new(ip) + else + last_frame.incr + end + else + last_frame = RepeatedFrame.new(ip) + end end - stack + yield last_frame if last_frame + end + + struct RepeatedFrame + getter ip : Void*, count : Int32 + + def initialize(@ip : Void*) + @count = 0 + end + + def incr + @count += 1 + end + end + + private record StackContext, context : LibC::CONTEXT*, thread : LibC::HANDLE + + def self.print_backtrace(exception_info) : Nil + # we are still on the same thread which may have a corrupted stack, so we + # spawn a new thread and print the backtrace from a clean stack + info = StackContext.new(exception_info.value.contextRecord, LibC.GetCurrentThread) + handle = LibC.CreateThread(nil, 0, ->(lpParameter) { + the_info = lpParameter.as(StackContext*) + each_frame(the_info.value.context, the_info.value.thread) do |frame| + print_frame(frame) + end + LibC::DWORD.new!(0) + }, pointerof(info), 0, nil) + LibC.WaitForSingleObject(handle, LibC::INFINITE) + LibC.CloseHandle(handle) + end + + private def self.print_frame(repeated_frame) + if name = decode_function_name(repeated_frame.ip.address) + file, line, _ = decode_line_number(repeated_frame.ip.address) + if file != "??" && line != 0 + if repeated_frame.count == 0 + Crystal::System.print_error "[0x%llx] %s at %s:%ld\n", repeated_frame.ip, name, file, line + else + Crystal::System.print_error "[0x%llx] %s at %s:%ld (%ld times)\n", repeated_frame.ip, name, file, line, repeated_frame.count + 1 + end + return + end + end + + if frame = decode_frame(repeated_frame.ip) + offset, sname, fname = frame + if repeated_frame.count == 0 + Crystal::System.print_error "[0x%llx] %s +%lld in %s\n", repeated_frame.ip, sname, offset, fname + else + Crystal::System.print_error "[0x%llx] %s +%lld in %s (%ld times)\n", repeated_frame.ip, sname, offset, fname, repeated_frame.count + 1 + end + else + if repeated_frame.count == 0 + Crystal::System.print_error "[0x%llx] ???\n", repeated_frame.ip + else + Crystal::System.print_error "[0x%llx] ??? (%ld times)\n", repeated_frame.ip, repeated_frame.count + 1 + end + end end protected def self.decode_line_number(pc) @@ -86,19 +185,15 @@ struct Exception::CallStack if LibC.SymGetLineFromAddrW64(LibC.GetCurrentProcess, pc, out displacement, pointerof(line_info)) != 0 file_name = String.from_utf16(line_info.fileName)[0] - line_number = line_info.lineNumber + line_number = line_info.lineNumber.to_i32 else line_number = 0 end unless file_name - module_info = Pointer(LibC::IMAGEHLP_MODULEW64).malloc(1) - module_info.value.sizeOfStruct = sizeof(LibC::IMAGEHLP_MODULEW64) - - if LibC.SymGetModuleInfoW64(LibC.GetCurrentProcess, pc, module_info) != 0 - mod_displacement = pc - LibC.SymGetModuleBase64(LibC.GetCurrentProcess, pc) - image_name = String.from_utf16(module_info.value.loadedImageName.to_unsafe)[0] - file_name = "#{image_name} +#{mod_displacement}" + if m_info = sym_get_module_info(pc) + offset, image_name = m_info + file_name = "#{image_name} +#{offset}" else file_name = "??" end @@ -108,6 +203,37 @@ struct Exception::CallStack end protected def self.decode_function_name(pc) + if sym = sym_from_addr(pc) + _, sname = sym + sname + end + end + + protected def self.decode_frame(ip) + pc = decode_address(ip) + if sym = sym_from_addr(pc) + if m_info = sym_get_module_info(pc) + offset, sname = sym + _, fname = m_info + {offset, sname, fname} + end + end + end + + private def self.sym_get_module_info(pc) + load_debug_info + + module_info = Pointer(LibC::IMAGEHLP_MODULEW64).malloc(1) + module_info.value.sizeOfStruct = sizeof(LibC::IMAGEHLP_MODULEW64) + + if LibC.SymGetModuleInfoW64(LibC.GetCurrentProcess, pc, module_info) != 0 + mod_displacement = pc - LibC.SymGetModuleBase64(LibC.GetCurrentProcess, pc) + image_name = String.from_utf16(module_info.value.loadedImageName.to_unsafe)[0] + {mod_displacement, image_name} + end + end + + private def self.sym_from_addr(pc) load_debug_info symbol_size = sizeof(LibC::SYMBOL_INFOW) + (LibC::MAX_SYM_NAME - 1) * sizeof(LibC::WCHAR) @@ -117,13 +243,11 @@ struct Exception::CallStack sym_displacement = LibC::DWORD64.zero if LibC.SymFromAddrW(LibC.GetCurrentProcess, pc, pointerof(sym_displacement), symbol) != 0 - String.from_utf16(symbol.value.name.to_unsafe.to_slice(symbol.value.nameLen)) + symbol_str = String.from_utf16(symbol.value.name.to_unsafe.to_slice(symbol.value.nameLen)) + {sym_displacement, symbol_str} end end - protected def self.decode_frame(pc) - end - protected def self.decode_address(ip) ip.address end diff --git a/src/kernel.cr b/src/kernel.cr index c698fd6d4dff..6a7ca8017246 100644 --- a/src/kernel.cr +++ b/src/kernel.cr @@ -533,7 +533,6 @@ end end Signal.setup_default_handlers - Signal.setup_segfault_handler {% end %} # load debug info on start up of the program is executed with CRYSTAL_LOAD_DEBUG_INFO=1 @@ -543,6 +542,7 @@ end # - CRYSTAL_LOAD_DEBUG_INFO=1 will load debug info on startup # - Other values will load debug info on demand: when the backtrace of the first exception is generated Exception::CallStack.load_debug_info if ENV["CRYSTAL_LOAD_DEBUG_INFO"]? == "1" +Exception::CallStack.setup_crash_handler {% if flag?(:preview_mt) %} Crystal::Scheduler.init_workers diff --git a/src/lib_c/x86_64-windows-msvc/c/errhandlingapi.cr b/src/lib_c/x86_64-windows-msvc/c/errhandlingapi.cr index 039ce2dc0b17..44421fd2c2d0 100644 --- a/src/lib_c/x86_64-windows-msvc/c/errhandlingapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/errhandlingapi.cr @@ -1,6 +1,14 @@ require "c/int_safe" lib LibC + EXCEPTION_CONTINUE_SEARCH = LONG.new!(0) + + EXCEPTION_ACCESS_VIOLATION = 0xC0000005_u32 + EXCEPTION_STACK_OVERFLOW = 0xC00000FD_u32 + + alias PVECTORED_EXCEPTION_HANDLER = EXCEPTION_POINTERS* -> LONG + fun GetLastError : DWORD fun SetLastError(dwErrCode : DWORD) + fun AddVectoredExceptionHandler(first : DWORD, handler : PVECTORED_EXCEPTION_HANDLER) : Void* end diff --git a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr index ca76d7ca36cf..b1e823ba05ca 100644 --- a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr @@ -32,6 +32,8 @@ lib LibC hStdError : HANDLE end + alias LPTHREAD_START_ROUTINE = Void* -> DWORD + fun GetCurrentThread : HANDLE fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void fun GetCurrentProcess : HANDLE @@ -43,6 +45,9 @@ lib LibC bInheritHandles : BOOL, dwCreationFlags : DWORD, lpEnvironment : Void*, lpCurrentDirectory : LPWSTR, lpStartupInfo : STARTUPINFOW*, lpProcessInformation : PROCESS_INFORMATION*) : BOOL + fun CreateThread(lpThreadAttributes : SECURITY_ATTRIBUTES*, dwStackSize : SizeT, + lpStartAddress : LPTHREAD_START_ROUTINE, lpParameter : Void*, + dwCreationFlags : DWORD, lpThreadId : DWORD*) : HANDLE fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*, lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL diff --git a/src/lib_c/x86_64-windows-msvc/c/winnt.cr b/src/lib_c/x86_64-windows-msvc/c/winnt.cr index e910a217e1bd..edd247c2c723 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winnt.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winnt.cr @@ -153,4 +153,19 @@ lib LibC {% end %} fun RtlCaptureContext(contextRecord : CONTEXT*) + + struct EXCEPTION_RECORD64 + exceptionCode : DWORD + exceptionFlags : DWORD + exceptionRecord : DWORD64 + exceptionAddress : DWORD64 + numberParameters : DWORD + __unusedAlignment : DWORD + exceptionInformation : DWORD64[15] + end + + struct EXCEPTION_POINTERS + exceptionRecord : EXCEPTION_RECORD64* + contextRecord : CONTEXT* + end end From 4eac5531b8d40fd4bb7795ec474ec9b77d2819d3 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sat, 11 Dec 2021 22:17:14 +0800 Subject: [PATCH 2/5] absolutely suspend new thread --- src/exception/call_stack/stackwalk.cr | 3 ++- src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/exception/call_stack/stackwalk.cr b/src/exception/call_stack/stackwalk.cr index 89b387e7192b..77eda5a69f1b 100644 --- a/src/exception/call_stack/stackwalk.cr +++ b/src/exception/call_stack/stackwalk.cr @@ -143,7 +143,8 @@ struct Exception::CallStack print_frame(frame) end LibC::DWORD.new!(0) - }, pointerof(info), 0, nil) + }, pointerof(info), LibC::CREATE_SUSPENDED, nil) + LibC.ResumeThread(handle) LibC.WaitForSingleObject(handle, LibC::INFINITE) LibC.CloseHandle(handle) end diff --git a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr index b1e823ba05ca..2221b5cc6661 100644 --- a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr @@ -34,6 +34,8 @@ lib LibC alias LPTHREAD_START_ROUTINE = Void* -> DWORD + CREATE_SUSPENDED = 0x00000004 + fun GetCurrentThread : HANDLE fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void fun GetCurrentProcess : HANDLE @@ -48,6 +50,7 @@ lib LibC fun CreateThread(lpThreadAttributes : SECURITY_ATTRIBUTES*, dwStackSize : SizeT, lpStartAddress : LPTHREAD_START_ROUTINE, lpParameter : Void*, dwCreationFlags : DWORD, lpThreadId : DWORD*) : HANDLE + fun ResumeThread(hThread : HANDLE) : DWORD fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*, lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL From 0a3da8d49ae910b17e0877e336d8f2dc161bf022 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sat, 11 Dec 2021 22:34:12 +0800 Subject: [PATCH 3/5] take 2 --- src/exception/call_stack/stackwalk.cr | 5 +++++ src/lib_c/x86_64-windows-msvc/c/malloc.cr | 3 +++ src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr | 1 + 3 files changed, 9 insertions(+) create mode 100644 src/lib_c/x86_64-windows-msvc/c/malloc.cr diff --git a/src/exception/call_stack/stackwalk.cr b/src/exception/call_stack/stackwalk.cr index 77eda5a69f1b..6f08d0b9ec55 100644 --- a/src/exception/call_stack/stackwalk.cr +++ b/src/exception/call_stack/stackwalk.cr @@ -1,4 +1,5 @@ require "c/dbghelp" +require "c/malloc" # :nodoc: struct Exception::CallStack @@ -40,6 +41,7 @@ struct Exception::CallStack print_backtrace(exception_info) LibC._exit(1) when LibC::EXCEPTION_STACK_OVERFLOW + LibC._resetstkoflw Crystal::System.print_error "Stack overflow (e.g., infinite or very deep recursion)\n" print_backtrace(exception_info) LibC._exit(1) @@ -47,6 +49,9 @@ struct Exception::CallStack LibC::EXCEPTION_CONTINUE_SEARCH end end) + + stack_size = LibC::DWORD.new!(4096) + LibC.SetThreadStackGuarantee(pointerof(stack_size)) end protected def self.unwind diff --git a/src/lib_c/x86_64-windows-msvc/c/malloc.cr b/src/lib_c/x86_64-windows-msvc/c/malloc.cr new file mode 100644 index 000000000000..9db8c805846f --- /dev/null +++ b/src/lib_c/x86_64-windows-msvc/c/malloc.cr @@ -0,0 +1,3 @@ +lib LibC + fun _resetstkoflw : Int +end diff --git a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr index 2221b5cc6661..c4ee7382a973 100644 --- a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr @@ -51,6 +51,7 @@ lib LibC lpStartAddress : LPTHREAD_START_ROUTINE, lpParameter : Void*, dwCreationFlags : DWORD, lpThreadId : DWORD*) : HANDLE fun ResumeThread(hThread : HANDLE) : DWORD + fun SetThreadStackGuarantee(stackSizeInBytes : DWORD*) : BOOL fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*, lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL From 9e9c0ac5e5eded1e22955f4715a48d44697866b1 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sun, 12 Dec 2021 00:06:04 +0800 Subject: [PATCH 4/5] take 3 --- src/exception/call_stack/stackwalk.cr | 30 ++++++++----------- .../c/processthreadsapi.cr | 8 ----- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/exception/call_stack/stackwalk.cr b/src/exception/call_stack/stackwalk.cr index 6f08d0b9ec55..c41c9a2635ce 100644 --- a/src/exception/call_stack/stackwalk.cr +++ b/src/exception/call_stack/stackwalk.cr @@ -50,7 +50,9 @@ struct Exception::CallStack end end) - stack_size = LibC::DWORD.new!(4096) + # ensure that even in the case of stack overflow there is enough reserved + # stack space for recovery + stack_size = LibC::DWORD.new!(0x10000) LibC.SetThreadStackGuarantee(pointerof(stack_size)) end @@ -61,7 +63,7 @@ struct Exception::CallStack LibC.RtlCaptureContext(context) stack = [] of Void* - each_frame(context, LibC.GetCurrentThread) do |frame| + each_frame(context) do |frame| (frame.count + 1).times do stack << frame.ip end @@ -69,7 +71,7 @@ struct Exception::CallStack stack end - private def self.each_frame(context, thread, &) + private def self.each_frame(context, &) # unlike DWARF, this is required on Windows to even be able to produce # correct stack traces, so we do it here but not in `libunwind.cr` load_debug_info @@ -93,12 +95,14 @@ struct Exception::CallStack stack_frame.addrStack.offset = context.value.rsp last_frame = nil + cur_proc = LibC.GetCurrentProcess + cur_thread = LibC.GetCurrentThread while true ret = LibC.StackWalk64( machine_type, - LibC.GetCurrentProcess, - thread, + cur_proc, + cur_thread, pointerof(stack_frame), context, nil, @@ -139,19 +143,9 @@ struct Exception::CallStack private record StackContext, context : LibC::CONTEXT*, thread : LibC::HANDLE def self.print_backtrace(exception_info) : Nil - # we are still on the same thread which may have a corrupted stack, so we - # spawn a new thread and print the backtrace from a clean stack - info = StackContext.new(exception_info.value.contextRecord, LibC.GetCurrentThread) - handle = LibC.CreateThread(nil, 0, ->(lpParameter) { - the_info = lpParameter.as(StackContext*) - each_frame(the_info.value.context, the_info.value.thread) do |frame| - print_frame(frame) - end - LibC::DWORD.new!(0) - }, pointerof(info), LibC::CREATE_SUSPENDED, nil) - LibC.ResumeThread(handle) - LibC.WaitForSingleObject(handle, LibC::INFINITE) - LibC.CloseHandle(handle) + each_frame(exception_info.value.contextRecord) do |frame| + print_frame(frame) + end end private def self.print_frame(repeated_frame) diff --git a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr index c4ee7382a973..6d0336a37e16 100644 --- a/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/processthreadsapi.cr @@ -32,10 +32,6 @@ lib LibC hStdError : HANDLE end - alias LPTHREAD_START_ROUTINE = Void* -> DWORD - - CREATE_SUSPENDED = 0x00000004 - fun GetCurrentThread : HANDLE fun GetCurrentThreadStackLimits(lowLimit : ULONG_PTR*, highLimit : ULONG_PTR*) : Void fun GetCurrentProcess : HANDLE @@ -47,10 +43,6 @@ lib LibC bInheritHandles : BOOL, dwCreationFlags : DWORD, lpEnvironment : Void*, lpCurrentDirectory : LPWSTR, lpStartupInfo : STARTUPINFOW*, lpProcessInformation : PROCESS_INFORMATION*) : BOOL - fun CreateThread(lpThreadAttributes : SECURITY_ATTRIBUTES*, dwStackSize : SizeT, - lpStartAddress : LPTHREAD_START_ROUTINE, lpParameter : Void*, - dwCreationFlags : DWORD, lpThreadId : DWORD*) : HANDLE - fun ResumeThread(hThread : HANDLE) : DWORD fun SetThreadStackGuarantee(stackSizeInBytes : DWORD*) : BOOL fun GetProcessTimes(hProcess : HANDLE, lpCreationTime : FILETIME*, lpExitTime : FILETIME*, lpKernelTime : FILETIME*, lpUserTime : FILETIME*) : BOOL From cd2e57acc8f8c50c206d14527141c1fa68a4dbdc Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Tue, 14 Dec 2021 14:05:54 +0800 Subject: [PATCH 5/5] fixup --- src/exception/call_stack/stackwalk.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/exception/call_stack/stackwalk.cr b/src/exception/call_stack/stackwalk.cr index c41c9a2635ce..f45b87acffd6 100644 --- a/src/exception/call_stack/stackwalk.cr +++ b/src/exception/call_stack/stackwalk.cr @@ -56,7 +56,8 @@ struct Exception::CallStack LibC.SetThreadStackGuarantee(pointerof(stack_size)) end - protected def self.unwind + {% if flag?(:interpreted) %} @[Primitive(:interpreter_call_stack_unwind)] {% end %} + protected def self.unwind : Array(Void*) # TODO: use stack if possible (must be 16-byte aligned) context = Pointer(LibC::CONTEXT).malloc(1) context.value.contextFlags = LibC::CONTEXT_FULL