8343756: CAN_SHOW_REGISTERS_ON_ASSERT for Windows
Reviewed-by: stuefe, jsjolen
This commit is contained in:
parent
3a625f38aa
commit
0054bbed7f
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
@ -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<PEXCEPTION_RECORD>(exrecord),
|
||||
static_cast<PCONTEXT>(context),
|
||||
flags);
|
||||
PEXCEPTION_RECORD exception_record = static_cast<PEXCEPTION_RECORD>(const_cast<void*>(exrecord));
|
||||
PCONTEXT ctx = static_cast<PCONTEXT>(const_cast<void*>(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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
// 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.
|
||||
}
|
||||
// Store Context away.
|
||||
if (ucVoid) {
|
||||
if (ucVoid != nullptr) {
|
||||
// Save context.
|
||||
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;
|
||||
os::save_assert_context(ucVoid);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user