8343756: CAN_SHOW_REGISTERS_ON_ASSERT for Windows

Reviewed-by: stuefe, jsjolen
This commit is contained in:
Markus Grönlund 2024-11-26 10:50:20 +00:00
parent 3a625f38aa
commit 0054bbed7f
11 changed files with 219 additions and 97 deletions

View File

@ -2111,7 +2111,7 @@ void os::shutdown() {
// easily trigger secondary faults in those threads. To reduce the likelihood // 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. // 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. // 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(); os::shutdown();
if (dump_core) { if (dump_core) {
LINUX_ONLY(if (DumpPrivateMappingsInCore) ClassLoader::close_jrt_image();) 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) { bool os::pd_unmap_memory(char* addr, size_t bytes) {
return munmap(addr, bytes) == 0; 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;
}

View File

@ -578,9 +578,8 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info,
// Handle assertion poison page accesses. // Handle assertion poison page accesses.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT #ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if (!signal_was_handled && if (VMError::was_assert_poison_crash(info)) {
((sig == SIGSEGV || sig == SIGBUS) && info != nullptr && info->si_addr == g_assert_poison)) { signal_was_handled = handle_assert_poison_fault(ucVoid);
signal_was_handled = handle_assert_poison_fault(ucVoid, info->si_addr);
} }
#endif #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) { 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]; char buf[20];
os->print("siginfo:"); os->print("siginfo:");

View File

@ -84,8 +84,8 @@ static void crash_handler(int sig, siginfo_t* info, void* context) {
// Needed because asserts may happen in error handling too. // Needed because asserts may happen in error handling too.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT #ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != nullptr && info->si_addr == g_assert_poison) { if (VMError::was_assert_poison_crash(info)) {
if (handle_assert_poison_fault(context, info->si_addr)) { if (handle_assert_poison_fault(context)) {
return; return;
} }
} }
@ -127,3 +127,14 @@ void VMError::check_failing_cds_access(outputStream* st, const void* siginfo) {
} }
#endif #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;
}

View File

@ -72,6 +72,7 @@
#include "services/runtimeService.hpp" #include "services/runtimeService.hpp"
#include "symbolengine.hpp" #include "symbolengine.hpp"
#include "utilities/align.hpp" #include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include "utilities/decoder.hpp" #include "utilities/decoder.hpp"
#include "utilities/defaultStream.hpp" #include "utilities/defaultStream.hpp"
#include "utilities/events.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; EXCEPTION_POINTERS ep;
MINIDUMP_EXCEPTION_INFORMATION mei; MINIDUMP_EXCEPTION_INFORMATION mei;
MINIDUMP_EXCEPTION_INFORMATION* pmei; 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) { 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; const EXCEPTION_RECORD* const er = (EXCEPTION_RECORD*)siginfo;
st->print("siginfo:"); st->print("siginfo:");
char tmp[64]; char tmp[64];
@ -2625,6 +2636,14 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
#endif #endif
#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()) { if (t != nullptr && t->is_Java_thread()) {
JavaThread* thread = JavaThread::cast(t); JavaThread* thread = JavaThread::cast(t);
bool in_java = thread->thread_state() == _thread_in_Java; 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) { void os::print_active_locale(outputStream* st) {
// not implemented yet // 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<const EXCEPTION_POINTERS*>(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;
}

View File

@ -28,6 +28,7 @@
#include "runtime/arguments.hpp" #include "runtime/arguments.hpp"
#include "runtime/javaThread.hpp" #include "runtime/javaThread.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "utilities/debug.hpp"
#include "utilities/vmError.hpp" #include "utilities/vmError.hpp"
LONG WINAPI crash_handler(struct _EXCEPTION_POINTERS* exceptionInfo) { 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::reporting_started() {}
void VMError::interrupt_reporting_thread() {} 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; DWORD flags = (exrecord == nullptr) ? FAIL_FAST_GENERATE_EXCEPTION_ADDRESS : 0;
RaiseFailFastException(static_cast<PEXCEPTION_RECORD>(exrecord), PEXCEPTION_RECORD exception_record = static_cast<PEXCEPTION_RECORD>(const_cast<void*>(exrecord));
static_cast<PCONTEXT>(context), PCONTEXT ctx = static_cast<PCONTEXT>(const_cast<void*>(context));
flags); RaiseFailFastException(exception_record, ctx, flags);
::abort(); ::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;
}

View File

@ -617,6 +617,10 @@ class os: AllStatic {
static frame fetch_frame_from_context(const void* ucVoid); static frame fetch_frame_from_context(const void* ucVoid);
static frame fetch_compiled_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 void breakpoint();
static bool start_debugging(char *buf, int buflen); 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 // Terminate with an error. Default is to generate a core file on platforms
// that support such things. This calls shutdown() and then aborts. // 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); [[noreturn]] static void abort(bool dump_core = true);
// Die immediately, no exit hook, no abort hook, no cleanup. // Die immediately, no exit hook, no abort hook, no cleanup.

View File

@ -74,8 +74,8 @@
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT #ifdef CAN_SHOW_REGISTERS_ON_ASSERT
static char g_dummy; static char g_dummy;
char* g_assert_poison = &g_dummy; char* g_assert_poison = &g_dummy;
const char* g_assert_poison_read_only = &g_dummy;
static intx g_asserting_thread = 0; static intx g_asserting_thread = 0;
static void* g_assertion_context = nullptr;
#endif // CAN_SHOW_REGISTERS_ON_ASSERT #endif // CAN_SHOW_REGISTERS_ON_ASSERT
int DebuggingContext::_enabled = 0; // Initially disabled. 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_list detail_args;
va_start(detail_args, detail_fmt); 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); 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); 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, ...) { void report_fatal(VMErrorType error_type, const char* file, int line, const char* detail_fmt, ...) {
va_list detail_args; va_list detail_args;
va_start(detail_args, detail_fmt); 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); 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, 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); file, line, 0);
va_end(detail_args); va_end(detail_args);
} }
@ -705,9 +714,6 @@ struct TestMultipleStaticAssertFormsInClassScope {
// Support for showing register content on asserts/guarantees. // Support for showing register content on asserts/guarantees.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT #ifdef CAN_SHOW_REGISTERS_ON_ASSERT
static ucontext_t g_stored_assertion_context;
void initialize_assert_poison() { void initialize_assert_poison() {
char* page = os::reserve_memory(os::vm_page_size()); char* page = os::reserve_memory(os::vm_page_size());
if (page) { if (page) {
@ -715,6 +721,7 @@ void initialize_assert_poison() {
if (os::commit_memory(page, os::vm_page_size(), false) && if (os::commit_memory(page, os::vm_page_size(), false) &&
os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) { os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) {
g_assert_poison = page; g_assert_poison = page;
g_assert_poison_read_only = page;
} }
} }
} }
@ -723,48 +730,29 @@ void disarm_assert_poison() {
g_assert_poison = &g_dummy; 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 #ifdef ASSERT
fprintf(stderr, "Assertion poison page cannot be unprotected - mprotect failed with %d (%s)", static void print_unprotect_error() {
errno, os::strerror(errno)); fprintf(stderr, "Assertion poison page cannot be unprotected - mprotect failed with %d (%s)",
fflush(stderr); errno, os::strerror(errno));
fflush(stderr);
}
#endif #endif
return false; // unprotecting memory may fail in OOM situations, as surprising as this sounds.
} // TOUCH_ASSERT_POISON writes to the protected g_assert_poison page, which faults
// Store Context away. // and enters platform signal handlers which in turn invokes this routine.
if (ucVoid) { bool handle_assert_poison_fault(const void* ucVoid) {
const intx my_tid = os::current_thread_id(); // Disarm poison page.
if (Atomic::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) { if (!os::protect_memory((char*)g_assert_poison, os::vm_page_size(), os::MEM_PROT_RWX)) {
store_context(ucVoid); DEBUG_ONLY(print_unprotect_error();)
g_assertion_context = &g_stored_assertion_context; return false; // unprotecting memory may fail in OOM situations, as surprising as this sounds.
}
}
return true;
} }
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 #endif // CAN_SHOW_REGISTERS_ON_ASSERT

View File

@ -34,14 +34,15 @@
class oopDesc; class oopDesc;
// ShowRegistersOnAssert support (for now Linux only) // ShowRegistersOnAssert support (for now Linux and Windows only)
#if defined(LINUX) && !defined(ZERO) #if (defined(LINUX) || defined(_WINDOWS)) && !defined(ZERO)
#define CAN_SHOW_REGISTERS_ON_ASSERT #define CAN_SHOW_REGISTERS_ON_ASSERT
extern char* g_assert_poison; extern char* g_assert_poison;
extern const char* g_assert_poison_read_only;
#define TOUCH_ASSERT_POISON (*g_assert_poison) = 'X'; #define TOUCH_ASSERT_POISON (*g_assert_poison) = 'X';
void initialize_assert_poison(); void initialize_assert_poison();
void disarm_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 #else
#define TOUCH_ASSERT_POISON #define TOUCH_ASSERT_POISON
#endif // CAN_SHOW_REGISTERS_ON_ASSERT #endif // CAN_SHOW_REGISTERS_ON_ASSERT

View File

@ -95,8 +95,8 @@ const char* VMError::_message;
char VMError::_detail_msg[1024]; char VMError::_detail_msg[1024];
Thread* VMError::_thread; Thread* VMError::_thread;
address VMError::_pc; address VMError::_pc;
void* VMError::_siginfo; const void* VMError::_siginfo;
void* VMError::_context; const void* VMError::_context;
bool VMError::_print_native_stack_used = false; bool VMError::_print_native_stack_used = false;
const char* VMError::_filename; const char* VMError::_filename;
int VMError::_lineno; 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."); 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; const int number_of_stack_slots = 8;
int i = continuation; int i = continuation;
@ -1583,8 +1583,8 @@ int VMError::prepare_log_file(const char* pattern, const char* default_pattern,
return fd; return fd;
} }
void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, void VMError::report_and_die(Thread* thread, unsigned int sig, address pc, const void* siginfo,
void* context, const char* detail_fmt, ...) const void* context, const char* detail_fmt, ...)
{ {
va_list detail_args; va_list detail_args;
va_start(detail_args, detail_fmt); 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); 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, ...) { const char* detail_fmt, ...) {
va_list detail_args; va_list detail_args;
va_start(detail_args, detail_fmt); 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); 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", ""); 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) 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); 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, 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) int lineno, size_t size)
{ {
// A single scratch buffer to be used from here on. // A single scratch buffer to be used from here on.

View File

@ -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. * Copyright (c) 2017, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
@ -46,9 +46,9 @@ class VMError : public AllStatic {
// additional info for crashes // additional info for crashes
static address _pc; // faulting PC static address _pc; // faulting PC
static void* _siginfo; // ExceptionRecord on Windows, static const void* _siginfo; // ExceptionRecord on Windows,
// siginfo_t on Solaris/Linux // siginfo_t on Solaris/Linux
static void* _context; // ContextRecord on Windows, static const void* _context; // ContextRecord on Windows,
// ucontext_t on Solaris/Linux // ucontext_t on Solaris/Linux
// records if VMError::print_native_stack was used to // records if VMError::print_native_stack was used to
@ -144,7 +144,7 @@ class VMError : public AllStatic {
static jlong get_step_start_time(); static jlong get_step_start_time();
static void clear_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: public:
@ -166,28 +166,28 @@ public:
// main error reporting function // main error reporting function
[[noreturn]] [[noreturn]]
ATTRIBUTE_PRINTF(6, 7) ATTRIBUTE_PRINTF(6, 7)
static void report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, static void report_and_die(Thread* thread, unsigned int sig, address pc, const void* siginfo,
void* context, const char* detail_fmt, ...); const void* context, const char* detail_fmt, ...);
[[noreturn]] [[noreturn]]
ATTRIBUTE_PRINTF(6, 7) ATTRIBUTE_PRINTF(6, 7)
static void report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, static void report_and_die(Thread* thread, const void* context, const char* filename,
const char* detail_fmt, ...); int lineno, const char* message, const char* detail_fmt, ...);
[[noreturn]] [[noreturn]]
ATTRIBUTE_PRINTF(3, 0) ATTRIBUTE_PRINTF(3, 0)
static void report_and_die(int id, const char* message, const char* detail_fmt, va_list detail_args, 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); const char* filename, int lineno, size_t size);
[[noreturn]] [[noreturn]]
static void report_and_die(Thread* thread, unsigned int sig, address pc, static void report_and_die(Thread* thread, unsigned int sig, address pc,
void* siginfo, void* context); const void* siginfo, const void* context);
[[noreturn]] [[noreturn]]
ATTRIBUTE_PRINTF(6, 0) ATTRIBUTE_PRINTF(6, 0)
static void report_and_die(Thread* thread, void* context, const char* filename, int lineno, const char* message, static void report_and_die(Thread* thread, const void* context, const char* filename,
const char* detail_fmt, va_list detail_args); int lineno, const char* message, const char* detail_fmt, va_list detail_args);
[[noreturn]] [[noreturn]]
ATTRIBUTE_PRINTF(6, 0) ATTRIBUTE_PRINTF(6, 0)
@ -225,6 +225,7 @@ public:
// permissions. // permissions.
static int prepare_log_file(const char* pattern, const char* default_pattern, bool overwrite_existing, char* buf, size_t buflen); 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 { class VMErrorCallback {

View File

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2018, 2022 SAP SE. All rights reserved. * 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -29,7 +29,7 @@
* @summary Show Registers on assert/guarantee * @summary Show Registers on assert/guarantee
* @library /test/lib * @library /test/lib
* @requires vm.flagless * @requires vm.flagless
* @requires (vm.debug == true) & (os.family == "linux") * @requires vm.debug == true & (os.family == "linux" | os.family == "windows")
* @author Thomas Stuefe (SAP) * @author Thomas Stuefe (SAP)
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* java.management * java.management
@ -67,6 +67,20 @@ public class ShowRegistersOnAssertTest {
// (which would be a sign that the assert poison page mechanism does not work). // (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("# A fatal error has been detected by the Java Runtime Environment:.*");
output_detail.shouldMatch("# +Internal Error.*"); 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 { public static void main(String[] args) throws Exception {