8296907: VMError: add optional callstacks, siginfo for secondary errors
Reviewed-by: aboldtch, rschmelter
This commit is contained in:
parent
a57392390b
commit
3b3bbe5487
@ -517,6 +517,9 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"error log in case of a crash.") \
|
||||
range(0, (uint64_t)max_jlong/1000) \
|
||||
\
|
||||
product(bool, ErrorLogSecondaryErrorDetails, false, DIAGNOSTIC, \
|
||||
"If enabled, show details on secondary crashes in the error log") \
|
||||
\
|
||||
develop(intx, TraceDwarfLevel, 0, \
|
||||
"Debug levels for the dwarf parser") \
|
||||
range(0, 4) \
|
||||
|
@ -745,7 +745,7 @@ extern "C" JNIEXPORT void pns(void* sp, void* fp, void* pc) { // print native st
|
||||
Thread* t = Thread::current_or_null();
|
||||
// Call generic frame constructor (certain arguments may be ignored)
|
||||
frame fr(sp, fp, pc);
|
||||
VMError::print_native_stack(tty, fr, t, false, buf, sizeof(buf));
|
||||
VMError::print_native_stack(tty, fr, t, false, -1, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
//
|
||||
@ -765,7 +765,7 @@ extern "C" JNIEXPORT void pns2() { // print native stack
|
||||
} else {
|
||||
Thread* t = Thread::current_or_null();
|
||||
frame fr = os::current_frame();
|
||||
VMError::print_native_stack(tty, fr, t, false, buf, sizeof(buf));
|
||||
VMError::print_native_stack(tty, fr, t, false, -1, buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -347,14 +347,14 @@ static frame next_frame(frame fr, Thread* t) {
|
||||
}
|
||||
}
|
||||
|
||||
void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, bool print_source_info, char* buf, int buf_size) {
|
||||
void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, bool print_source_info, int max_frames, char* buf, int buf_size) {
|
||||
|
||||
// see if it's a valid frame
|
||||
if (fr.pc()) {
|
||||
st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)");
|
||||
|
||||
const int limit = max_frames == -1 ? StackPrintLimit : MIN2(max_frames, (int)StackPrintLimit);
|
||||
int count = 0;
|
||||
while (count++ < StackPrintLimit) {
|
||||
while (count++ < limit) {
|
||||
fr.print_on_error(st, buf, buf_size);
|
||||
if (fr.pc()) { // print source file and line, if available
|
||||
char filename[128];
|
||||
@ -374,11 +374,10 @@ void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, bool pri
|
||||
}
|
||||
}
|
||||
|
||||
if (count > StackPrintLimit) {
|
||||
if (count > limit) {
|
||||
st->print_cr("...<more frames>...");
|
||||
}
|
||||
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
|
||||
@ -818,7 +817,7 @@ void VMError::report(outputStream* st, bool _verbose) {
|
||||
frame fr = _context ? os::fetch_frame_from_context(_context)
|
||||
: os::current_frame();
|
||||
|
||||
print_native_stack(st, fr, _thread, true, buf, sizeof(buf));
|
||||
print_native_stack(st, fr, _thread, true, -1, buf, sizeof(buf));
|
||||
_print_native_stack_used = true;
|
||||
}
|
||||
print_native_stack_succeeded = true;
|
||||
@ -827,7 +826,7 @@ void VMError::report(outputStream* st, bool _verbose) {
|
||||
st->cr();
|
||||
st->print_cr("Retrying call stack printing without source information...");
|
||||
frame fr = _context ? os::fetch_frame_from_context(_context) : os::current_frame();
|
||||
print_native_stack(st, fr, _thread, false, buf, sizeof(buf));
|
||||
print_native_stack(st, fr, _thread, false, -1, buf, sizeof(buf));
|
||||
_print_native_stack_used = true;
|
||||
|
||||
STEP_IF("printing Java stack", _verbose && _thread && _thread->is_Java_thread())
|
||||
@ -1501,27 +1500,49 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt
|
||||
// Watcherthread is about to call os::die. Lets just wait.
|
||||
os::infinite_sleep();
|
||||
} else {
|
||||
// Crash or assert during error reporting. Lets continue reporting with the next step.
|
||||
stringStream ss(buffer, sizeof(buffer));
|
||||
// A secondary error happened. Print brief information, but take care, since crashing
|
||||
// here would just recurse endlessly.
|
||||
// Any information (signal, context, siginfo etc) printed here should use the function
|
||||
// arguments, not the information stored in *this, since those describe the primary crash.
|
||||
static char tmp[256]; // cannot use global scratch buffer
|
||||
// Note: this string does get parsed by a number of jtreg tests,
|
||||
// see hotspot/jtreg/runtime/ErrorHandling.
|
||||
ss.print("[error occurred during error reporting (%s), id 0x%x",
|
||||
st->print("[error occurred during error reporting (%s), id 0x%x",
|
||||
_current_step_info, id);
|
||||
char signal_name[64];
|
||||
if (os::exception_name(id, signal_name, sizeof(signal_name))) {
|
||||
ss.print(", %s (0x%x) at pc=" PTR_FORMAT, signal_name, id, p2i(pc));
|
||||
if (os::exception_name(id, tmp, sizeof(tmp))) {
|
||||
st->print(", %s (0x%x) at pc=" PTR_FORMAT, tmp, id, p2i(pc));
|
||||
} else {
|
||||
if (should_report_bug(id)) {
|
||||
ss.print(", Internal Error (%s:%d)",
|
||||
st->print(", Internal Error (%s:%d)",
|
||||
filename == NULL ? "??" : filename, lineno);
|
||||
} else {
|
||||
ss.print(", Out of Memory Error (%s:%d)",
|
||||
st->print(", Out of Memory Error (%s:%d)",
|
||||
filename == NULL ? "??" : filename, lineno);
|
||||
}
|
||||
}
|
||||
ss.print("]");
|
||||
st->print_raw_cr(buffer);
|
||||
st->cr();
|
||||
st->print_cr("]");
|
||||
if (ErrorLogSecondaryErrorDetails) {
|
||||
static bool recursed = false;
|
||||
if (!recursed) {
|
||||
recursed = true;
|
||||
// Print even more information for secondary errors. This may generate a lot of output
|
||||
// and possibly disturb error reporting, therefore its optional and only available in debug builds.
|
||||
if (siginfo != nullptr) {
|
||||
st->print("[");
|
||||
os::print_siginfo(st, siginfo);
|
||||
st->print_cr("]");
|
||||
}
|
||||
st->print("[stack: ");
|
||||
frame fr = context ? os::fetch_frame_from_context(context) : os::current_frame();
|
||||
// Subsequent secondary errors build up stack; to avoid flooding the hs-err file with irrelevant
|
||||
// call stacks, limit the stack we print here (we are only interested in what happened before the
|
||||
// last assert/fault).
|
||||
const int max_stack_size = 15;
|
||||
print_native_stack(st, fr, _thread, true, max_stack_size, tmp, sizeof(tmp));
|
||||
st->print_cr("]");
|
||||
} // !recursed
|
||||
recursed = false; // Note: reset outside !recursed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,8 +105,11 @@ class VMError : public AllStatic {
|
||||
|
||||
// public for use by the internal non-product debugger.
|
||||
NOT_PRODUCT(public:)
|
||||
// print_source_info: if true, we try to resolve the source information on platforms that support it
|
||||
// (useful but may slow down, timeout or misfunction in error situations)
|
||||
// max_frames: if not -1, overrides StackPrintLimit
|
||||
static void print_native_stack(outputStream* st, frame fr, Thread* t, bool print_source_info,
|
||||
char* buf, int buf_size);
|
||||
int max_frames, char* buf, int buf_size);
|
||||
NOT_PRODUCT(private:)
|
||||
|
||||
static const char* get_filename_only() {
|
||||
|
@ -25,21 +25,28 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8065895
|
||||
* @summary Synchronous signals during error reporting may terminate or hang VM process
|
||||
* @summary Check secondary error handling
|
||||
* @library /test/lib
|
||||
* @requires vm.debug
|
||||
* @requires os.family != "windows"
|
||||
* @author Thomas Stuefe (SAP)
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver SecondaryErrorTest
|
||||
* @run driver SecondaryErrorTest no_callstacks
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Check secondary error handling
|
||||
* @library /test/lib
|
||||
* @requires vm.debug
|
||||
* @requires os.family != "windows"
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver SecondaryErrorTest with_callstacks
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
@ -49,12 +56,35 @@ public class SecondaryErrorTest {
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
boolean with_callstacks = false;
|
||||
if (args.length != 1) {
|
||||
throw new IllegalArgumentException("Missing argument");
|
||||
} else if (args[0].equals("with_callstacks")) {
|
||||
with_callstacks = true;
|
||||
} else if (args[0].equals("no_callstacks")) {
|
||||
with_callstacks = false;
|
||||
} else {
|
||||
throw new IllegalArgumentException("unknown argument (" + args[0] + ")");
|
||||
}
|
||||
|
||||
// How this works:
|
||||
// The test will fault with SIGFPE (ErrorHandlerTest=15) and then, during error handling,
|
||||
// fault twice with SIGSEGV (TestCrashInErrorHandler=14). The point is not only to test
|
||||
// secondary crashes, but secondary crashes with a *different* error signal. This should
|
||||
// be handled correctly and not hang/end the process (so the signal mask must be set correctly).
|
||||
// See JDK-8065895.
|
||||
// We do this twice, to check that secondary signal handling works repeatedly.
|
||||
// We also check, optionally, that +ErrorLogSecondaryErrorDetails produces callstacks for
|
||||
// the secondary error.
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-Xmx100M",
|
||||
"-XX:-CreateCoredumpOnCrash",
|
||||
"-XX:ErrorHandlerTest=15",
|
||||
"-XX:TestCrashInErrorHandler=14",
|
||||
"-XX:" + (with_callstacks ? "+" : "-") + "ErrorLogSecondaryErrorDetails",
|
||||
"-version");
|
||||
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
@ -72,12 +102,23 @@ public class SecondaryErrorTest {
|
||||
// which is an end marker written in the last step and proves that hs-err file was
|
||||
// completely written.
|
||||
|
||||
Pattern [] pattern = new Pattern[] {
|
||||
Pattern.compile("Will crash now \\(TestCrashInErrorHandler=14\\)..."),
|
||||
Pattern.compile("\\[error occurred during error reporting \\(test secondary crash 1\\).*\\]"),
|
||||
Pattern.compile("Will crash now \\(TestCrashInErrorHandler=14\\)..."),
|
||||
Pattern.compile("\\[error occurred during error reporting \\(test secondary crash 2\\).*\\]"),
|
||||
};
|
||||
ArrayList<Pattern> patternlist = new ArrayList<>();
|
||||
patternlist.add(Pattern.compile("Will crash now \\(TestCrashInErrorHandler=14\\)..."));
|
||||
patternlist.add(Pattern.compile("\\[error occurred during error reporting \\(test secondary crash 1\\).*\\]"));
|
||||
if (with_callstacks) {
|
||||
patternlist.add(Pattern.compile("\\[siginfo:.*\\(SIGSEGV\\).*\\]"));
|
||||
patternlist.add(Pattern.compile("\\[stack: Native frames:.*"));
|
||||
patternlist.add(Pattern.compile(".*VMError::controlled_crash.*"));
|
||||
}
|
||||
// and again, to see that repeated error reporting steps work
|
||||
patternlist.add(Pattern.compile("Will crash now \\(TestCrashInErrorHandler=14\\)..."));
|
||||
patternlist.add(Pattern.compile("\\[error occurred during error reporting \\(test secondary crash 2\\).*\\]"));
|
||||
if (with_callstacks) {
|
||||
patternlist.add(Pattern.compile("\\[siginfo:.*\\(SIGSEGV\\).*\\]"));
|
||||
patternlist.add(Pattern.compile("\\[stack: Native frames:.*"));
|
||||
patternlist.add(Pattern.compile(".*VMError::controlled_crash.*"));
|
||||
}
|
||||
Pattern[] pattern = patternlist.toArray(new Pattern[] {});
|
||||
|
||||
HsErrFileUtils.checkHsErrFileContent(hs_err_file, pattern, false);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user