8308151: [JVMCI] capture JVMCI exceptions in hs-err
Reviewed-by: never
This commit is contained in:
parent
beb75e651f
commit
05c095cf39
src
hotspot/share/jvmci
jvmci.hppjvmciCompilerToVM.cppjvmciEnv.cppjvmciEnv.hppjvmciExceptions.hppjvmciJavaClasses.hppjvmciRuntime.cppjvmciRuntime.hppvmSymbols_jvmci.hpp
jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot
test/hotspot/jtreg/compiler/jvmci
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
JavaThread* THREAD = JavaThread::current(); // For exception macros.
|
||||
if (!is_hotspot()) {
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
if (jni()->ExceptionCheck()) {
|
||||
jthrowable ex = !clear ? jni()->ExceptionOccurred() : nullptr;
|
||||
jni()->ExceptionDescribe();
|
||||
if (ex != nullptr) {
|
||||
jni()->Throw(ex);
|
||||
// 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++;
|
||||
}
|
||||
} else {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
JVMCIRuntime::describe_pending_hotspot_exception(THREAD, clear);
|
||||
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);
|
||||
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) {
|
||||
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()) {
|
||||
|
@ -258,10 +258,16 @@ public:
|
||||
// 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 @@ public:
|
||||
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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
|
||||
// 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) { \
|
||||
|
@ -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") \
|
||||
|
@ -28,6 +28,7 @@ import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
|
||||
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.Executable;
|
||||
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 final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1 @@
|
||||
compiler.jvmci.TestUncaughtErrorInCompileMethod
|
@ -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> S getProvider(Class<S> 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user