diff --git a/src/hotspot/share/jvmci/jvmci.hpp b/src/hotspot/share/jvmci/jvmci.hpp index 79b2121550f7d..848cf0d4f210c 100644 --- a/src/hotspot/share/jvmci/jvmci.hpp +++ b/src/hotspot/share/jvmci/jvmci.hpp @@ -152,6 +152,8 @@ class JVMCI : public AllStatic { static void initialize_globals(); + // Called to force initialization of the JVMCI compiler + // early in VM startup. static void initialize_compiler(TRAPS); // Ensures the boxing cache classes (e.g., java.lang.Integer.IntegerCache) are initialized. diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 4b24b4a627761..247551e1301fa 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -2408,7 +2408,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas HandleMark hm(THREAD); runtime = JVMCI::compiler_runtime(thread); if (peerEnv->has_pending_exception()) { - peerEnv->describe_pending_exception(true); + peerEnv->describe_pending_exception(tty); } sl_handle = JVMCI::get_shared_library(sl_path, false); if (sl_handle == nullptr) { @@ -2577,7 +2577,7 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb HandleMark hm(thread); JVMCIObject receiver = runtime->get_HotSpotJVMCIRuntime(peerJVMCIEnv); if (peerJVMCIEnv->has_pending_exception()) { - peerJVMCIEnv->describe_pending_exception(true); + peerJVMCIEnv->describe_pending_exception(tty); } char* sl_path; if (JVMCI::get_shared_library(sl_path, false) == nullptr) { diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index 8c92209d46ad8..b3888caf7f239 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -311,25 +311,138 @@ void JVMCIEnv::init(JavaThread* thread, bool is_hotspot, bool jni_enomem_is_fata } } -// Prints a pending exception (if any) and its stack trace. -void JVMCIEnv::describe_pending_exception(bool clear) { +// Prints a pending exception (if any) and its stack trace to st. +// Also partially logs the stack trace to the JVMCI event log. +void JVMCIEnv::describe_pending_exception(outputStream* st) { + ResourceMark rm; + char* stack_trace = nullptr; + if (pending_exception_as_string(nullptr, (const char**) &stack_trace)) { + st->print_raw_cr(stack_trace); + + // Use up to half the lines of the JVMCI event log to + // show the stack trace. + char* cursor = stack_trace; + int line = 0; + const int max_lines = LogEventsBufferEntries / 2; + char* last_line = nullptr; + while (*cursor != '\0') { + char* eol = strchr(cursor, '\n'); + if (eol == nullptr) { + if (line == max_lines - 1) { + last_line = cursor; + } else if (line < max_lines) { + JVMCI_event_1("%s", cursor); + } + cursor = cursor + strlen(cursor); + } else { + *eol = '\0'; + if (line == max_lines - 1) { + last_line = cursor; + } else if (line < max_lines) { + JVMCI_event_1("%s", cursor); + } + cursor = eol + 1; + } + line++; + } + if (last_line != nullptr) { + if (line > max_lines) { + JVMCI_event_1("%s [elided %d more stack trace lines]", last_line, line - max_lines); + } else { + JVMCI_event_1("%s", last_line); + } + } + } +} + +bool JVMCIEnv::pending_exception_as_string(const char** to_string, const char** stack_trace) { JavaThread* THREAD = JavaThread::current(); // For exception macros. + JVMCIObject to_string_obj; + JVMCIObject stack_trace_obj; + bool had_nested_exception = false; if (!is_hotspot()) { JNIAccessMark jni(this, THREAD); - if (jni()->ExceptionCheck()) { - jthrowable ex = !clear ? jni()->ExceptionOccurred() : nullptr; - jni()->ExceptionDescribe(); - if (ex != nullptr) { - jni()->Throw(ex); + jthrowable ex = jni()->ExceptionOccurred(); + if (ex != NULL) { + jni()->ExceptionClear(); + jobjectArray pair = (jobjectArray) jni()->CallStaticObjectMethod( + JNIJVMCI::HotSpotJVMCIRuntime::clazz(), + JNIJVMCI::HotSpotJVMCIRuntime::exceptionToString_method(), + ex, to_string != nullptr, stack_trace != nullptr); + if (jni()->ExceptionCheck()) { + // As last resort, dump nested exception + jni()->ExceptionDescribe(); + had_nested_exception = true; + } else { + guarantee(pair != nullptr, "pair is null"); + int len = jni()->GetArrayLength(pair); + guarantee(len == 2, "bad len is %d", len); + if (to_string != nullptr) { + to_string_obj = JVMCIObject::create(jni()->GetObjectArrayElement(pair, 0), false); + } + if (stack_trace != nullptr) { + stack_trace_obj = JVMCIObject::create(jni()->GetObjectArrayElement(pair, 1), false); + } } + } else { + return false; } } else { if (HAS_PENDING_EXCEPTION) { - JVMCIRuntime::describe_pending_hotspot_exception(THREAD, clear); + Handle exception(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + JavaCallArguments jargs; + jargs.push_oop(exception); + jargs.push_int(to_string != nullptr); + jargs.push_int(stack_trace != nullptr); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + HotSpotJVMCI::HotSpotJVMCIRuntime::klass(), + vmSymbols::exceptionToString_name(), + vmSymbols::exceptionToString_signature(), &jargs, THREAD); + if (HAS_PENDING_EXCEPTION) { + Handle nested_exception(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + java_lang_Throwable::print_stack_trace(nested_exception, tty); + // Clear and ignore any exceptions raised during printing + CLEAR_PENDING_EXCEPTION; + had_nested_exception = true; + } else { + oop pair = result.get_oop(); + guarantee(pair->is_objArray(), "must be"); + objArrayOop pair_arr = objArrayOop(pair); + int len = pair_arr->length(); + guarantee(len == 2, "bad len is %d", len); + if (to_string != nullptr) { + to_string_obj = HotSpotJVMCI::wrap(pair_arr->obj_at(0)); + } + if (stack_trace != nullptr) { + stack_trace_obj = HotSpotJVMCI::wrap(pair_arr->obj_at(1)); + } + } + } else { + return false; + } + } + if (had_nested_exception) { + if (to_string != nullptr) { + *to_string = "nested exception occurred converting exception to string"; + } + if (stack_trace != nullptr) { + *stack_trace = "nested exception occurred converting exception stack to string"; + } + } else { + if (to_string_obj.is_non_null()) { + *to_string = as_utf8_string(to_string_obj); + } + if (stack_trace_obj.is_non_null()) { + *stack_trace = as_utf8_string(stack_trace_obj); } } + return true; } + // Shared code for translating an exception from HotSpot to libjvmci or vice versa. class ExceptionTranslation: public StackObj { protected: @@ -771,10 +884,11 @@ const char* JVMCIEnv::as_utf8_string(JVMCIObject str) { return java_lang_String::as_utf8_string(HotSpotJVMCI::resolve(str)); } else { JNIAccessMark jni(this); - int length = jni()->GetStringLength(str.as_jstring()); - int utf8_length = jni()->GetStringUTFLength(str.as_jstring()); + jstring jstr = str.as_jstring(); + int length = jni()->GetStringLength(jstr); + int utf8_length = jni()->GetStringUTFLength(jstr); char* result = NEW_RESOURCE_ARRAY(char, utf8_length + 1); - jni()->GetStringUTFRegion(str.as_jstring(), 0, length, result); + jni()->GetStringUTFRegion(jstr, 0, length, result); return result; } } @@ -904,7 +1018,7 @@ void JVMCIEnv::call_HotSpotJVMCIRuntime_shutdown (JVMCIObject runtime) { if (has_pending_exception()) { // This should never happen as HotSpotJVMCIRuntime.shutdown() should // handle all exceptions. - describe_pending_exception(true); + describe_pending_exception(tty); } } @@ -960,30 +1074,6 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_getCompiler (JVMCIObject runtime, } } - -JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object, JVMCIEnv* JVMCIENV) { - JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros. - if (is_hotspot()) { - JavaCallArguments jargs; - jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(object))); - JavaValue result(T_OBJECT); - JavaCalls::call_static(&result, - HotSpotJVMCI::HotSpotJVMCIRuntime::klass(), - vmSymbols::callToString_name(), - vmSymbols::callToString_signature(), &jargs, CHECK_(JVMCIObject())); - return wrap(result.get_oop()); - } else { - JNIAccessMark jni(this, THREAD); - jobject result = (jstring) jni()->CallStaticObjectMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(), - JNIJVMCI::HotSpotJVMCIRuntime::callToString_method(), - object.as_jobject()); - if (jni()->ExceptionCheck()) { - return JVMCIObject(); - } - return wrap(result); - } -} - void JVMCIEnv::call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCIEnv* JVMCIENV) { JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros. if (is_hotspot()) { diff --git a/src/hotspot/share/jvmci/jvmciEnv.hpp b/src/hotspot/share/jvmci/jvmciEnv.hpp index fc02207ae9ca7..0f25e8ce5c259 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.hpp +++ b/src/hotspot/share/jvmci/jvmciEnv.hpp @@ -258,10 +258,16 @@ class JVMCIEnv : public ResourceObj { // Returns true if a pending exception was transferred, false otherwise. static jboolean transfer_pending_exception_to_jni(JavaThread* THREAD, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env); - // Prints the toString() and stack trace of a pending exception. + // Prints the stack trace of a pending exception to `st` and clears the exception. // If there is no pending exception, this is a nop. - // If `clear` is false, the pending exception will remain pending upon return. - void describe_pending_exception(bool clear); + void describe_pending_exception(outputStream* st); + + // Gets the output of calling toString and/or printStactTrace on the pending exception. + // If to_string is not null, the output of toString is returned in it. + // If stack_trace is not null, the output of printStackTrace is returned in it. + // Returns false if there is no pending exception otherwise clears the pending + // exception and returns true. + bool pending_exception_as_string(const char** to_string, const char** stack_trace); int get_length(JVMCIArray array); @@ -342,8 +348,6 @@ class JVMCIEnv : public ResourceObj { JVMCIObject call_JVMCI_getRuntime(JVMCI_TRAPS); JVMCIObject call_HotSpotJVMCIRuntime_getCompiler(JVMCIObject runtime, JVMCI_TRAPS); - JVMCIObject call_HotSpotJVMCIRuntime_callToString(JVMCIObject object, JVMCI_TRAPS); - JVMCIObject call_JavaConstant_forPrimitive(jchar type_char, jlong value, JVMCI_TRAPS); jboolean call_HotSpotJVMCIRuntime_isGCSupported(JVMCIObject runtime, jint gcIdentifier); diff --git a/src/hotspot/share/jvmci/jvmciExceptions.hpp b/src/hotspot/share/jvmci/jvmciExceptions.hpp index 9d7c986b43dfe..02dfb7924d143 100644 --- a/src/hotspot/share/jvmci/jvmciExceptions.hpp +++ b/src/hotspot/share/jvmci/jvmciExceptions.hpp @@ -72,7 +72,7 @@ class JVMCIEnv; #define JVMCI_CATCH \ JVMCIENV); if (JVMCI_HAS_PENDING_EXCEPTION) { \ - JVMCIENV->describe_pending_exception(true); \ + JVMCIENV->describe_pending_exception(tty); \ ShouldNotReachHere(); \ } (void)(0 diff --git a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp index 4b84819c9ed65..c771f4e8f391f 100644 --- a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp +++ b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp @@ -206,7 +206,7 @@ jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, shutdown, void_method_signature, (JVMCIObject runtime)) \ jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, runtime, runtime_signature, (JVMCI_TRAPS)) \ jvmci_method(CallObjectMethod, GetMethodID, call_virtual, JVMCIObject, HotSpotJVMCIRuntime, getCompiler, getCompiler_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \ - jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, callToString, callToString_signature, (JVMCIObject object, JVMCI_TRAPS)) \ + jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, exceptionToString, exceptionToString_signature, (JVMCIObject object, bool toString, bool stackTrace, JVMCI_TRAPS)) \ jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, postTranslation, object_void_signature, (JVMCIObject object, JVMCI_TRAPS)) \ end_class \ start_class(JVMCIError, jdk_vm_ci_common_JVMCIError) \ diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index f21a5759b2ade..bdeb9cb6d7ea7 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -35,6 +35,7 @@ #include "jvmci/jvmciRuntime.hpp" #include "jvmci/metadataHandles.hpp" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/oopFactory.hpp" #include "memory/universe.hpp" #include "oops/constantPool.inline.hpp" @@ -1610,19 +1611,14 @@ void JVMCIRuntime::bootstrap_finished(TRAPS) { } } -void JVMCIRuntime::describe_pending_hotspot_exception(JavaThread* THREAD, bool clear) { +void JVMCIRuntime::describe_pending_hotspot_exception(JavaThread* THREAD) { if (HAS_PENDING_EXCEPTION) { Handle exception(THREAD, PENDING_EXCEPTION); - const char* exception_file = THREAD->exception_file(); - int exception_line = THREAD->exception_line(); CLEAR_PENDING_EXCEPTION; java_lang_Throwable::print_stack_trace(exception, tty); // Clear and ignore any exceptions raised during printing CLEAR_PENDING_EXCEPTION; - if (!clear) { - THREAD->set_pending_exception(exception(), exception_file, exception_line); - } } } @@ -1635,15 +1631,15 @@ void JVMCIRuntime::fatal_exception(JVMCIEnv* JVMCIENV, const char* message) { // Only report an error once tty->print_raw_cr(message); if (JVMCIENV != nullptr) { - JVMCIENV->describe_pending_exception(true); + JVMCIENV->describe_pending_exception(tty); } else { - describe_pending_hotspot_exception(THREAD, true); + describe_pending_hotspot_exception(THREAD); } } else { - // Allow error reporting thread to print the stack trace. + // Allow error reporting thread time to print the stack trace. THREAD->sleep(200); } - fatal("Fatal exception in JVMCI: %s", message); + fatal("Fatal JVMCI exception (see JVMCI Events for stack trace): %s", message); } // ------------------------------------------------------------------ @@ -1992,19 +1988,25 @@ JVMCI::CodeInstallResult JVMCIRuntime::validate_compile_task_dependencies(Depend // Otherwise, it returns false. static bool after_compiler_upcall(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, const methodHandle& method, const char* function) { if (JVMCIENV->has_pending_exception()) { + ResourceMark rm; bool reason_on_C_heap = true; - const char* failure_reason = os::strdup(err_msg("uncaught exception in %s", function), mtJVMCI); + const char* pending_string = nullptr; + const char* pending_stack_trace = nullptr; + JVMCIENV->pending_exception_as_string(&pending_string, &pending_stack_trace); + if (pending_string == nullptr) pending_string = "null"; + const char* failure_reason = os::strdup(err_msg("uncaught exception in %s [%s]", function, pending_string), mtJVMCI); if (failure_reason == nullptr) { failure_reason = "uncaught exception"; reason_on_C_heap = false; } + JVMCI_event_1("%s", failure_reason); Log(jit, compilation) log; if (log.is_info()) { - ResourceMark rm; log.info("%s while compiling %s", failure_reason, method->name_and_sig_as_C_string()); - JVMCIENV->describe_pending_exception(true); - } else { - JVMCIENV->clear_pending_exception(); + if (pending_stack_trace != nullptr) { + LogStream ls(log.info()); + ls.print_raw_cr(pending_stack_trace); + } } JVMCICompileState* compile_state = JVMCIENV->compile_state(); compile_state->set_failure(true, failure_reason, reason_on_C_heap); @@ -2049,6 +2051,13 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c JVMCIObject result_object = JVMCIENV->call_HotSpotJVMCIRuntime_compileMethod(receiver, jvmci_method, entry_bci, (jlong) compile_state, compile_state->task()->compile_id()); + if (JVMCIENV->has_pending_exception()) { + const char* val = Arguments::PropertyList_get_value(Arguments::system_properties(), "test.jvmci.compileMethodExceptionIsFatal"); + if (val != nullptr && strcmp(val, "true") == 0) { + fatal_exception(JVMCIENV, "testing JVMCI fatal exception handling"); + } + } + if (after_compiler_upcall(JVMCIENV, compiler, method, "call_HotSpotJVMCIRuntime_compileMethod")) { return; } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index e9068ea2819ef..5648a508b3a34 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -469,7 +469,7 @@ class JVMCIRuntime: public CHeapObj { // Reports an unexpected exception and exits the VM with a fatal error. static void fatal_exception(JVMCIEnv* JVMCIENV, const char* message); - static void describe_pending_hotspot_exception(JavaThread* THREAD, bool clear); + static void describe_pending_hotspot_exception(JavaThread* THREAD); #define CHECK_EXIT THREAD); \ if (HAS_PENDING_EXCEPTION) { \ diff --git a/src/hotspot/share/jvmci/vmSymbols_jvmci.hpp b/src/hotspot/share/jvmci/vmSymbols_jvmci.hpp index d9be99068a7b4..eb1eaee2ac868 100644 --- a/src/hotspot/share/jvmci/vmSymbols_jvmci.hpp +++ b/src/hotspot/share/jvmci/vmSymbols_jvmci.hpp @@ -94,8 +94,8 @@ template(runtime_signature, "()Ljdk/vm/ci/hotspot/HotSpotJVMCIRuntime;") \ template(getCompiler_name, "getCompiler") \ template(getCompiler_signature, "()Ljdk/vm/ci/runtime/JVMCICompiler;") \ - template(callToString_name, "callToString") \ - template(callToString_signature, "(Ljava/lang/Object;)Ljava/lang/String;") \ + template(exceptionToString_name, "exceptionToString") \ + template(exceptionToString_signature, "(Ljava/lang/Throwable;ZZ)[Ljava/lang/String;") \ template(postTranslation_name, "postTranslation") \ template(getName_name, "getName") \ template(bootstrapFinished_name, "bootstrapFinished") \ diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 9425296ae4fef..423b52e183242 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -28,6 +28,7 @@ import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; import java.io.IOException; +import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.Serializable; @@ -39,6 +40,7 @@ import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Formatter; @@ -198,8 +200,19 @@ public static HotSpotJVMCIRuntime runtime() { } @VMEntryPoint - static String callToString(Object o) { - return o.toString(); + static String[] exceptionToString(Throwable o, boolean toString, boolean stackTrace) { + String[] res = {null, null}; + if (toString) { + res[0] = o.toString(); + } + if (stackTrace) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + try (PrintStream ps = new PrintStream(buf)) { + o.printStackTrace(ps); + } + res[1] = buf.toString(StandardCharsets.UTF_8); + } + return res; } /** diff --git a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.config b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.config new file mode 100644 index 0000000000000..521c27977f242 --- /dev/null +++ b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.config @@ -0,0 +1 @@ +compiler.jvmci.TestUncaughtErrorInCompileMethod diff --git a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java new file mode 100644 index 0000000000000..902dd839b7dd0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2023, 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 + * @summary Tests handling of an exception thrown by HotSpotJVMCIRuntime.compileMethod. + * @requires vm.jvmci + * @library /test/lib / + * @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot + * jdk.internal.vm.ci/jdk.vm.ci.code + * jdk.internal.vm.ci/jdk.vm.ci.meta + * jdk.internal.vm.ci/jdk.vm.ci.runtime + * jdk.internal.vm.ci/jdk.vm.ci.services + * @run driver jdk.test.lib.FileInstaller ./TestUncaughtErrorInCompileMethod.config + * ./META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator + * @run driver compiler.jvmci.TestUncaughtErrorInCompileMethod + */ + +package compiler.jvmci; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.vm.ci.services.JVMCIServiceLocator; +import jdk.vm.ci.runtime.JVMCICompiler; +import jdk.vm.ci.runtime.JVMCICompilerFactory; +import jdk.vm.ci.runtime.JVMCIRuntime; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class TestUncaughtErrorInCompileMethod extends JVMCIServiceLocator { + + /** + * Name of file whose existence implies that a JVMCICompiler has been created. + */ + static String tmpFileName = "ErrorCompilerCreated." + System.nanoTime(); + + /** + * @param args if args.length != 0, then executing in subprocess + */ + public static void main(String[] args) throws Exception { + if (args.length == 0) { + testSubprocess(false); + testSubprocess(true); + } else { + File watch = new File(tmpFileName); + int total = 0; + long start = System.currentTimeMillis(); + + // Use a 10 sec timeout to prevent endless loop if + // JVMCI compiler creation fails + while (System.currentTimeMillis() - start < 10_000) { + total += getTime(); + if (watch.exists()) { + System.err.println("saw " + watch + " - exiting loop"); + watch.delete(); + break; + } + } + System.out.println(total); + } + } + + private static long getTime() { + return System.currentTimeMillis(); + } + + static void testSubprocess(boolean fatalError) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseJVMCICompiler", "-Djvmci.Compiler=ErrorCompiler", + "-XX:-TieredCompilation", + "-XX:+PrintCompilation", + "--add-exports=jdk.internal.vm.ci/jdk.vm.ci.services=ALL-UNNAMED", + "-Dtest.jvmci.compileMethodExceptionIsFatal=" + (fatalError ? "true" : "false"), + "-XX:+PrintWarnings", + "-Xbootclasspath/a:.", + TestUncaughtErrorInCompileMethod.class.getName(), "true"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + if (fatalError) { + output.shouldContain("testing JVMCI fatal exception handling"); + output.shouldNotHaveExitValue(0); + File hs_err_file = openHsErrFileFromOutput(output); + Path hsErrPath = hs_err_file.toPath(); + if (!Files.exists(hsErrPath)) { + throw new RuntimeException("hs_err_pid file missing at " + hsErrPath); + } + String hsErr = Files.readString(hsErrPath); + + /* + * JVMCI Events (11 events): + * ... + * Event: 0.274 Thread 0x0000000146819210 compiler.jvmci.TestUncaughtErrorInCompileMethod$CompilerCreationError + * Event: 0.274 Thread 0x0000000146819210 at compiler.jvmci.TestUncaughtErrorInCompileMethod$1.createCompiler(TestUncaughtErrorInCompileMethod.java:147) + * Event: 0.274 Thread 0x0000000146819210 at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.getCompiler(HotSpotJVMCIRuntime.java:829) + * Event: 0.274 Thread 0x0000000146819210 at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.compileMethod(HotSpotJVMCIRuntime.java:943) + */ + + // Check that hs-err contains the stack trace of the fatal exception (sample shown above) + String[] stackTraceSubstrings = { + "at compiler.jvmci.TestUncaughtErrorInCompileMethod$1.createCompiler(TestUncaughtErrorInCompileMethod.java", + "at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.compileMethod(HotSpotJVMCIRuntime.java" + }; + for (String expect : stackTraceSubstrings) { + if (!hsErr.contains(expect)) { + throw new RuntimeException("Could not find \"" + expect + "\" in " + hsErrPath); + } + } + } else { + output.shouldContain("COMPILE SKIPPED: uncaught exception in call_HotSpotJVMCIRuntime_compileMethod [compiler.jvmci.TestUncaughtErrorInCompileMethod$CompilerCreationError"); + output.shouldHaveExitValue(0); + } + } + + public TestUncaughtErrorInCompileMethod() { + } + + static class CompilerCreationError extends InternalError { + CompilerCreationError(int attempt) { + super("attempt " + attempt); + } + } + + @Override + public S getProvider(Class service) { + if (service == JVMCICompilerFactory.class) { + return service.cast(new JVMCICompilerFactory() { + final AtomicInteger counter = new AtomicInteger(); + @Override + public String getCompilerName() { + return "ErrorCompiler"; + } + + @Override + public JVMCICompiler createCompiler(JVMCIRuntime runtime) { + int attempt = counter.incrementAndGet(); + CompilerCreationError e = new CompilerCreationError(attempt); + e.printStackTrace(); + if (attempt == 10) { + // Delay the creation of the file that causes the + // loop in main to exit so that compilation failures + // have time to be reported by -XX:+PrintCompilation. + File watch = new File(tmpFileName); + try { + System.err.println("creating " + watch); + watch.createNewFile(); + System.err.println("created " + watch); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + throw e; + } + }); + } + return null; + } + + /** + * Given the output of a java VM that crashed, extract the name of the hs-err file from the output + */ + public static String extractHsErrFileNameFromOutput(OutputAnalyzer output) { + output.shouldMatch("# A fatal error has been detected.*"); + + // extract hs-err file + String hs_err_file = output.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"); + } + + return hs_err_file; + } + + /** + * Given the output of a java VM that crashed, extract the name of the hs-err file from the output, + * open that file and return its File. + */ + public static File openHsErrFileFromOutput(OutputAnalyzer output) { + String name = extractHsErrFileNameFromOutput(output); + File f = new File(name); + if (!f.exists()) { + throw new RuntimeException("Cannot find hs-err file at " + f.getAbsolutePath()); + } + return f; + } +}