From 0054bbed7fce5b8566655d6910b09b10c952e609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Tue, 26 Nov 2024 10:50:20 +0000 Subject: [PATCH] 8343756: CAN_SHOW_REGISTERS_ON_ASSERT for Windows Reviewed-by: stuefe, jsjolen --- src/hotspot/os/posix/os_posix.cpp | 42 ++++++- src/hotspot/os/posix/signals_posix.cpp | 15 ++- src/hotspot/os/posix/vmError_posix.cpp | 15 ++- src/hotspot/os/windows/os_windows.cpp | 44 +++++++- src/hotspot/os/windows/vmError_windows.cpp | 22 +++- src/hotspot/share/runtime/os.hpp | 6 +- src/hotspot/share/utilities/debug.cpp | 104 ++++++++---------- src/hotspot/share/utilities/debug.hpp | 7 +- src/hotspot/share/utilities/vmError.cpp | 18 +-- src/hotspot/share/utilities/vmError.hpp | 25 +++-- .../ShowRegistersOnAssertTest.java | 18 ++- 11 files changed, 219 insertions(+), 97 deletions(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 9aae3b5c143..7d418d4ad43 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -2111,7 +2111,7 @@ void os::shutdown() { // easily trigger secondary faults in those threads. To reduce the likelihood // of that we use _exit rather than exit, so that no atexit hooks get run. // But note that os::shutdown() could also trigger secondary faults. -void os::abort(bool dump_core, void* siginfo, const void* context) { +void os::abort(bool dump_core, const void* siginfo, const void* context) { os::shutdown(); if (dump_core) { LINUX_ONLY(if (DumpPrivateMappingsInCore) ClassLoader::close_jrt_image();) @@ -2186,3 +2186,43 @@ char* os::pd_map_memory(int fd, const char* unused, bool os::pd_unmap_memory(char* addr, size_t bytes) { return munmap(addr, bytes) == 0; } + +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT +static ucontext_t _saved_assert_context; +static bool _has_saved_context = false; +#endif // CAN_SHOW_REGISTERS_ON_ASSERT + +void os::save_assert_context(const void* ucVoid) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + assert(ucVoid != nullptr, "invariant"); + assert(!_has_saved_context, "invariant"); + memcpy(&_saved_assert_context, ucVoid, sizeof(ucontext_t)); + // on Linux ppc64, ucontext_t contains pointers into itself which have to be patched up + // after copying the context (see comment in sys/ucontext.h): +#if defined(PPC64) + *((void**)&_saved_assert_context.uc_mcontext.regs) = &(_saved_assert_context.uc_mcontext.gp_regs); +#elif defined(AMD64) + // In the copied version, fpregs should point to the copied contents. + // Sanity check: fpregs should point into the context. + if ((address)((const ucontext_t*)ucVoid)->uc_mcontext.fpregs > (address)ucVoid) { + size_t fpregs_offset = pointer_delta(((const ucontext_t*)ucVoid)->uc_mcontext.fpregs, ucVoid, 1); + if (fpregs_offset < sizeof(ucontext_t)) { + // Preserve the offset. + *((void**)&_saved_assert_context.uc_mcontext.fpregs) = (void*)((address)(void*)&_saved_assert_context + fpregs_offset); + } + } +#endif + _has_saved_context = true; +#endif // CAN_SHOW_REGISTERS_ON_ASSERT +} + +const void* os::get_saved_assert_context(const void** sigInfo) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + assert(sigInfo != nullptr, "invariant"); + *sigInfo = nullptr; + return _has_saved_context ? &_saved_assert_context : nullptr; +#endif + *sigInfo = nullptr; + return nullptr; +} + diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 6a14d0a4856..bbf122fabfb 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -578,9 +578,8 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info, // Handle assertion poison page accesses. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (!signal_was_handled && - ((sig == SIGSEGV || sig == SIGBUS) && info != nullptr && info->si_addr == g_assert_poison)) { - signal_was_handled = handle_assert_poison_fault(ucVoid, info->si_addr); + if (VMError::was_assert_poison_crash(info)) { + signal_was_handled = handle_assert_poison_fault(ucVoid); } #endif @@ -1136,8 +1135,16 @@ static const char* get_signal_name(int sig, char* out, size_t outlen) { } void os::print_siginfo(outputStream* os, const void* si0) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + // If we are here because of an assert/guarantee, we suppress + // printing the siginfo, because it is only an implementation + // detail capturing the context for said assert/guarantee. + if (VMError::was_assert_poison_crash(si0)) { + return; + } +#endif - const siginfo_t* const si = (const siginfo_t*) si0; + const siginfo_t* const si = (const siginfo_t*)si0; char buf[20]; os->print("siginfo:"); diff --git a/src/hotspot/os/posix/vmError_posix.cpp b/src/hotspot/os/posix/vmError_posix.cpp index 30ebf8092f8..c1d89efa855 100644 --- a/src/hotspot/os/posix/vmError_posix.cpp +++ b/src/hotspot/os/posix/vmError_posix.cpp @@ -84,8 +84,8 @@ static void crash_handler(int sig, siginfo_t* info, void* context) { // Needed because asserts may happen in error handling too. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != nullptr && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(context, info->si_addr)) { + if (VMError::was_assert_poison_crash(info)) { + if (handle_assert_poison_fault(context)) { return; } } @@ -127,3 +127,14 @@ void VMError::check_failing_cds_access(outputStream* st, const void* siginfo) { } #endif } + +bool VMError::was_assert_poison_crash(const void* siginfo) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if (siginfo == nullptr) { + return false; + } + const siginfo_t* const si = (siginfo_t*)siginfo; + return (si->si_signo == SIGSEGV || si->si_signo == SIGBUS) && si->si_addr == g_assert_poison_read_only; +#endif + return false; +} diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index fd857c2cd95..8bb3789bbad 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -72,6 +72,7 @@ #include "services/runtimeService.hpp" #include "symbolengine.hpp" #include "utilities/align.hpp" +#include "utilities/debug.hpp" #include "utilities/decoder.hpp" #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" @@ -1317,7 +1318,7 @@ void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool che } } -void os::abort(bool dump_core, void* siginfo, const void* context) { +void os::abort(bool dump_core, const void* siginfo, const void* context) { EXCEPTION_POINTERS ep; MINIDUMP_EXCEPTION_INFORMATION mei; MINIDUMP_EXCEPTION_INFORMATION* pmei; @@ -2112,7 +2113,17 @@ bool os::signal_sent_by_kill(const void* siginfo) { } void os::print_siginfo(outputStream *st, const void* siginfo) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + // If we are here because of an assert/guarantee, we suppress + // printing the siginfo, because it is only an implementation + // detail capturing the context for said assert/guarantee. + if (VMError::was_assert_poison_crash(siginfo)) { + return; + } +#endif + const EXCEPTION_RECORD* const er = (EXCEPTION_RECORD*)siginfo; + st->print("siginfo:"); char tmp[64]; @@ -2625,6 +2636,14 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { #endif #endif +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if (VMError::was_assert_poison_crash(exception_record)) { + if (handle_assert_poison_fault(exceptionInfo)) { + return EXCEPTION_CONTINUE_EXECUTION; + } + } +#endif + if (t != nullptr && t->is_Java_thread()) { JavaThread* thread = JavaThread::cast(t); bool in_java = thread->thread_state() == _thread_in_Java; @@ -6165,3 +6184,26 @@ void os::print_user_info(outputStream* st) { void os::print_active_locale(outputStream* st) { // not implemented yet } + +static CONTEXT _saved_assert_context; +static EXCEPTION_RECORD _saved_exception_record; +static bool _has_saved_context = false; + +void os::save_assert_context(const void* ucVoid) { + assert(ucVoid != nullptr, "invariant"); + assert(!_has_saved_context, "invariant"); + const EXCEPTION_POINTERS* ep = static_cast(ucVoid); + memcpy(&_saved_assert_context, ep->ContextRecord, sizeof(CONTEXT)); + memcpy(&_saved_exception_record, ep->ExceptionRecord, sizeof(EXCEPTION_RECORD)); + _has_saved_context = true; +} + +const void* os::get_saved_assert_context(const void** sigInfo) { + assert(sigInfo != nullptr, "invariant"); + if (_has_saved_context) { + *sigInfo = &_saved_exception_record; + return &_saved_assert_context; + } + *sigInfo = nullptr; + return nullptr; +} diff --git a/src/hotspot/os/windows/vmError_windows.cpp b/src/hotspot/os/windows/vmError_windows.cpp index 363ec3192ce..705e04e77db 100644 --- a/src/hotspot/os/windows/vmError_windows.cpp +++ b/src/hotspot/os/windows/vmError_windows.cpp @@ -28,6 +28,7 @@ #include "runtime/arguments.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" +#include "utilities/debug.hpp" #include "utilities/vmError.hpp" LONG WINAPI crash_handler(struct _EXCEPTION_POINTERS* exceptionInfo) { @@ -67,10 +68,23 @@ void VMError::check_failing_cds_access(outputStream* st, const void* siginfo) { void VMError::reporting_started() {} void VMError::interrupt_reporting_thread() {} -void VMError::raise_fail_fast(void* exrecord, void* context) { +void VMError::raise_fail_fast(const void* exrecord, const void* context) { DWORD flags = (exrecord == nullptr) ? FAIL_FAST_GENERATE_EXCEPTION_ADDRESS : 0; - RaiseFailFastException(static_cast(exrecord), - static_cast(context), - flags); + PEXCEPTION_RECORD exception_record = static_cast(const_cast(exrecord)); + PCONTEXT ctx = static_cast(const_cast(context)); + RaiseFailFastException(exception_record, ctx, flags); ::abort(); } + +bool VMError::was_assert_poison_crash(const void* siginfo) { +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if (siginfo == nullptr) { + return false; + } + const EXCEPTION_RECORD* const er = (EXCEPTION_RECORD*)siginfo; + if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && er->NumberParameters >= 2) { + return (void*)er->ExceptionInformation[1] == g_assert_poison_read_only; + } +#endif + return false; +} diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 54771f622e9..8e4a214b691 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -617,6 +617,10 @@ class os: AllStatic { static frame fetch_frame_from_context(const void* ucVoid); static frame fetch_compiled_frame_from_context(const void* ucVoid); + // For saving an os specific context generated by an assert or guarantee. + static void save_assert_context(const void* ucVoid); + static const void* get_saved_assert_context(const void** sigInfo); + static void breakpoint(); static bool start_debugging(char *buf, int buflen); @@ -643,7 +647,7 @@ class os: AllStatic { // Terminate with an error. Default is to generate a core file on platforms // that support such things. This calls shutdown() and then aborts. - [[noreturn]] static void abort(bool dump_core, void *siginfo, const void *context); + [[noreturn]] static void abort(bool dump_core, const void *siginfo, const void *context); [[noreturn]] static void abort(bool dump_core = true); // Die immediately, no exit hook, no abort hook, no cleanup. diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 88730c1e6ec..7286f70412a 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -74,8 +74,8 @@ #ifdef CAN_SHOW_REGISTERS_ON_ASSERT static char g_dummy; char* g_assert_poison = &g_dummy; +const char* g_assert_poison_read_only = &g_dummy; static intx g_asserting_thread = 0; -static void* g_assertion_context = nullptr; #endif // CAN_SHOW_REGISTERS_ON_ASSERT int DebuggingContext::_enabled = 0; // Initially disabled. @@ -181,16 +181,21 @@ void report_vm_error(const char* file, int line, const char* error_msg, const ch { va_list detail_args; va_start(detail_args, detail_fmt); - void* context = nullptr; -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (g_assertion_context != nullptr && os::current_thread_id() == g_asserting_thread) { - context = g_assertion_context; - } -#endif // CAN_SHOW_REGISTERS_ON_ASSERT print_error_for_unit_test(error_msg, detail_fmt, detail_args); - VMError::report_and_die(Thread::current_or_null(), context, file, line, error_msg, detail_fmt, detail_args); + const void* context = nullptr; + const void* siginfo = nullptr; + +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if (os::current_thread_id() == g_asserting_thread) { + context = os::get_saved_assert_context(&siginfo); + } +#endif // CAN_SHOW_REGISTERS_ON_ASSERT + + VMError::report_and_die(INTERNAL_ERROR, error_msg, detail_fmt, detail_args, + Thread::current_or_null(), nullptr, siginfo, context, + file, line, 0); va_end(detail_args); } @@ -202,17 +207,21 @@ void report_vm_status_error(const char* file, int line, const char* error_msg, void report_fatal(VMErrorType error_type, const char* file, int line, const char* detail_fmt, ...) { va_list detail_args; va_start(detail_args, detail_fmt); - void* context = nullptr; -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if (g_assertion_context != nullptr && os::current_thread_id() == g_asserting_thread) { - context = g_assertion_context; - } -#endif // CAN_SHOW_REGISTERS_ON_ASSERT + print_error_for_unit_test("fatal error", detail_fmt, detail_args); + const void* context = nullptr; + const void* siginfo = nullptr; + +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if (os::current_thread_id() == g_asserting_thread) { + context = os::get_saved_assert_context(&siginfo); + } +#endif // CAN_SHOW_REGISTERS_ON_ASSERT + VMError::report_and_die(error_type, "fatal error", detail_fmt, detail_args, - Thread::current_or_null(), nullptr, nullptr, context, + Thread::current_or_null(), nullptr, siginfo, context, file, line, 0); va_end(detail_args); } @@ -705,9 +714,6 @@ struct TestMultipleStaticAssertFormsInClassScope { // Support for showing register content on asserts/guarantees. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT - -static ucontext_t g_stored_assertion_context; - void initialize_assert_poison() { char* page = os::reserve_memory(os::vm_page_size()); if (page) { @@ -715,6 +721,7 @@ void initialize_assert_poison() { if (os::commit_memory(page, os::vm_page_size(), false) && os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) { g_assert_poison = page; + g_assert_poison_read_only = page; } } } @@ -723,48 +730,29 @@ void disarm_assert_poison() { g_assert_poison = &g_dummy; } -static void store_context(const void* context) { - memcpy(&g_stored_assertion_context, context, sizeof(ucontext_t)); -#if defined(LINUX) - // on Linux ppc64, ucontext_t contains pointers into itself which have to be patched up - // after copying the context (see comment in sys/ucontext.h): -#if defined(PPC64) - *((void**) &g_stored_assertion_context.uc_mcontext.regs) = &(g_stored_assertion_context.uc_mcontext.gp_regs); -#elif defined(AMD64) - // In the copied version, fpregs should point to the copied contents. - // Sanity check: fpregs should point into the context. - if ((address)((const ucontext_t*)context)->uc_mcontext.fpregs > (address)context) { - size_t fpregs_offset = pointer_delta(((const ucontext_t*)context)->uc_mcontext.fpregs, context, 1); - if (fpregs_offset < sizeof(ucontext_t)) { - // Preserve the offset. - *((void**) &g_stored_assertion_context.uc_mcontext.fpregs) = (void*)((address)(void*)&g_stored_assertion_context + fpregs_offset); - } - } -#endif -#endif -} - -bool handle_assert_poison_fault(const void* ucVoid, const void* faulting_address) { - if (faulting_address == g_assert_poison) { - // Disarm poison page. - if (os::protect_memory((char*)g_assert_poison, os::vm_page_size(), os::MEM_PROT_RWX) == false) { #ifdef ASSERT - fprintf(stderr, "Assertion poison page cannot be unprotected - mprotect failed with %d (%s)", - errno, os::strerror(errno)); - fflush(stderr); +static void print_unprotect_error() { + fprintf(stderr, "Assertion poison page cannot be unprotected - mprotect failed with %d (%s)", + errno, os::strerror(errno)); + fflush(stderr); +} #endif - return false; // unprotecting memory may fail in OOM situations, as surprising as this sounds. - } - // Store Context away. - if (ucVoid) { - const intx my_tid = os::current_thread_id(); - if (Atomic::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) { - store_context(ucVoid); - g_assertion_context = &g_stored_assertion_context; - } - } - return true; + +// TOUCH_ASSERT_POISON writes to the protected g_assert_poison page, which faults +// and enters platform signal handlers which in turn invokes this routine. +bool handle_assert_poison_fault(const void* ucVoid) { + // Disarm poison page. + if (!os::protect_memory((char*)g_assert_poison, os::vm_page_size(), os::MEM_PROT_RWX)) { + DEBUG_ONLY(print_unprotect_error();) + return false; // unprotecting memory may fail in OOM situations, as surprising as this sounds. } - return false; + if (ucVoid != nullptr) { + // Save context. + const intx my_tid = os::current_thread_id(); + if (Atomic::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) { + os::save_assert_context(ucVoid); + } + } + return true; } #endif // CAN_SHOW_REGISTERS_ON_ASSERT diff --git a/src/hotspot/share/utilities/debug.hpp b/src/hotspot/share/utilities/debug.hpp index 567e72cda57..12724153659 100644 --- a/src/hotspot/share/utilities/debug.hpp +++ b/src/hotspot/share/utilities/debug.hpp @@ -34,14 +34,15 @@ class oopDesc; -// ShowRegistersOnAssert support (for now Linux only) -#if defined(LINUX) && !defined(ZERO) +// ShowRegistersOnAssert support (for now Linux and Windows only) +#if (defined(LINUX) || defined(_WINDOWS)) && !defined(ZERO) #define CAN_SHOW_REGISTERS_ON_ASSERT extern char* g_assert_poison; +extern const char* g_assert_poison_read_only; #define TOUCH_ASSERT_POISON (*g_assert_poison) = 'X'; void initialize_assert_poison(); void disarm_assert_poison(); -bool handle_assert_poison_fault(const void* ucVoid, const void* faulting_address); +bool handle_assert_poison_fault(const void* ucVoid); #else #define TOUCH_ASSERT_POISON #endif // CAN_SHOW_REGISTERS_ON_ASSERT diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index cdece538cba..bb57c19c528 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -95,8 +95,8 @@ const char* VMError::_message; char VMError::_detail_msg[1024]; Thread* VMError::_thread; address VMError::_pc; -void* VMError::_siginfo; -void* VMError::_context; +const void* VMError::_siginfo; +const void* VMError::_context; bool VMError::_print_native_stack_used = false; const char* VMError::_filename; int VMError::_lineno; @@ -532,7 +532,7 @@ static void print_oom_reasons(outputStream* st) { st->print_cr("# This output file may be truncated or incomplete."); } -static void print_stack_location(outputStream* st, void* context, int& continuation) { +static void print_stack_location(outputStream* st, const void* context, int& continuation) { const int number_of_stack_slots = 8; int i = continuation; @@ -1583,8 +1583,8 @@ int VMError::prepare_log_file(const char* pattern, const char* default_pattern, return fd; } -void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, - void* context, const char* detail_fmt, ...) +void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, const void* siginfo, + const void* context, const char* detail_fmt, ...) { va_list detail_args; va_start(detail_args, detail_fmt); @@ -1592,7 +1592,7 @@ void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, void* va_end(detail_args); } -void VMError::report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, +void VMError::report_and_die(Thread* thread, const void* context, const char* filename, int lineno, const char* message, const char* detail_fmt, ...) { va_list detail_args; va_start(detail_args, detail_fmt); @@ -1600,12 +1600,12 @@ void VMError::report_and_die(Thread* thread, void* context, const char* filename va_end(detail_args); } -void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, void* context) +void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, const void* siginfo, const void* context) { report_and_die(thread, sig, pc, siginfo, context, "%s", ""); } -void VMError::report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, +void VMError::report_and_die(Thread* thread, const void* context, const char* filename, int lineno, const char* message, const char* detail_fmt, va_list detail_args) { report_and_die(INTERNAL_ERROR, message, detail_fmt, detail_args, thread, nullptr, nullptr, context, filename, lineno, 0); @@ -1617,7 +1617,7 @@ void VMError::report_and_die(Thread* thread, const char* filename, int lineno, s } void VMError::report_and_die(int id, const char* message, const char* detail_fmt, va_list detail_args, - Thread* thread, address pc, void* siginfo, void* context, const char* filename, + Thread* thread, address pc, const void* siginfo, const void* context, const char* filename, int lineno, size_t size) { // A single scratch buffer to be used from here on. diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index dee8335afd5..405cb515896 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -46,9 +46,9 @@ class VMError : public AllStatic { // additional info for crashes static address _pc; // faulting PC - static void* _siginfo; // ExceptionRecord on Windows, + static const void* _siginfo; // ExceptionRecord on Windows, // siginfo_t on Solaris/Linux - static void* _context; // ContextRecord on Windows, + static const void* _context; // ContextRecord on Windows, // ucontext_t on Solaris/Linux // records if VMError::print_native_stack was used to @@ -144,7 +144,7 @@ class VMError : public AllStatic { static jlong get_step_start_time(); static void clear_step_start_time(); - WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(void* exrecord, void* context);) + WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(const void* exrecord, const void* context);) public: @@ -166,28 +166,28 @@ public: // main error reporting function [[noreturn]] ATTRIBUTE_PRINTF(6, 7) - static void report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, - void* context, const char* detail_fmt, ...); + static void report_and_die(Thread* thread, unsigned int sig, address pc, const void* siginfo, + const void* context, const char* detail_fmt, ...); [[noreturn]] ATTRIBUTE_PRINTF(6, 7) - static void report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, - const char* detail_fmt, ...); + static void report_and_die(Thread* thread, const void* context, const char* filename, + int lineno, const char* message, const char* detail_fmt, ...); [[noreturn]] ATTRIBUTE_PRINTF(3, 0) static void report_and_die(int id, const char* message, const char* detail_fmt, va_list detail_args, - Thread* thread, address pc, void* siginfo, void* context, + Thread* thread, address pc, const void* siginfo, const void* context, const char* filename, int lineno, size_t size); [[noreturn]] static void report_and_die(Thread* thread, unsigned int sig, address pc, - void* siginfo, void* context); + const void* siginfo, const void* context); [[noreturn]] ATTRIBUTE_PRINTF(6, 0) - static void report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, - const char* detail_fmt, va_list detail_args); + static void report_and_die(Thread* thread, const void* context, const char* filename, + int lineno, const char* message, const char* detail_fmt, va_list detail_args); [[noreturn]] ATTRIBUTE_PRINTF(6, 0) @@ -225,6 +225,7 @@ public: // permissions. static int prepare_log_file(const char* pattern, const char* default_pattern, bool overwrite_existing, char* buf, size_t buflen); + static bool was_assert_poison_crash(const void* sigInfo); }; class VMErrorCallback { diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java index b861e44532d..d9ccac0a3df 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2022 SAP SE. All rights reserved. - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ * @summary Show Registers on assert/guarantee * @library /test/lib * @requires vm.flagless - * @requires (vm.debug == true) & (os.family == "linux") + * @requires vm.debug == true & (os.family == "linux" | os.family == "windows") * @author Thomas Stuefe (SAP) * @modules java.base/jdk.internal.misc * java.management @@ -67,6 +67,20 @@ public class ShowRegistersOnAssertTest { // (which would be a sign that the assert poison page mechanism does not work). output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); output_detail.shouldMatch("# +Internal Error.*"); + if (show_registers_on_assert) { + // Extract the hs_err_pid file. + File hs_err_file = HsErrFileUtils.openHsErrFileFromOutput(output_detail); + Pattern[] pattern = new Pattern[] { Pattern.compile("Registers:"), null }; + if (Platform.isX64()) { + pattern[1] = Pattern.compile("RAX=.*"); + } else if (Platform.isX86()) { + pattern[1] = Pattern.compile("EAX=.*"); + } else if (Platform.isAArch64()) { + pattern[1] = Pattern.compile("R0=.*"); + } + // Pattern match the hs_err_pid file. + HsErrFileUtils.checkHsErrFileContent(hs_err_file, pattern, false); + } } public static void main(String[] args) throws Exception {