8191101: Show register content in hs-err file on assert

Reviewed-by: adinn, clanger, simonis
This commit is contained in:
Thomas Stuefe 2018-03-30 06:31:16 +02:00
parent 213862d866
commit 3e603a776e
15 changed files with 272 additions and 22 deletions

View file

@ -27,6 +27,7 @@
#include "runtime/arguments.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/vmError.hpp"
#include <sys/types.h>
@ -122,11 +123,20 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
pc = (address) info->si_addr;
}
// Needed to make it possible to call SafeFetch.. APIs in error handling.
if (uc && pc && StubRoutines::is_safefetch_fault(pc)) {
os::Posix::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc));
return;
}
// Needed because asserts may happen in error handling too.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return;
}
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
VMError::report_and_die(NULL, sig, pc, info, ucVoid);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -50,6 +50,7 @@
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/timer.hpp"
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
#ifdef BUILTIN_SIM
@ -306,6 +307,13 @@ JVM_handle_linux_signal(int sig,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, 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
@ -47,6 +47,7 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/timer.hpp"
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
@ -311,6 +312,13 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -51,6 +51,7 @@
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/timer.hpp"
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
@ -266,6 +267,13 @@ JVM_handle_linux_signal(int sig,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -54,6 +54,7 @@
#include "runtime/thread.inline.hpp"
#include "runtime/timer.hpp"
#include "utilities/events.hpp"
#include "utilities/debug.hpp"
#include "utilities/vmError.hpp"
// put OS-includes here
@ -270,6 +271,13 @@ JVM_handle_linux_signal(int sig,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2018, 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
@ -49,6 +49,7 @@
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/timer.hpp"
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
@ -513,6 +514,13 @@ JVM_handle_linux_signal(int sig,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -51,6 +51,7 @@
#include "runtime/timer.hpp"
#include "services/memTracker.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
@ -304,6 +305,13 @@ JVM_handle_linux_signal(int sig,
}
}
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) {
handle_assert_poison_fault(ucVoid, info->si_addr);
return 1;
}
#endif
JavaThread* thread = NULL;
VMThread* vmthread = NULL;
if (os::Linux::signal_handlers_are_installed) {

View file

@ -3412,6 +3412,10 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) {
}
#endif
#ifndef CAN_SHOW_REGISTERS_ON_ASSERT
UNSUPPORTED_OPTION(ShowRegistersOnAssert);
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
return JNI_OK;
}

View file

@ -4062,6 +4062,9 @@ public:
develop(bool, VerifyMetaspace, false, \
"Verify metaspace on chunk movements.") \
\
diagnostic(bool, ShowRegistersOnAssert, false, \
"On internal errors, include registers in error report.") \
\

View file

@ -3661,6 +3661,13 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// Timing (must come after argument parsing)
TraceTime timer("Create VM", TRACETIME_LOG(Info, startuptime));
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
// Initialize assert poison page mechanism.
if (ShowRegistersOnAssert) {
initialize_assert_poison();
}
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
// Initialize the os module after parsing the args
jint os_init_2_result = os::init_2();
if (os_init_2_result != JNI_OK) return os_init_2_result;

View file

@ -54,11 +54,20 @@
#include "utilities/defaultStream.hpp"
#include "utilities/events.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
#include "utilities/vmError.hpp"
#include <stdio.h>
// Support for showing register content on asserts/guarantees.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
static char g_dummy;
char* g_assert_poison = &g_dummy;
static intx g_asserting_thread = 0;
static void* g_assertion_context = NULL;
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
#ifndef ASSERT
# ifdef _DEBUG
// NOTE: don't turn the lines below into a comment -- if you're getting
@ -212,7 +221,13 @@ void report_vm_error(const char* file, int line, const char* error_msg, const ch
if (Debugging || error_is_suppressed(file, line)) return;
va_list detail_args;
va_start(detail_args, detail_fmt);
VMError::report_and_die(Thread::current_or_null(), file, line, error_msg, detail_fmt, detail_args);
void* context = NULL;
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if (g_assertion_context != NULL && os::current_thread_id() == g_asserting_thread) {
context = g_assertion_context;
}
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
VMError::report_and_die(Thread::current_or_null(), context, file, line, error_msg, detail_fmt, detail_args);
va_end(detail_args);
}
@ -226,7 +241,13 @@ void report_fatal(const char* file, int line, const char* detail_fmt, ...)
if (Debugging || error_is_suppressed(file, line)) return;
va_list detail_args;
va_start(detail_args, detail_fmt);
VMError::report_and_die(Thread::current_or_null(), file, line, "fatal error", detail_fmt, detail_args);
void* context = NULL;
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
if (g_assertion_context != NULL && os::current_thread_id() == g_asserting_thread) {
context = g_assertion_context;
}
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
VMError::report_and_die(Thread::current_or_null(), context, file, line, "fatal error", detail_fmt, detail_args);
va_end(detail_args);
}
@ -676,3 +697,50 @@ struct TestMultipleStaticAssertFormsInClassScope {
};
#endif // !PRODUCT
// 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) {
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;
}
}
}
static bool store_context(const void* context) {
if (memcpy(&g_stored_assertion_context, context, sizeof(ucontext_t)) == false) {
return false;
}
#if defined(__linux) && defined(PPC64)
// 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):
*((void**) &g_stored_assertion_context.uc_mcontext.regs) = &(g_stored_assertion_context.uc_mcontext.gp_regs);
#endif
return true;
}
bool handle_assert_poison_fault(const void* ucVoid, const void* faulting_address) {
if (faulting_address == g_assert_poison) {
// Disarm poison page.
os::protect_memory((char*)g_assert_poison, os::vm_page_size(), os::MEM_PROT_RWX);
// Store Context away.
if (ucVoid) {
const jlong my_tid = os::current_thread_id();
if (Atomic::cmpxchg(my_tid, &g_asserting_thread, (intx)0) == 0) {
if (store_context(ucVoid)) {
g_assertion_context = &g_stored_assertion_context;
}
}
}
return true;
}
return false;
}
#endif // CAN_SHOW_REGISTERS_ON_ASSERT

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2018, 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
@ -31,6 +31,17 @@
#include <stddef.h>
// ShowRegistersOnAssert support (for now Linux only)
#if defined(LINUX) && !defined(ZERO)
#define CAN_SHOW_REGISTERS_ON_ASSERT
extern char* g_assert_poison;
#define TOUCH_ASSERT_POISON (*g_assert_poison) = 'X';
void initialize_assert_poison();
bool handle_assert_poison_fault(const void* ucVoid, const void* faulting_address);
#else
#define TOUCH_ASSERT_POISON
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
// assertions
#ifndef ASSERT
#define vmassert(p, ...)
@ -42,6 +53,7 @@
#define vmassert(p, ...) \
do { \
if (!(p)) { \
TOUCH_ASSERT_POISON; \
if (is_executing_unit_tests()) { \
report_assert_msg(__VA_ARGS__); \
} \
@ -67,6 +79,7 @@ do { \
#define vmassert_status(p, status, msg) \
do { \
if (!(p)) { \
TOUCH_ASSERT_POISON; \
report_vm_status_error(__FILE__, __LINE__, "assert(" #p ") failed", \
status, msg); \
BREAKPOINT; \
@ -83,6 +96,7 @@ do { \
#define guarantee(p, ...) \
do { \
if (!(p)) { \
TOUCH_ASSERT_POISON; \
report_vm_error(__FILE__, __LINE__, "guarantee(" #p ") failed", __VA_ARGS__); \
BREAKPOINT; \
} \
@ -90,6 +104,7 @@ do {
#define fatal(...) \
do { \
TOUCH_ASSERT_POISON; \
report_fatal(__FILE__, __LINE__, __VA_ARGS__); \
BREAKPOINT; \
} while (0)
@ -103,18 +118,21 @@ do {
#define ShouldNotCallThis() \
do { \
TOUCH_ASSERT_POISON; \
report_should_not_call(__FILE__, __LINE__); \
BREAKPOINT; \
} while (0)
#define ShouldNotReachHere() \
do { \
TOUCH_ASSERT_POISON; \
report_should_not_reach_here(__FILE__, __LINE__); \
BREAKPOINT; \
} while (0)
#define Unimplemented() \
do { \
TOUCH_ASSERT_POISON; \
report_unimplemented(__FILE__, __LINE__); \
BREAKPOINT; \
} while (0)

View file

@ -1238,10 +1238,10 @@ void VMError::report_and_die(const char* message)
report_and_die(message, "%s", "");
}
void VMError::report_and_die(Thread* thread, const char* filename, int lineno, const char* message,
void VMError::report_and_die(Thread* thread, 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, NULL, NULL, NULL, filename, lineno, 0);
report_and_die(INTERNAL_ERROR, message, detail_fmt, detail_args, thread, NULL, NULL, context, filename, lineno, 0);
}
void VMError::report_and_die(Thread* thread, const char* filename, int lineno, size_t size,
@ -1674,24 +1674,24 @@ void VMError::controlled_crash(int how) {
// Case 16 is tested by test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java.
// Case 17 is tested by test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java.
switch (how) {
case 1: vmassert(str == NULL, "expected null");
case 1: vmassert(str == NULL, "expected null"); break;
case 2: vmassert(num == 1023 && *str == 'X',
"num=" SIZE_FORMAT " str=\"%s\"", num, str);
case 3: guarantee(str == NULL, "expected null");
"num=" SIZE_FORMAT " str=\"%s\"", num, str); break;
case 3: guarantee(str == NULL, "expected null"); break;
case 4: guarantee(num == 1023 && *str == 'X',
"num=" SIZE_FORMAT " str=\"%s\"", num, str);
case 5: fatal("expected null");
case 6: fatal("num=" SIZE_FORMAT " str=\"%s\"", num, str);
"num=" SIZE_FORMAT " str=\"%s\"", num, str); break;
case 5: fatal("expected null"); break;
case 6: fatal("num=" SIZE_FORMAT " str=\"%s\"", num, str); break;
case 7: fatal("%s%s# %s%s# %s%s# %s%s# %s%s# "
"%s%s# %s%s# %s%s# %s%s# %s%s# "
"%s%s# %s%s# %s%s# %s%s# %s",
msg, eol, msg, eol, msg, eol, msg, eol, msg, eol,
msg, eol, msg, eol, msg, eol, msg, eol, msg, eol,
msg, eol, msg, eol, msg, eol, msg, eol, msg);
case 8: vm_exit_out_of_memory(num, OOM_MALLOC_ERROR, "ChunkPool::allocate");
case 9: ShouldNotCallThis();
case 10: ShouldNotReachHere();
case 11: Unimplemented();
msg, eol, msg, eol, msg, eol, msg, eol, msg); break;
case 8: vm_exit_out_of_memory(num, OOM_MALLOC_ERROR, "ChunkPool::allocate"); break;
case 9: ShouldNotCallThis(); break;
case 10: ShouldNotReachHere(); break;
case 11: Unimplemented(); break;
// There's no guarantee the bad data pointer will crash us
// so "break" out to the ShouldNotReachHere().
case 12: *dataPtr = '\0'; break;
@ -1714,6 +1714,7 @@ void VMError::controlled_crash(int how) {
default: tty->print_cr("ERROR: %d: unexpected test_num value.", how);
}
tty->print_cr("VMError::controlled_crash: survived intentional crash. Did you suppress the assert?");
ShouldNotReachHere();
}
#endif // !PRODUCT

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2018, 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
@ -158,8 +158,8 @@ public:
static void report_and_die(Thread* thread, unsigned int sig, address pc,
void* siginfo, void* context);
static void report_and_die(Thread* thread,const char* filename, int lineno, const char* message,
const char* detail_fmt, va_list detail_args) ATTRIBUTE_PRINTF(5, 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) ATTRIBUTE_PRINTF(6, 0);
static void report_and_die(Thread* thread, const char* filename, int lineno, size_t size,
VMErrorType vm_err_type, const char* detail_fmt,

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2018, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191101
* @summary Show Registers on assert/guarantee
* @library /test/lib
* @requires (vm.debug == true) & (os.family == "linux")
* @author Thomas Stuefe (SAP)
* @modules java.base/jdk.internal.misc
* java.management
*/
// Note: this test can only run on debug since it relies on VMError::controlled_crash() which
// only exists in debug builds.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools;
public class ShowRegistersOnAssertTest {
private static void do_test(boolean do_assert, // true - assert, false - guarantee
boolean suppress_assert,
boolean show_registers_on_assert) throws Exception
{
System.out.println("Testing " + (suppress_assert ? "suppressed" : "normal") + " " + (do_assert ? "assert" : "guarantee") +
" with " + (show_registers_on_assert ? "-XX:+ShowRegistersOnAssert" : "-XX:-ShowRegistersOnAssert") + "...");
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions", "-Xmx100M", "-XX:-CreateCoredumpOnCrash",
"-XX:ErrorHandlerTest=" + (do_assert ? "1" : "3"),
(suppress_assert ? "-XX:SuppressErrorAt=/vmError.cpp" : ""),
(show_registers_on_assert ? "-XX:+ShowRegistersOnAssert" : "-XX:-ShowRegistersOnAssert"),
"-version");
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
if (suppress_assert) {
// we should have not have crashed. See VMError::controlled_crash().
output_detail.shouldMatch(".*survived intentional crash.*");
} else {
// we should have crashed with an internal error. We should definitly NOT have crashed with a segfault
// (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.*");
}
}
public static void main(String[] args) throws Exception {
// Note: for now, this is only a regression test testing that the addition of ShowRegistersOnAssert does
// not break normal assert/guarantee handling. The feature is not implemented on all platforms and really testing
// it requires more effort.
do_test(false, false, false);
do_test(false, false, true);
do_test(false, true, false);
do_test(false, true, true);
do_test(true, false, false);
do_test(true, false, true);
do_test(true, true, false);
do_test(true, true, true);
}
}