Mercurial > hg > jdk9-shenandoah > hotspot
changeset 8717:3eb61269f421
8074552: SafeFetch32 and SafeFetchN do not work in error handling
Reviewed-by: dholmes, goetz
Contributed-by: Thomas Stufe <thomas.stuefe@gmail.com>
line wrap: on
line diff
--- a/src/os/aix/vm/vmError_aix.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/aix/vm/vmError_aix.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -109,7 +109,15 @@ } sigthreadmask(SIG_UNBLOCK, &newset, NULL); - VMError err(NULL, sig, NULL, info, ucVoid); + // support safefetch faults in error handling + ucontext_t* const uc = (ucontext_t*) ucVoid; + address const pc = uc ? os::Aix::ucontext_get_pc(uc) : NULL; + if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { + os::Aix::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + return; + } + + VMError err(NULL, sig, pc, info, ucVoid); err.report_and_die(); }
--- a/src/os/bsd/vm/os_bsd.hpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/bsd/vm/os_bsd.hpp Thu Mar 12 19:34:50 2015 -0400 @@ -99,6 +99,7 @@ static void set_page_size(int val) { _page_size = val; } static address ucontext_get_pc(ucontext_t* uc); + static void ucontext_set_pc(ucontext_t* uc, address pc); static intptr_t* ucontext_get_sp(ucontext_t* uc); static intptr_t* ucontext_get_fp(ucontext_t* uc);
--- a/src/os/bsd/vm/vmError_bsd.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/bsd/vm/vmError_bsd.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -112,7 +112,16 @@ } pthread_sigmask(SIG_UNBLOCK, &newset, NULL); - VMError err(NULL, sig, NULL, info, ucVoid); + // support safefetch faults in error handling + ucontext_t* const uc = (ucontext_t*) ucVoid; + address const pc = uc ? os::Bsd::ucontext_get_pc(uc) : NULL; + + if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { + os::Bsd::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + return; + } + + VMError err(NULL, sig, pc, info, ucVoid); err.report_and_die(); }
--- a/src/os/linux/vm/os_linux.hpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/linux/vm/os_linux.hpp Thu Mar 12 19:34:50 2015 -0400 @@ -143,6 +143,7 @@ static int vm_default_page_size(void) { return _vm_default_page_size; } static address ucontext_get_pc(ucontext_t* uc); + static void ucontext_set_pc(ucontext_t* uc, address pc); static intptr_t* ucontext_get_sp(ucontext_t* uc); static intptr_t* ucontext_get_fp(ucontext_t* uc);
--- a/src/os/linux/vm/vmError_linux.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/linux/vm/vmError_linux.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -112,7 +112,16 @@ } pthread_sigmask(SIG_UNBLOCK, &newset, NULL); - VMError err(NULL, sig, NULL, info, ucVoid); + // support safefetch faults in error handling + ucontext_t* const uc = (ucontext_t*) ucVoid; + address const pc = uc ? os::Linux::ucontext_get_pc(uc) : NULL; + + if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { + os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + return; + } + + VMError err(NULL, sig, pc, info, ucVoid); err.report_and_die(); }
--- a/src/os/solaris/vm/os_solaris.hpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/solaris/vm/os_solaris.hpp Thu Mar 12 19:34:50 2015 -0400 @@ -137,6 +137,7 @@ // ucontext_get_fp() is only used by Solaris X86 (see note below) static intptr_t* ucontext_get_fp(ucontext_t* uc); static address ucontext_get_pc(ucontext_t* uc); + static void ucontext_set_pc(ucontext_t* uc, address pc); // For Analyzer Forte AsyncGetCallTrace profiling support: // Parameter ret_fp is only used by Solaris X86.
--- a/src/os/solaris/vm/vmError_solaris.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os/solaris/vm/vmError_solaris.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -109,7 +109,15 @@ } thr_sigsetmask(SIG_UNBLOCK, &newset, NULL); - VMError err(NULL, sig, NULL, info, ucVoid); + // support safefetch faults in error handling + ucontext_t* const uc = (ucontext_t*) ucVoid; + address const pc = uc ? os::Solaris::ucontext_get_pc(uc) : NULL; + if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { + os::Solaris::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + return; + } + + VMError err(NULL, sig, pc, info, ucVoid); err.report_and_die(); }
--- a/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -397,7 +397,7 @@ // continue at the next instruction after the faulting read. Returning // garbage from this read is ok. thread->set_pending_unsafe_access_error(); - uc->uc_mcontext.jmp_context.iar = ((unsigned long)pc) + 4; + os::Aix::ucontext_set_pc(uc, pc + 4); return 1; } } @@ -420,7 +420,7 @@ // continue at the next instruction after the faulting read. Returning // garbage from this read is ok. thread->set_pending_unsafe_access_error(); - uc->uc_mcontext.jmp_context.iar = ((unsigned long)pc) + 4; + os::Aix::ucontext_set_pc(uc, pc + 4); return 1; } } @@ -445,7 +445,7 @@ if (stub != NULL) { // Save all thread context in case we need to restore it. if (thread != NULL) thread->set_saved_exception_pc(pc); - uc->uc_mcontext.jmp_context.iar = (unsigned long)stub; + os::Aix::ucontext_set_pc(uc, stub); return 1; }
--- a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -309,6 +309,10 @@ return (address)uc->context_pc; } +void os::Bsd::ucontext_set_pc(ucontext_t * uc, address pc) { + uc->context_pc = (intptr_t)pc ; +} + intptr_t* os::Bsd::ucontext_get_sp(ucontext_t * uc) { return (intptr_t*)uc->context_sp; } @@ -463,7 +467,7 @@ pc = (address) os::Bsd::ucontext_get_pc(uc); if (StubRoutines::is_safefetch_fault(pc)) { - uc->context_pc = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + os::Bsd::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return 1; } @@ -703,7 +707,7 @@ // save all thread context in case we need to restore it if (thread != NULL) thread->set_saved_exception_pc(pc); - uc->context_pc = (intptr_t)stub; + os::Bsd::ucontext_set_pc(uc, stub); return true; }
--- a/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -107,6 +107,10 @@ return NULL; } +void os::Bsd::ucontext_set_pc(ucontext_t * uc, address pc) { + ShouldNotCallThis(); +} + ExtendedPC os::fetch_frame_from_context(void* ucVoid, intptr_t** ret_sp, intptr_t** ret_fp) {
--- a/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -113,6 +113,14 @@ return (address)uc->uc_mcontext.regs->nip; } +// modify PC in ucontext. +// Note: Only use this for an ucontext handed down to a signal handler. See comment +// in ucontext_get_pc. +void os::Linux::ucontext_set_pc(ucontext_t * uc, address pc) { + guarantee(uc->uc_mcontext.regs != NULL, "only use ucontext_set_pc in sigaction context"); + uc->uc_mcontext.regs->nip = (unsigned long)pc; +} + intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { return (intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/]; } @@ -213,7 +221,7 @@ if (uc) { address const pc = os::Linux::ucontext_get_pc(uc); if (pc && StubRoutines::is_safefetch_fault(pc)) { - uc->uc_mcontext.regs->nip = (unsigned long)StubRoutines::continuation_for_safefetch_fault(pc); + os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return true; } } @@ -360,7 +368,7 @@ // continue at the next instruction after the faulting read. Returning // garbage from this read is ok. thread->set_pending_unsafe_access_error(); - uc->uc_mcontext.regs->nip = ((unsigned long)pc) + 4; + os::Linux::ucontext_set_pc(uc, pc + 4); return true; } } @@ -379,7 +387,7 @@ // continue at the next instruction after the faulting read. Returning // garbage from this read is ok. thread->set_pending_unsafe_access_error(); - uc->uc_mcontext.regs->nip = ((unsigned long)pc) + 4; + os::Linux::ucontext_set_pc(uc, pc + 4); return true; } } @@ -402,7 +410,7 @@ if (stub != NULL) { // Save all thread context in case we need to restore it. if (thread != NULL) thread->set_saved_exception_pc(pc); - uc->uc_mcontext.regs->nip = (unsigned long)stub; + os::Linux::ucontext_set_pc(uc, stub); return true; }
--- a/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -85,11 +85,6 @@ CON_O7, }; -static inline void set_cont_address(sigcontext* ctx, address addr) { - SIG_PC(ctx) = (intptr_t)addr; - SIG_NPC(ctx) = (intptr_t)(addr+4); -} - // For Forte Analyzer AsyncGetCallTrace profiling support - thread is // currently interrupted by SIGPROF. // os::Solaris::fetch_frame_from_ucontext() tries to skip nested @@ -351,6 +346,12 @@ return (address) SIG_PC((sigcontext*)uc); } +void os::Linux::ucontext_set_pc(ucontext_t* uc, address pc) { + sigcontext_t* ctx = (sigcontext_t*) uc; + SIG_PC(ctx) = (intptr_t)addr; + SIG_NPC(ctx) = (intptr_t)(addr+4); +} + intptr_t* os::Linux::ucontext_get_sp(ucontext_t *uc) { return (intptr_t*) ((intptr_t)SIG_REGS((sigcontext*)uc).u_regs[CON_O6] + STACK_BIAS); @@ -366,7 +367,7 @@ inline static bool checkPrefetch(sigcontext* uc, address pc) { if (StubRoutines::is_safefetch_fault(pc)) { - set_cont_address(uc, address(StubRoutines::continuation_for_safefetch_fault(pc))); + os::Linux::ucontext_set_pc((ucontext_t*)uc, StubRoutines::continuation_for_safefetch_fault(pc)); return true; } return false; @@ -666,7 +667,7 @@ // save all thread context in case we need to restore it thread->set_saved_exception_pc(pc); thread->set_saved_exception_npc(npc); - set_cont_address(uc, stub); + os::Linux::ucontext_set_pc((ucontext_t*)uc, stub); return true; } }
--- a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -122,6 +122,10 @@ return (address)uc->uc_mcontext.gregs[REG_PC]; } +void os::Linux::ucontext_set_pc(ucontext_t * uc, address pc) { + uc->uc_mcontext.gregs[REG_PC] = (intptr_t)pc; +} + intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { return (intptr_t*)uc->uc_mcontext.gregs[REG_SP]; } @@ -279,7 +283,7 @@ pc = (address) os::Linux::ucontext_get_pc(uc); if (StubRoutines::is_safefetch_fault(pc)) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return 1; } @@ -514,7 +518,7 @@ // save all thread context in case we need to restore it if (thread != NULL) thread->set_saved_exception_pc(pc); - uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub; + os::Linux::ucontext_set_pc(uc, stub); return true; }
--- a/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -100,6 +100,10 @@ ShouldNotCallThis(); } +void os::Linux::ucontext_set_pc(ucontext_t * uc, address pc) { + ShouldNotCallThis(); +} + ExtendedPC os::fetch_frame_from_context(void* ucVoid, intptr_t** ret_sp, intptr_t** ret_fp) {
--- a/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -184,6 +184,11 @@ return ExtendedPC(pc); } +void os::Solaris::ucontext_set_pc(ucontext_t* uc, address pc) { + uc->uc_mcontext.gregs [REG_PC] = (greg_t) pc; + uc->uc_mcontext.gregs [REG_nPC] = (greg_t) (pc + 4); +} + // Assumes ucontext is valid intptr_t* os::Solaris::ucontext_get_sp(ucontext_t *uc) { return (intptr_t*)((intptr_t)uc->uc_mcontext.gregs[REG_SP] + STACK_BIAS); @@ -355,8 +360,7 @@ // SafeFetch() support if (StubRoutines::is_safefetch_fault(pc)) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); - uc->uc_mcontext.gregs[REG_nPC] = uc->uc_mcontext.gregs[REG_PC] + 4; + os::Solaris::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return 1; } @@ -494,8 +498,7 @@ // simulate a branch to the stub (a "call" in the safepoint stub case) // factor me: setPC - uc->uc_mcontext.gregs[REG_PC ] = (greg_t)stub; - uc->uc_mcontext.gregs[REG_nPC] = (greg_t)(stub + 4); + os::Solaris::ucontext_set_pc(uc, stub); #ifndef PRODUCT if (TraceJumps) thread->record_jump(stub, NULL, __FILE__, __LINE__);
--- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -174,6 +174,10 @@ return ExtendedPC((address)uc->uc_mcontext.gregs[REG_PC]); } +void os::Solaris::ucontext_set_pc(ucontext_t* uc, address pc) { + uc->uc_mcontext.gregs [REG_PC] = (greg_t) pc; +} + // Assumes ucontext is valid intptr_t* os::Solaris::ucontext_get_sp(ucontext_t *uc) { return (intptr_t*)uc->uc_mcontext.gregs[REG_SP]; @@ -411,7 +415,7 @@ pc = (address) uc->uc_mcontext.gregs[REG_PC]; if (StubRoutines::is_safefetch_fault(pc)) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + os::Solaris::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return true; } @@ -614,8 +618,7 @@ if (thread != NULL) thread->set_saved_exception_pc(pc); // 12/02/99: On Sparc it appears that the full context is also saved // but as yet, no one looks at or restores that saved context - // factor me: setPC - uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub; + os::Solaris::ucontext_set_pc(uc, stub); return true; }
--- a/src/share/vm/runtime/globals.hpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/share/vm/runtime/globals.hpp Thu Mar 12 19:34:50 2015 -0400 @@ -921,6 +921,9 @@ "If > 0, provokes an error inside VM error handler (a secondary " \ "crash). see test_error_handler() in debug.cpp.") \ \ + notproduct(bool, TestSafeFetchInErrorHandler, false, \ + "If true, tests SafeFetch inside error handler.") \ + \ develop(bool, Verbose, false, \ "Print additional debugging information from other modes") \ \
--- a/src/share/vm/runtime/stubRoutines.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/share/vm/runtime/stubRoutines.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -210,8 +210,36 @@ assert(fbuffer[i] == v && fbuffer2[i] == v2, "shouldn't have copied anything"); } } + +// simple test for SafeFetch32 +static void test_safefetch32() { + int dummy = 17; + int* const p_invalid = (int*) get_segfault_address(); + int* const p_valid = &dummy; + int result_invalid = SafeFetch32(p_invalid, 0xABC); + assert(result_invalid == 0xABC, "SafeFetch32 error"); + int result_valid = SafeFetch32(p_valid, 0xABC); + assert(result_valid == 17, "SafeFetch32 error"); +} + +// simple test for SafeFetchN +static void test_safefetchN() { +#ifdef _LP64 + const intptr_t v1 = UCONST64(0xABCD00000000ABCD); + const intptr_t v2 = UCONST64(0xDEFD00000000DEFD); +#else + const intptr_t v1 = 0xABCDABCD; + const intptr_t v2 = 0xDEFDDEFD; #endif - + intptr_t dummy = v1; + intptr_t* const p_invalid = (intptr_t*) get_segfault_address(); + intptr_t* const p_valid = &dummy; + intptr_t result_invalid = SafeFetchN(p_invalid, v2); + assert(result_invalid == v2, "SafeFetchN error"); + intptr_t result_valid = SafeFetchN(p_valid, v2); + assert(result_valid == v1, "SafeFetchN error"); +} +#endif void StubRoutines::initialize2() { if (_code2 == NULL) { @@ -300,6 +328,13 @@ test_arraycopy_func(CAST_FROM_FN_PTR(address, Copy::aligned_conjoint_words), sizeof(jlong)); test_arraycopy_func(CAST_FROM_FN_PTR(address, Copy::aligned_disjoint_words), sizeof(jlong)); + // test safefetch routines + // Not on Windows 32bit until 8074860 is fixed +#if ! (defined(_WIN32) && defined(_M_IX86)) + test_safefetch32(); + test_safefetchN(); +#endif + #endif }
--- a/src/share/vm/runtime/stubRoutines.hpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/share/vm/runtime/stubRoutines.hpp Thu Mar 12 19:34:50 2015 -0400 @@ -458,4 +458,9 @@ return StubRoutines::SafeFetchN_stub()(adr, errValue); } + +// returns true if SafeFetch32 and SafeFetchN can be used safely (stubroutines are already generated) +inline bool CanUseSafeFetch32() { return StubRoutines::SafeFetch32_stub() ? true : false; } +inline bool CanUseSafeFetchN() { return StubRoutines::SafeFetchN_stub() ? true : false; } + #endif // SHARE_VM_RUNTIME_STUBROUTINES_HPP
--- a/src/share/vm/utilities/vmError.cpp Wed Mar 11 18:49:22 2015 -0400 +++ b/src/share/vm/utilities/vmError.cpp Thu Mar 12 19:34:50 2015 -0400 @@ -358,18 +358,38 @@ // test secondary error handling. Test it twice, to test that resetting // error handler after a secondary crash works. - STEP(13, "(test secondary crash 1)") + STEP(11, "(test secondary crash 1)") + if (_verbose && TestCrashInErrorHandler != 0) { + st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...", + TestCrashInErrorHandler); + controlled_crash(TestCrashInErrorHandler); + } + + STEP(12, "(test secondary crash 2)") if (_verbose && TestCrashInErrorHandler != 0) { st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...", TestCrashInErrorHandler); controlled_crash(TestCrashInErrorHandler); } - STEP(14, "(test secondary crash 2)") - if (_verbose && TestCrashInErrorHandler != 0) { - st->print_cr("Will crash now (TestCrashInErrorHandler=%d)...", - TestCrashInErrorHandler); - controlled_crash(TestCrashInErrorHandler); + STEP(13, "(test safefetch in error handler)") + // test whether it is safe to use SafeFetch32 in Crash Handler. Test twice + // to test that resetting the signal handler works correctly. + if (_verbose && TestSafeFetchInErrorHandler) { + st->print_cr("Will test SafeFetch..."); + if (CanUseSafeFetch32()) { + int* const invalid_pointer = (int*) get_segfault_address(); + const int x = 0x76543210; + int i1 = SafeFetch32(invalid_pointer, x); + int i2 = SafeFetch32(invalid_pointer, x); + if (i1 == x && i2 == x) { + st->print_cr("SafeFetch OK."); // Correctly deflected and returned default pattern + } else { + st->print_cr("??"); + } + } else { + st->print_cr("not possible; skipped."); + } } #endif // PRODUCT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/ErrorHandling/SafeFetchInErrorHandlingTest.java Thu Mar 12 19:34:50 2015 -0400 @@ -0,0 +1,92 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.regex.Pattern; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.ProcessTools; + +/* + * @test + * @bug 8074552 + * @summary SafeFetch32 and SafeFetchN do not work in error handling + * @library /testlibrary + * @author Thomas Stuefe (SAP) + */ + +public class SafeFetchInErrorHandlingTest { + + + public static void main(String[] args) throws Exception { + + if (!Platform.isDebugBuild()) { + return; + } + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-Xmx100M", + "-XX:ErrorHandlerTest=14", + "-XX:+TestSafeFetchInErrorHandler", + "-version"); + + OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); + + // we should have crashed with a SIGSEGV + output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + output_detail.shouldMatch("# +(?:SIGSEGV|EXCEPTION_ACCESS_VIOLATION).*"); + + // extract hs-err file + String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1); + if (hs_err_file == null) { + throw new RuntimeException("Did not find hs-err file in output.\n"); + } + + File f = new File(hs_err_file); + if (!f.exists()) { + throw new RuntimeException("hs-err file missing at " + + f.getAbsolutePath() + ".\n"); + } + + System.out.println("Found hs_err file. Scanning..."); + + FileInputStream fis = new FileInputStream(f); + BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + String line = null; + + Pattern [] pattern = new Pattern[] { + Pattern.compile("Will test SafeFetch..."), + Pattern.compile("SafeFetch OK."), + }; + int currentPattern = 0; + + String lastLine = null; + while ((line = br.readLine()) != null) { + if (currentPattern < pattern.length) { + if (pattern[currentPattern].matcher(line).matches()) { + System.out.println("Found: " + line + "."); + currentPattern ++; + } + } + lastLine = line; + } + br.close(); + + if (currentPattern < pattern.length) { + throw new RuntimeException("hs-err file incomplete (first missing pattern: " + currentPattern + ")"); + } + + if (!lastLine.equals("END.")) { + throw new RuntimeException("hs-err file incomplete (missing END marker.)"); + } else { + System.out.println("End marker found."); + } + + System.out.println("OK."); + + } + +} +