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
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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:");
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user