8298099: [JVMCI] decouple libgraal from JVMCI module at runtime

Reviewed-by: never
This commit is contained in:
Doug Simon 2022-12-07 22:11:11 +00:00
parent 8a9911ef17
commit 8b69a2e434
18 changed files with 381 additions and 467 deletions
src
hotspot/share
java.base/share/classes/jdk/internal/vm
jdk.internal.vm.ci/share/classes
jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot
jdk.vm.ci.services/src/jdk/vm/ci/services
test
hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test
jdk/jdk/internal/vm

@ -671,6 +671,18 @@ void ClassLoader::setup_bootstrap_search_path_impl(JavaThread* current, const ch
}
}
// Gets the exploded path for the named module. The memory for the path
// is allocated on the C heap if `c_heap` is true otherwise in the resource area.
static const char* get_exploded_module_path(const char* module_name, bool c_heap) {
const char *home = Arguments::get_java_home();
const char file_sep = os::file_separator()[0];
// 10 represents the length of "modules" + 2 file separators + \0
size_t len = strlen(home) + strlen(module_name) + 10;
char *path = c_heap ? NEW_C_HEAP_ARRAY(char, len, mtModule) : NEW_RESOURCE_ARRAY(char, len);
jio_snprintf(path, len, "%s%cmodules%c%s", home, file_sep, file_sep, module_name);
return path;
}
// During an exploded modules build, each module defined to the boot loader
// will be added to the ClassLoader::_exploded_entries array.
void ClassLoader::add_to_exploded_build_list(JavaThread* current, Symbol* module_sym) {
@ -680,12 +692,7 @@ void ClassLoader::add_to_exploded_build_list(JavaThread* current, Symbol* module
// Find the module's symbol
ResourceMark rm(current);
const char *module_name = module_sym->as_C_string();
const char *home = Arguments::get_java_home();
const char file_sep = os::file_separator()[0];
// 10 represents the length of "modules" + 2 file separators + \0
size_t len = strlen(home) + strlen(module_name) + 10;
char *path = NEW_RESOURCE_ARRAY(char, len);
jio_snprintf(path, len, "%s%cmodules%c%s", home, file_sep, file_sep, module_name);
const char *path = get_exploded_module_path(module_name, false);
struct stat st;
if (os::stat(path, &st) == 0) {
@ -1415,6 +1422,20 @@ char* ClassLoader::lookup_vm_options() {
return options;
}
bool ClassLoader::is_module_observable(const char* module_name) {
assert(JImageOpen != NULL, "jimage library should have been opened");
if (JImage_file == NULL) {
struct stat st;
const char *path = get_exploded_module_path(module_name, true);
bool res = os::stat(path, &st) == 0;
FREE_C_HEAP_ARRAY(char, path);
return res;
}
jlong size;
const char *jimage_version = get_jimage_version_string();
return (*JImageFindResource)(JImage_file, module_name, jimage_version, "module-info.class", &size) != 0;
}
#if INCLUDE_CDS
void ClassLoader::initialize_shared_path(JavaThread* current) {
if (Arguments::is_dumping_archive()) {

@ -374,6 +374,10 @@ class ClassLoader: AllStatic {
static char* lookup_vm_options();
// Determines if the named module is present in the
// modules jimage file or in the exploded modules directory.
static bool is_module_observable(const char* module_name);
static JImageLocationRef jimage_find_resource(JImageFile* jf, const char* module_name,
const char* file_name, jlong &size);

@ -745,6 +745,10 @@
do_alias(appendToClassPathForInstrumentation_signature, string_void_signature) \
template(serializePropertiesToByteArray_name, "serializePropertiesToByteArray") \
template(serializeAgentPropertiesToByteArray_name, "serializeAgentPropertiesToByteArray") \
template(serializeSavedPropertiesToByteArray_name, "serializeSavedPropertiesToByteArray") \
template(encodeThrowable_name, "encodeThrowable") \
template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
template(classRedefinedCount_name, "classRedefinedCount") \
template(classLoader_name, "classLoader") \
template(componentType_name, "componentType") \

@ -35,6 +35,7 @@
#include "memory/universe.hpp"
#include "runtime/arguments.hpp"
#include "runtime/atomic.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/os.hpp"
#include "utilities/events.hpp"
@ -228,9 +229,17 @@ void JVMCI::vlog(int level, const char* format, va_list ap) {
void JVMCI::vtrace(int level, const char* format, va_list ap) {
if (JVMCITraceLevel >= level) {
Thread* thread = Thread::current_or_null_safe();
if (thread != nullptr) {
ResourceMark rm;
tty->print("JVMCITrace-%d[%s]:%*c", level, thread->name(), level, ' ');
if (thread != nullptr && thread->is_Java_thread()) {
ResourceMark rm(thread);
JavaThreadState state = JavaThread::cast(thread)->thread_state();
if (state == _thread_in_vm || state == _thread_in_Java || state == _thread_new) {
tty->print("JVMCITrace-%d[%s]:%*c", level, thread->name(), level, ' ');
} else {
// According to check_access_thread_state, it's unsafe to
// resolve the j.l.Thread object unless the thread is in
// one of the states above.
tty->print("JVMCITrace-%d[%s@" PTR_FORMAT "]:%*c", level, thread->type_name(), p2i(thread), level, ' ');
}
} else {
tty->print("JVMCITrace-%d[?]:%*c", level, level, ' ');
}

@ -142,8 +142,8 @@ static JavaThread* get_current_thread(bool allow_null=true) {
err_msg("Cannot call into HotSpot from JVMCI shared library without attaching current thread")); \
return; \
} \
JVMCITraceMark jtm("CompilerToVM::" #name); \
C2V_BLOCK(result_type, name, signature)
C2V_BLOCK(result_type, name, signature) \
JVMCITraceMark jtm("CompilerToVM::" #name);
#define C2V_VMENTRY_(result_type, name, signature, result) \
JNIEXPORT result_type JNICALL c2v_ ## name signature { \
@ -153,8 +153,8 @@ static JavaThread* get_current_thread(bool allow_null=true) {
err_msg("Cannot call into HotSpot from JVMCI shared library without attaching current thread")); \
return result; \
} \
JVMCITraceMark jtm("CompilerToVM::" #name); \
C2V_BLOCK(result_type, name, signature)
C2V_BLOCK(result_type, name, signature) \
JVMCITraceMark jtm("CompilerToVM::" #name);
#define C2V_VMENTRY_NULL(result_type, name, signature) C2V_VMENTRY_(result_type, name, signature, NULL)
#define C2V_VMENTRY_0(result_type, name, signature) C2V_VMENTRY_(result_type, name, signature, 0)
@ -503,7 +503,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
} else {
// Use the System class loader
class_loader = Handle(THREAD, SystemDictionary::java_system_loader());
JVMCIENV->runtime()->initialize(JVMCIENV);
JVMCIENV->runtime()->initialize(JVMCI_CHECK_NULL);
}
if (resolve) {
@ -2312,9 +2312,9 @@ C2V_VMENTRY_PREFIX(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject c2vm
// Called from unattached JVMCI shared library thread
return false;
}
JVMCITraceMark jtm("isCurrentThreadAttached");
if (thread->jni_environment() == env) {
C2V_BLOCK(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject))
JVMCITraceMark jtm("isCurrentThreadAttached");
requireJVMCINativeLibrary(JVMCI_CHECK_0);
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime == nullptr || !runtime->has_shared_library_javavm()) {
@ -2331,7 +2331,6 @@ C2V_VMENTRY_PREFIX(jlong, getCurrentJavaThread, (JNIEnv* env, jobject c2vm))
// Called from unattached JVMCI shared library thread
return 0L;
}
JVMCITraceMark jtm("getCurrentJavaThread");
return (jlong) p2i(thread);
C2V_END
@ -2377,10 +2376,10 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb
attachSharedLibraryThread(env, name, as_daemon);
return true;
}
JVMCITraceMark jtm("attachCurrentThread");
if (thread->jni_environment() == env) {
// Called from HotSpot
C2V_BLOCK(jboolean, attachCurrentThread, (JNIEnv* env, jobject, jboolean))
JVMCITraceMark jtm("attachCurrentThread");
requireJVMCINativeLibrary(JVMCI_CHECK_0);
JVMCIRuntime* runtime = JVMCI::compiler_runtime(thread);
@ -2435,10 +2434,10 @@ C2V_VMENTRY_PREFIX(jboolean, detachCurrentThread, (JNIEnv* env, jobject c2vm, jb
// Called from unattached JVMCI shared library thread
JNI_THROW_("detachCurrentThread", IllegalStateException, "Cannot detach non-attached thread", false);
}
JVMCITraceMark jtm("detachCurrentThread");
if (thread->jni_environment() == env) {
// Called from HotSpot
C2V_BLOCK(void, detachCurrentThread, (JNIEnv* env, jobject))
JVMCITraceMark jtm("detachCurrentThread");
requireJVMCINativeLibrary(JVMCI_CHECK_0);
requireInHotSpot("detachCurrentThread", JVMCI_CHECK_0);
JVMCIRuntime* runtime = thread->libjvmci_runtime();

@ -39,11 +39,15 @@
#include "runtime/deoptimization.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/os.hpp"
#include "jvmci/jniAccessMark.inline.hpp"
#include "jvmci/jvmciCompiler.hpp"
#include "jvmci/jvmciRuntime.hpp"
jbyte* JVMCIEnv::_serialized_saved_properties = nullptr;
int JVMCIEnv::_serialized_saved_properties_len = 0;
JVMCICompileState::JVMCICompileState(CompileTask* task, JVMCICompiler* compiler):
_task(task),
_compiler(compiler),
@ -98,61 +102,73 @@ bool JVMCICompileState::jvmti_state_changed() const {
return false;
}
void JVMCIEnv::copy_saved_properties() {
assert(!is_hotspot(), "can only copy saved properties from HotSpot to native image");
jbyte* JVMCIEnv::get_serialized_saved_properties(int& props_len, TRAPS) {
jbyte* props = _serialized_saved_properties;
if (props == nullptr) {
// load VMSupport
Symbol* klass = vmSymbols::jdk_internal_vm_VMSupport();
Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK_NULL);
JavaThread* THREAD = JavaThread::current(); // For exception macros.
Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_services_Services(), Handle(), Handle(), true, THREAD);
if (HAS_PENDING_EXCEPTION) {
JVMCIRuntime::fatal_exception(NULL, "Error initializing jdk.vm.ci.services.Services");
}
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->should_be_initialized()) {
ik->initialize(THREAD);
if (HAS_PENDING_EXCEPTION) {
JVMCIRuntime::fatal_exception(NULL, "Error initializing jdk.vm.ci.services.Services");
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->should_be_initialized()) {
ik->initialize(CHECK_NULL);
}
}
// Get the serialized saved properties from HotSpot
TempNewSymbol serializeSavedProperties = SymbolTable::new_symbol("serializeSavedProperties");
JavaValue result(T_OBJECT);
JavaCallArguments args;
JavaCalls::call_static(&result, ik, serializeSavedProperties, vmSymbols::void_byte_array_signature(), &args, THREAD);
if (HAS_PENDING_EXCEPTION) {
JVMCIRuntime::fatal_exception(NULL, "Error calling jdk.vm.ci.services.Services.serializeSavedProperties");
}
oop res = result.get_oop();
assert(res->is_typeArray(), "must be");
assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "must be");
typeArrayOop ba = typeArrayOop(res);
int serialized_properties_len = ba->length();
// invoke the serializeSavedPropertiesToByteArray method
JavaValue result(T_OBJECT);
JavaCallArguments args;
// Copy serialized saved properties from HotSpot object into native buffer
jbyte* serialized_properties = NEW_RESOURCE_ARRAY(jbyte, serialized_properties_len);
memcpy(serialized_properties, ba->byte_at_addr(0), serialized_properties_len);
Symbol* signature = vmSymbols::void_byte_array_signature();
JavaCalls::call_static(&result,
ik,
vmSymbols::serializeSavedPropertiesToByteArray_name(),
signature,
&args,
CHECK_NULL);
oop res = result.get_oop();
assert(res->is_typeArray(), "must be");
assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "must be");
typeArrayOop ba = typeArrayOop(res);
props_len = ba->length();
// Copy serialized saved properties from HotSpot object into C heap
props = NEW_C_HEAP_ARRAY(jbyte, props_len, mtJVMCI);
memcpy(props, ba->byte_at_addr(0), props_len);
_serialized_saved_properties_len = props_len;
_serialized_saved_properties = props;
} else {
props_len = _serialized_saved_properties_len;
}
return props;
}
void JVMCIEnv::copy_saved_properties(jbyte* properties, int properties_len, JVMCI_TRAPS) {
assert(!is_hotspot(), "can only copy saved properties from HotSpot to native image");
JavaThread* thread = JavaThread::current(); // For exception macros.
// Copy native buffer into shared library object
JVMCIPrimitiveArray buf = new_byteArray(serialized_properties_len, this);
JVMCIPrimitiveArray buf = new_byteArray(properties_len, this);
if (has_pending_exception()) {
describe_pending_exception(true);
fatal("Error in copy_saved_properties");
_runtime->fatal_exception(JVMCIENV, "Error in copy_saved_properties");
}
copy_bytes_from(serialized_properties, buf, 0, serialized_properties_len);
copy_bytes_from(properties, buf, 0, properties_len);
if (has_pending_exception()) {
describe_pending_exception(true);
fatal("Error in copy_saved_properties");
_runtime->fatal_exception(JVMCIENV, "Error in copy_saved_properties");
}
// Initialize saved properties in shared library
jclass servicesClass = JNIJVMCI::Services::clazz();
jmethodID initializeSavedProperties = JNIJVMCI::Services::initializeSavedProperties_method();
JNIAccessMark jni(this, THREAD);
jni()->CallStaticVoidMethod(servicesClass, initializeSavedProperties, buf.as_jobject());
if (jni()->ExceptionCheck()) {
jni()->ExceptionDescribe();
fatal("Error calling jdk.vm.ci.services.Services.initializeSavedProperties");
bool exception = false;
{
JNIAccessMark jni(this, thread);
jni()->CallStaticVoidMethod(servicesClass, initializeSavedProperties, buf.as_jobject());
exception = jni()->ExceptionCheck();
}
if (exception) {
_runtime->fatal_exception(JVMCIENV, "Error calling jdk.vm.ci.services.Services.initializeSavedProperties");
}
}
@ -302,39 +318,40 @@ class ExceptionTranslation: public StackObj {
// Encodes the exception in `_from_env` into `buffer`.
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
// and the encoding was written to `buffer` otherwise returns -N.
virtual int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) = 0;
virtual int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) = 0;
// Decodes the exception in `buffer` in `_to_env` and throws it.
virtual void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) = 0;
virtual void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) = 0;
public:
void doit(JavaThread* THREAD) {
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
// Resolve VMSupport class explicitly as HotSpotJVMCI::compute_offsets
// may not have been called.
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
guarantee(!HAS_PENDING_EXCEPTION, "");
int buffer_size = 2048;
while (true) {
ResourceMark rm;
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jbyte, buffer_size);
if (buffer == 0L) {
decode(THREAD, runtimeKlass, 0L);
decode(THREAD, vmSupport, 0L);
return;
}
int res = encode(THREAD, runtimeKlass, buffer, buffer_size);
int res = encode(THREAD, vmSupport, buffer, buffer_size);
if (_from_env != nullptr && !_from_env->is_hotspot() && _from_env->has_pending_exception()) {
// Cannot get name of exception thrown by `encode` as that involves
// calling into libjvmci which in turn can raise another exception.
_from_env->clear_pending_exception();
decode(THREAD, runtimeKlass, -2L);
decode(THREAD, vmSupport, -2L);
return;
} else if (HAS_PENDING_EXCEPTION) {
Symbol *ex_name = PENDING_EXCEPTION->klass()->name();
CLEAR_PENDING_EXCEPTION;
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
decode(THREAD, runtimeKlass, -1L);
decode(THREAD, vmSupport, -1L);
} else {
decode(THREAD, runtimeKlass, -2L);
decode(THREAD, vmSupport, -2L);
}
return;
} else if (res < 0) {
@ -343,9 +360,9 @@ class ExceptionTranslation: public StackObj {
buffer_size = required_buffer_size;
}
} else {
decode(THREAD, runtimeKlass, buffer);
decode(THREAD, vmSupport, buffer);
if (!_to_env->has_pending_exception()) {
_to_env->throw_InternalError("HotSpotJVMCIRuntime.decodeAndThrowThrowable should have thrown an exception");
_to_env->throw_InternalError("decodeAndThrowThrowable should have thrown an exception");
}
return;
}
@ -358,23 +375,23 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
private:
const Handle& _throwable;
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
JavaCallArguments jargs;
jargs.push_oop(_throwable);
jargs.push_long(buffer);
jargs.push_int(buffer_size);
JavaValue result(T_INT);
JavaCalls::call_static(&result,
runtimeKlass,
vmSupport,
vmSymbols::encodeThrowable_name(),
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
return result.get_jint();
}
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
JNIAccessMark jni(_to_env, THREAD);
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
JNIJVMCI::HotSpotJVMCIRuntime::decodeAndThrowThrowable_method(),
jni()->CallStaticVoidMethod(JNIJVMCI::VMSupport::clazz(),
JNIJVMCI::VMSupport::decodeAndThrowThrowable_method(),
buffer);
}
public:
@ -387,19 +404,19 @@ class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
private:
jthrowable _throwable;
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
JNIAccessMark jni(_from_env, THREAD);
return jni()->CallStaticIntMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
JNIJVMCI::HotSpotJVMCIRuntime::encodeThrowable_method(),
return jni()->CallStaticIntMethod(JNIJVMCI::VMSupport::clazz(),
JNIJVMCI::VMSupport::encodeThrowable_method(),
_throwable, buffer, buffer_size);
}
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
JavaCallArguments jargs;
jargs.push_long(buffer);
JavaValue result(T_VOID);
JavaCalls::call_static(&result,
runtimeKlass,
vmSupport,
vmSymbols::decodeAndThrowThrowable_name(),
vmSymbols::long_void_signature(), &jargs, THREAD);
}
@ -416,31 +433,34 @@ void JVMCIEnv::translate_from_jni_exception(JavaThread* THREAD, jthrowable throw
SharedLibraryToHotSpotExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
}
jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env) {
if (is_hotspot()) {
if (HAS_PENDING_EXCEPTION) {
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
translate_to_jni_exception(THREAD, throwable, this, peer_env);
return true;
}
} else {
jthrowable ex = nullptr;
{
JNIAccessMark jni(this, THREAD);
ex = jni()->ExceptionOccurred();
if (ex != nullptr) {
jni()->ExceptionClear();
}
}
if (ex != nullptr) {
translate_from_jni_exception(THREAD, ex, peer_env, this);
return true;
}
jboolean JVMCIEnv::transfer_pending_exception_to_jni(JavaThread* THREAD, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
if (HAS_PENDING_EXCEPTION) {
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
translate_to_jni_exception(THREAD, throwable, hotspot_env, jni_env);
return true;
}
return false;
}
jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env) {
if (is_hotspot()) {
return transfer_pending_exception_to_jni(THREAD, this, peer_env);
}
jthrowable ex = nullptr;
{
JNIAccessMark jni(this, THREAD);
ex = jni()->ExceptionOccurred();
if (ex != nullptr) {
jni()->ExceptionClear();
}
}
if (ex != nullptr) {
translate_from_jni_exception(THREAD, ex, peer_env, this);
return true;
}
return false;
}
JVMCIEnv::~JVMCIEnv() {
if (_attach_threw_OOME) {
@ -469,7 +489,8 @@ JVMCIEnv::~JVMCIEnv() {
if (has_pending_exception()) {
char message[256];
jio_snprintf(message, 256, "Uncaught exception exiting JVMCIEnv scope entered at %s:%d", _file, _line);
jio_snprintf(message, 256, "Uncaught exception exiting %s JVMCIEnv scope entered at %s:%d",
is_hotspot() ? "HotSpot" : "libjvmci", _file, _line);
JVMCIRuntime::fatal_exception(this, message);
}

@ -180,6 +180,12 @@ class JVMCIEnv : public ResourceObj {
// The translated exception is pending in hotspot_env upon returning.
static void translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
// Used by copy_saved_properties() to avoid OutOfMemoryErrors when
// initializing a libjvmci runtime in low HotSpot heap conditions.
// Must hold JVMCI_lock when initializing.
static jbyte* _serialized_saved_properties;
static int _serialized_saved_properties_len;
public:
// Opens a JVMCIEnv scope for a Java to VM call (e.g., via CompilerToVM).
// An exception occurring within the scope is left pending when the
@ -221,9 +227,13 @@ public:
return _runtime;
}
// Initializes Services.savedProperties in the shared library by copying
// the values from the same field in the HotSpot heap.
void copy_saved_properties();
// Gets the serialized saved properties from the HotSpot heap.
// The length of the returned array is saved in `len`.
jbyte* get_serialized_saved_properties(int& len, TRAPS);
// Initializes Services.savedProperties in the shared library from the given
// properties in the format produced by `get_serialized_saved_properties`.
void copy_saved_properties(jbyte* properties, int properties_len, JVMCI_TRAPS);
jboolean has_pending_exception();
void clear_pending_exception();
@ -233,6 +243,11 @@ public:
// if a pending exception was transferred, false otherwise.
jboolean transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env);
// If there is a pending HotSpot exception, clears it and translates it to the shared library heap.
// The translated exception is pending in the shared library upon returning.
// 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 an exception and stack trace of a pending exception.
void describe_pending_exception(bool clear);

@ -346,7 +346,7 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
JVMCI_event_2(" jclass for %s = " PTR_FORMAT, current_class_name, p2i(k)); \
/* SVM guarantees that jclass handles to classes in a native image are also */ \
/* in the image. Further calling NewGlobalRef on such a handle returns a stable */ \
/* values across all JavaVMs executing on the same native image. */ \
/* value across all JavaVMs executing on the same native image. */ \
if (current != nullptr) { \
fatal("jclass for %s re-initialized: " PTR_FORMAT " -> " PTR_FORMAT, \
current_class_name, p2i(current), p2i(k)); \

@ -191,8 +191,6 @@
objectarray_field(HotSpotJVMCIRuntime, excludeFromJVMCICompilation, "[Ljava/lang/Module;") \
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, compileMethod, compileMethod_signature, (JVMCIObject runtime, JVMCIObject method, int entry_bci, jlong env, int id)) \
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, isGCSupported, int_bool_signature, (JVMCIObject runtime, int gcIdentifier)) \
jvmci_method(CallStaticBooleanMethod, GetStaticMethodID, call_static, bool, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable, jlong buffer, int buffer_size)) \
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, decodeAndThrowThrowable, long_void_signature, (jlong buffer)) \
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, bootstrapFinished, void_method_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
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)) \
@ -216,6 +214,10 @@
start_class(Class, java_lang_Class) \
jvmci_method(CallObjectMethod, GetMethodID, call_virtual, JVMCIObject, Class, getName, void_string_signature, (JVMCI_TRAPS)) \
end_class \
start_class(VMSupport, jdk_internal_vm_VMSupport) \
jvmci_method(CallStaticIntMethod, GetStaticMethodID, call_static, int, VMSupport, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable, jlong buffer, int buffer_size)) \
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, VMSupport, decodeAndThrowThrowable, long_void_signature, (jlong buffer)) \
end_class \
start_class(ArrayIndexOutOfBoundsException, java_lang_ArrayIndexOutOfBoundsException) \
jvmci_constructor(ArrayIndexOutOfBoundsException, "(Ljava/lang/String;)V") \
end_class \

@ -749,7 +749,7 @@ JVM_END
void JVMCIRuntime::call_getCompiler(TRAPS) {
THREAD_JVMCIENV(JavaThread::current());
JVMCIObject jvmciRuntime = JVMCIRuntime::get_HotSpotJVMCIRuntime(JVMCI_CHECK);
initialize(JVMCIENV);
initialize(JVMCI_CHECK);
JVMCIENV->call_HotSpotJVMCIRuntime_getCompiler(jvmciRuntime, JVMCI_CHECK);
}
@ -1315,7 +1315,7 @@ void JVMCIRuntime::initialize_HotSpotJVMCIRuntime(JVMCI_TRAPS) {
}
}
initialize(JVMCIENV);
initialize(JVMCI_CHECK);
// This should only be called in the context of the JVMCI class being initialized
JVMCIObject result = JVMCIENV->call_HotSpotJVMCIRuntime_runtime(JVMCI_CHECK);
@ -1370,12 +1370,17 @@ class JavaVMRefsInitialization: public StackObj {
}
};
void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
void JVMCIRuntime::initialize(JVMCI_TRAPS) {
// Check first without _lock
if (_init_state == fully_initialized) {
return;
}
JavaThread* THREAD = JavaThread::current();
int properties_len = 0;
jbyte* properties = NULL;
MutexLocker locker(_lock);
// Check again under _lock
if (_init_state == fully_initialized) {
@ -1397,7 +1402,6 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
{
MutexUnlocker unlock(_lock);
JavaThread* THREAD = JavaThread::current();
HandleMark hm(THREAD);
ResourceMark rm(THREAD);
{
@ -1441,7 +1445,12 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
DEBUG_ONLY(CodeInstaller::verify_bci_constants(JVMCIENV);)
if (!JVMCIENV->is_hotspot()) {
JVMCIENV->copy_saved_properties();
Handle properties_exception;
properties = JVMCIENV->get_serialized_saved_properties(properties_len, THREAD);
if (JVMCIEnv::transfer_pending_exception_to_jni(THREAD, nullptr, JVMCIENV)) {
return;
}
JVMCIENV->copy_saved_properties(properties, properties_len, JVMCI_CHECK);
}
}
@ -1484,7 +1493,7 @@ void JVMCIRuntime::initialize_JVMCI(JVMCI_TRAPS) {
}
JVMCIObject JVMCIRuntime::get_HotSpotJVMCIRuntime(JVMCI_TRAPS) {
initialize(JVMCIENV);
initialize(JVMCI_CHECK_(JVMCIObject()));
initialize_JVMCI(JVMCI_CHECK_(JVMCIObject()));
return _HotSpotJVMCIRuntime_instance;
}
@ -1992,11 +2001,21 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c
HandleMark hm(thread);
JVMCIObject receiver = get_HotSpotJVMCIRuntime(JVMCIENV);
if (JVMCIENV->has_pending_exception()) {
fatal_exception(JVMCIENV, "Exception during HotSpotJVMCIRuntime initialization");
if (PrintWarnings) {
ResourceMark rm(thread);
warning("HotSpotJVMCIRuntime initialization failed when compiling %s", method->name_and_sig_as_C_string());
JVMCIENV->describe_pending_exception(true);
}
compile_state->set_failure(false, "exception during HotSpotJVMCIRuntime initialization");
return;
}
JVMCIObject jvmci_method = JVMCIENV->get_jvmci_method(method, JVMCIENV);
if (JVMCIENV->has_pending_exception()) {
JVMCIENV->describe_pending_exception(true);
if (PrintWarnings) {
ResourceMark rm(thread);
warning("Error creating JVMCI wrapper for %s", method->name_and_sig_as_C_string());
JVMCIENV->describe_pending_exception(true);
}
compile_state->set_failure(false, "exception getting JVMCI wrapper method");
return;
}

@ -79,9 +79,6 @@
template(compileMethod_name, "compileMethod") \
template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)Ljdk/vm/ci/hotspot/HotSpotCompilationRequestResult;") \
template(isGCSupported_name, "isGCSupported") \
template(encodeThrowable_name, "encodeThrowable") \
template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
template(fromMetaspace_name, "fromMetaspace") \
template(method_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;") \
template(constantPool_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotConstantPool;") \

@ -1954,8 +1954,10 @@ bool Arguments::check_vm_args_consistency() {
if (status && EnableJVMCI) {
PropertyList_unique_add(&_system_properties, "jdk.internal.vm.ci.enabled", "true",
AddProperty, UnwriteableProperty, InternalProperty);
if (!create_numbered_module_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) {
return false;
if (ClassLoader::is_module_observable("jdk.internal.vm.ci")) {
if (!create_numbered_module_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) {
return false;
}
}
}
#endif

@ -4,7 +4,9 @@
*
* 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.
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@ -20,13 +22,14 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.vm.ci.hotspot;
package jdk.internal.vm;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
@ -35,31 +38,32 @@ import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import jdk.vm.ci.common.JVMCIError;
/**
* Support for translating exceptions between different runtime heaps.
* Support for translating exceptions between the HotSpot heap and libjvmci heap.
*/
@SuppressWarnings("serial")
final class TranslatedException extends Exception {
/**
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails due to an
* {@link OutOfMemoryError}.
* The value returned by {@link #encodeThrowable(Throwable)} when encoding
* fails due to an {@link OutOfMemoryError}.
*/
private static final byte[] FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES;
/**
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails for any reason
* other than {@link OutOfMemoryError}.
* The value returned by {@link #encodeThrowable(Throwable)} when encoding
* fails for any reason other than {@link OutOfMemoryError}.
*/
private static final byte[] FALLBACK_ENCODED_THROWABLE_BYTES;
static {
try {
FALLBACK_ENCODED_THROWABLE_BYTES = encodeThrowable(new TranslatedException("error during encoding", "<unknown>"), false);
FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES = encodeThrowable(new OutOfMemoryError(), false);
FALLBACK_ENCODED_THROWABLE_BYTES =
encodeThrowable(new TranslatedException("error during encoding",
"<unknown>"), false);
FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES =
encodeThrowable(new OutOfMemoryError(), false);
} catch (IOException e) {
throw new JVMCIError(e);
throw new InternalError(e);
}
}
@ -74,7 +78,8 @@ final class TranslatedException extends Exception {
}
/**
* No need to record an initial stack trace since it will be manually overwritten.
* No need to record an initial stack trace since
* it will be manually overwritten.
*/
@SuppressWarnings("sync-override")
@Override
@ -95,12 +100,14 @@ final class TranslatedException extends Exception {
}
/**
* Prints a stack trace for {@code throwable} and returns {@code true}. Used to print stack
* traces only when assertions are enabled.
* Prints a stack trace for {@code throwable} if the system property
* {@code "jdk.internal.vm.TranslatedException.debug"} is true.
*/
private static boolean printStackTrace(Throwable throwable) {
throwable.printStackTrace();
return true;
private static void debugPrintStackTrace(Throwable throwable) {
if (Boolean.getBoolean("jdk.internal.vm.TranslatedException.debug")) {
System.err.print("DEBUG: ");
throwable.printStackTrace();
}
}
private static Throwable initCause(Throwable throwable, Throwable cause) {
@ -109,7 +116,7 @@ final class TranslatedException extends Exception {
throwable.initCause(cause);
} catch (IllegalStateException e) {
// Cause could not be set or overwritten.
assert printStackTrace(e);
debugPrintStackTrace(e);
}
}
return throwable;
@ -120,7 +127,8 @@ final class TranslatedException extends Exception {
try {
Class<?> cls = Class.forName(className);
if (cause != null) {
// Handle known exception types whose cause must be set in the constructor
// Handle known exception types whose cause must
// be set in the constructor
if (cls == InvocationTargetException.class) {
return new InvocationTargetException(cause, message);
}
@ -129,10 +137,13 @@ final class TranslatedException extends Exception {
}
}
if (message == null) {
return initCause((Throwable) cls.getConstructor().newInstance(), cause);
Constructor<?> cons = cls.getConstructor();
return initCause((Throwable) cons.newInstance(), cause);
}
return initCause((Throwable) cls.getDeclaredConstructor(String.class).newInstance(message), cause);
Constructor<?> cons = cls.getDeclaredConstructor(String.class);
return initCause((Throwable) cons.newInstance(message), cause);
} catch (Throwable translationFailure) {
debugPrintStackTrace(translationFailure);
return initCause(new TranslatedException(message, className), cause);
}
}
@ -149,7 +160,7 @@ final class TranslatedException extends Exception {
* Encodes {@code throwable} including its stack and causes as a {@linkplain GZIPOutputStream
* compressed} byte array that can be decoded by {@link #decodeThrowable}.
*/
static byte[] encodeThrowable(Throwable throwable) throws Throwable {
static byte[] encodeThrowable(Throwable throwable) {
try {
return encodeThrowable(throwable, true);
} catch (OutOfMemoryError e) {
@ -159,7 +170,8 @@ final class TranslatedException extends Exception {
}
}
private static byte[] encodeThrowable(Throwable throwable, boolean withCauseAndStack) throws IOException {
private static byte[] encodeThrowable(Throwable throwable,
boolean withCauseAndStack) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(baos))) {
List<Throwable> throwables = new ArrayList<>();
@ -199,20 +211,20 @@ final class TranslatedException extends Exception {
}
/**
* Gets the stack of the current thread without the frames between this call and the one just
* below the frame of the first method in {@link CompilerToVM}. The chopped frames are for the
* VM call to {@link HotSpotJVMCIRuntime#decodeAndThrowThrowable}.
* Gets the stack of the current thread as of the first native method. The chopped
* frames are for the VM call to {@link VMSupport#decodeAndThrowThrowable}.
*/
private static StackTraceElement[] getMyStackTrace() {
StackTraceElement[] stack = new Exception().getStackTrace();
Exception ex = new Exception();
StackTraceElement[] stack = ex.getStackTrace();
for (int i = 0; i < stack.length; i++) {
StackTraceElement e = stack[i];
if (e.getClassName().equals(CompilerToVM.class.getName())) {
if (e.isNativeMethod()) {
return Arrays.copyOfRange(stack, i, stack.length);
}
}
// This should never happen but since we're in exception handling
// code, just return a safe value instead raising a nested exception.
// This should never happen but since this is exception handling
// code, be defensive instead raising a nested exception.
return new StackTraceElement[0];
}
@ -223,7 +235,8 @@ final class TranslatedException extends Exception {
* {@link #encodeThrowable}
*/
static Throwable decodeThrowable(byte[] encodedThrowable) {
try (DataInputStream dis = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(encodedThrowable)))) {
ByteArrayInputStream bais = new ByteArrayInputStream(encodedThrowable);
try (DataInputStream dis = new DataInputStream(new GZIPInputStream(bais))) {
Throwable cause = null;
Throwable throwable = null;
StackTraceElement[] myStack = getMyStackTrace();
@ -243,7 +256,13 @@ final class TranslatedException extends Exception {
String methodName = emptyAsNull(dis.readUTF());
String fileName = emptyAsNull(dis.readUTF());
int lineNumber = dis.readInt();
StackTraceElement ste = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName, lineNumber);
StackTraceElement ste = new StackTraceElement(classLoaderName,
moduleName,
moduleVersion,
className,
methodName,
fileName,
lineNumber);
if (ste.isNativeMethod()) {
// Best effort attempt to weave stack traces from two heaps into
@ -263,13 +282,18 @@ final class TranslatedException extends Exception {
while (myStackIndex < myStack.length) {
stackTrace[stackTraceIndex++] = myStack[myStackIndex++];
}
if (stackTraceIndex != stackTrace.length) {
// Remove null entries at end of stackTrace
stackTrace = Arrays.copyOf(stackTrace, stackTraceIndex);
}
throwable.setStackTrace(stackTrace);
cause = throwable;
}
return throwable;
} catch (Throwable translationFailure) {
assert printStackTrace(translationFailure);
return new TranslatedException("Error decoding exception: " + encodedThrowable, translationFailure.getClass().getName());
debugPrintStackTrace(translationFailure);
return new TranslatedException("Error decoding exception: " + encodedThrowable,
translationFailure.getClass().getName());
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, 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
@ -32,12 +32,17 @@ import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import jdk.internal.misc.VM;
import jdk.internal.misc.Unsafe;
/*
* Support class used by JVMTI and VM attach mechanism.
* Support class used by JVMCI, JVMTI and VM attach mechanism.
*/
public class VMSupport {
private static final Unsafe U = Unsafe.getUnsafe();
private static Properties agentProps = null;
/**
* Returns the agent properties.
*/
@ -51,13 +56,20 @@ public class VMSupport {
private static native Properties initAgentProperties(Properties props);
/**
* Write the given properties list to a byte array and return it. Properties with
* a key or value that is not a String is filtered out. The stream written to the byte
* array is ISO 8859-1 encoded.
* Writes the given properties list to a byte array and return it. The stream written
* to the byte array is ISO 8859-1 encoded.
*/
private static byte[] serializePropertiesToByteArray(Properties p) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
p.store(out, null);
return out.toByteArray();
}
/**
* @returns a Properties object containing only the entries in {@code p}
* whose key and value are both Strings
*/
private static Properties onlyStrings(Properties p) {
Properties props = new Properties();
// stringPropertyNames() returns a snapshot of the property keys
@ -66,17 +78,28 @@ public class VMSupport {
String value = p.getProperty(key);
props.put(key, value);
}
props.store(out, null);
return out.toByteArray();
return props;
}
public static byte[] serializePropertiesToByteArray() throws IOException {
return serializePropertiesToByteArray(System.getProperties());
return serializePropertiesToByteArray(onlyStrings(System.getProperties()));
}
public static byte[] serializeAgentPropertiesToByteArray() throws IOException {
return serializePropertiesToByteArray(getAgentProperties());
return serializePropertiesToByteArray(onlyStrings(getAgentProperties()));
}
/**
* Serializes {@link VM#getSavedProperties()} to a byte array.
*
* Used by JVMCI to copy properties into libjvmci.
*/
public static byte[] serializeSavedPropertiesToByteArray() throws IOException {
Properties props = new Properties();
for (var e : VM.getSavedProperties().entrySet()) {
props.put(e.getKey(), e.getValue());
}
return serializePropertiesToByteArray(props);
}
/*
@ -88,4 +111,40 @@ public class VMSupport {
* variables such as java.io.tmpdir.
*/
public static native String getVMTemporaryDirectory();
/**
* Decodes the exception encoded in {@code buffer} and throws it.
*
* @param buffer a native byte buffer containing an exception encoded by
* {@link #encodeThrowable}
*/
public static void decodeAndThrowThrowable(long buffer) throws Throwable {
int encodingLength = U.getInt(buffer);
byte[] encoding = new byte[encodingLength];
U.copyMemory(null, buffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
throw TranslatedException.decodeThrowable(encoding);
}
/**
* If {@code bufferSize} is large enough, encodes {@code throwable} into a byte array and writes
* it to {@code buffer}. The encoding in {@code buffer} can be decoded by
* {@link #decodeAndThrowThrowable}.
*
* @param throwable the exception to encode
* @param buffer a native byte buffer
* @param bufferSize the size of {@code buffer} in bytes
* @return the number of bytes written into {@code buffer} if {@code bufferSize} is large
* enough, otherwise {@code -N} where {@code N} is the value {@code bufferSize} needs to
* be to fit the encoding
*/
public static int encodeThrowable(Throwable throwable, long buffer, int bufferSize) {
byte[] encoding = TranslatedException.encodeThrowable(throwable);
int requiredSize = 4 + encoding.length;
if (bufferSize < requiredSize) {
return -requiredSize;
}
U.putInt(buffer, encoding.length);
U.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
return requiredSize;
}
}

@ -206,64 +206,6 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
return result;
}
/**
* Decodes the exception encoded in {@code buffer} and throws it.
*
* @param errorOrBuffer an error code or a native byte buffer containing an exception encoded by
* {@link #encodeThrowable}. Error code values and their meanings are:
*
* <pre>
* 0: native memory for the buffer could not be allocated
* -1: an OutOfMemoryError was thrown while encoding the exception
* -2: some other throwable was thrown while encoding the exception
* </pre>
*/
@VMEntryPoint
static void decodeAndThrowThrowable(long errorOrBuffer) throws Throwable {
if (errorOrBuffer >= -2L && errorOrBuffer <= 0) {
String context = String.format("while encoding an exception to translate it from %s to %s",
IS_IN_NATIVE_IMAGE ? "HotSpot" : "libjvmci",
IS_IN_NATIVE_IMAGE ? "libjvmci" : "HotSpot");
if (errorOrBuffer == 0) {
throw new InternalError("native buffer could not be allocated " + context);
}
if (errorOrBuffer == -1L) {
throw new OutOfMemoryError("OutOfMemoryError occurred " + context);
}
throw new InternalError("unexpected problem occurred " + context);
}
Unsafe unsafe = UnsafeAccess.UNSAFE;
int encodingLength = unsafe.getInt(errorOrBuffer);
byte[] encoding = new byte[encodingLength];
unsafe.copyMemory(null, errorOrBuffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
throw TranslatedException.decodeThrowable(encoding);
}
/**
* If {@code bufferSize} is large enough, encodes {@code throwable} into a byte array and writes
* it to {@code buffer}. The encoding in {@code buffer} can be decoded by
* {@link #decodeAndThrowThrowable}.
*
* @param throwable the exception to encode
* @param buffer a native byte buffer
* @param bufferSize the size of {@code buffer} in bytes
* @return the number of bytes written into {@code buffer} if {@code bufferSize} is large
* enough, otherwise {@code -N} where {@code N} is the value {@code bufferSize} needs to
* be to fit the encoding
*/
@VMEntryPoint
static int encodeThrowable(Throwable throwable, long buffer, int bufferSize) throws Throwable {
byte[] encoding = TranslatedException.encodeThrowable(throwable);
int requiredSize = 4 + encoding.length;
if (bufferSize < requiredSize) {
return -requiredSize;
}
Unsafe unsafe = UnsafeAccess.UNSAFE;
unsafe.putInt(buffer, encoding.length);
unsafe.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
return requiredSize;
}
@VMEntryPoint
static String callToString(Object o) {
return o.toString();
@ -1330,7 +1272,7 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
for (String filter : filters) {
Matcher m = FORCE_TRANSLATE_FAILURE_FILTER_RE.matcher(filter);
if (!m.matches()) {
throw new JVMCIError(Option.ForceTranslateFailure + " filter does not match " + FORCE_TRANSLATE_FAILURE_FILTER_RE + ": " + filter);
throw new IllegalArgumentException(Option.ForceTranslateFailure + " filter does not match " + FORCE_TRANSLATE_FAILURE_FILTER_RE + ": " + filter);
}
String typeSelector = m.group(1);
String substring = m.group(2);
@ -1350,7 +1292,7 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
continue;
}
if (toMatch.contains(substring)) {
throw new JVMCIError("translation of " + translatedObject + " failed due to matching " + Option.ForceTranslateFailure + " filter \"" + filter + "\"");
throw new RuntimeException("translation of " + translatedObject + " failed due to matching " + Option.ForceTranslateFailure + " filter \"" + filter + "\"");
}
}
}

@ -23,9 +23,6 @@
package jdk.vm.ci.services;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@ -34,6 +31,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Consumer;
@ -42,8 +40,6 @@ import java.util.function.Supplier;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.VM;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Provides utilities needed by JVMCI clients.
*/
@ -266,109 +262,21 @@ public final class Services {
}
/**
* A Java {@code char} has a maximal UTF8 length of 3.
*/
private static final int MAX_UNICODE_IN_UTF8_LENGTH = 3;
/**
* {@link DataOutputStream#writeUTF(String)} only supports values whose UTF8 encoding length is
* less than 65535.
*/
private static final int MAX_UTF8_PROPERTY_STRING_LENGTH = 65535 / MAX_UNICODE_IN_UTF8_LENGTH;
/**
* Serializes the {@linkplain #getSavedProperties() saved system properties} to a byte array for
* the purpose of {@linkplain #initializeSavedProperties(byte[]) initializing} the initial
* properties in the JVMCI shared library.
*/
@VMEntryPoint
private static byte[] serializeSavedProperties() throws IOException {
if (IS_IN_NATIVE_IMAGE) {
throw new InternalError("Can only serialize saved properties in HotSpot runtime");
}
return serializeProperties(Services.getSavedProperties());
}
private static byte[] serializeProperties(Map<String, String> props) throws IOException {
// Compute size of output on the assumption that
// all system properties have ASCII names and values
int estimate = 4 + 4;
int nonUtf8Props = 0;
for (Map.Entry<String, String> e : props.entrySet()) {
String name = e.getKey();
String value = e.getValue();
estimate += (2 + (name.length())) + (2 + (value.length()));
if (name.length() > MAX_UTF8_PROPERTY_STRING_LENGTH || value.length() > MAX_UTF8_PROPERTY_STRING_LENGTH) {
nonUtf8Props++;
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(estimate);
DataOutputStream out = new DataOutputStream(baos);
out.writeInt(props.size() - nonUtf8Props);
out.writeInt(nonUtf8Props);
for (Map.Entry<String, String> e : props.entrySet()) {
String name = e.getKey();
String value = e.getValue();
if (name.length() <= MAX_UTF8_PROPERTY_STRING_LENGTH && value.length() <= MAX_UTF8_PROPERTY_STRING_LENGTH) {
out.writeUTF(name);
out.writeUTF(value);
}
}
if (nonUtf8Props != 0) {
for (Map.Entry<String, String> e : props.entrySet()) {
String name = e.getKey();
String value = e.getValue();
if (name.length() > MAX_UTF8_PROPERTY_STRING_LENGTH || value.length() > MAX_UTF8_PROPERTY_STRING_LENGTH) {
byte[] utf8Name = name.getBytes(UTF_8);
byte[] utf8Value = value.getBytes(UTF_8);
out.writeInt(utf8Name.length);
out.write(utf8Name);
out.writeInt(utf8Value.length);
out.write(utf8Value);
}
}
}
return baos.toByteArray();
}
/**
* Initialized the {@linkplain #getSavedProperties() saved system properties} in the JVMCI
* shared library from the {@linkplain #serializeSavedProperties() serialized saved properties}
* in the HotSpot runtime.
* Initializes {@link #savedProperties} from the byte array returned by
* {@code jdk.internal.vm.VMSupport.serializeSavedPropertiesToByteArray()}.
*/
@VMEntryPoint
private static void initializeSavedProperties(byte[] serializedProperties) throws IOException {
if (!IS_IN_NATIVE_IMAGE) {
throw new InternalError("Can only initialize saved properties in JVMCI shared library runtime");
}
savedProperties = Collections.unmodifiableMap(deserializeProperties(serializedProperties));
}
private static Map<String, String> deserializeProperties(byte[] serializedProperties) throws IOException {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(serializedProperties));
int utf8Props = in.readInt();
int nonUtf8Props = in.readInt();
Map<String, String> props = new HashMap<>(utf8Props + nonUtf8Props);
int index = 0;
while (in.available() != 0) {
if (index < utf8Props) {
String name = in.readUTF();
String value = in.readUTF();
props.put(name, value);
} else {
int nameLen = in.readInt();
byte[] nameBytes = new byte[nameLen];
in.read(nameBytes);
int valueLen = in.readInt();
byte[] valueBytes = new byte[valueLen];
in.read(valueBytes);
String name = new String(nameBytes, UTF_8);
String value = new String(valueBytes, UTF_8);
props.put(name, value);
}
index++;
Properties props = new Properties();
props.load(new ByteArrayInputStream(serializedProperties));
Map<String, String> map = new HashMap<>(props.size());
for (var e : props.entrySet()) {
map.put((String) e.getKey(), (String) e.getValue());
}
return props;
savedProperties = Collections.unmodifiableMap(map);
}
}

@ -1,93 +0,0 @@
/*
* Copyright (c) 2019, 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
* @requires vm.jvmci
* @modules jdk.internal.vm.ci/jdk.vm.ci.services:+open
* @library /compiler/jvmci/jdk.vm.ci.hotspot.test/src
* @run testng/othervm
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
* jdk.vm.ci.hotspot.test.TestServices
*/
package jdk.vm.ci.hotspot.test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.testng.Assert;
import org.testng.annotations.Test;
import jdk.vm.ci.services.Services;
public class TestServices {
@SuppressWarnings("unchecked")
@Test
public void serializeSavedPropertiesTest() throws Exception {
Field f = Services.class.getDeclaredField("MAX_UTF8_PROPERTY_STRING_LENGTH");
f.setAccessible(true);
int maxUtf8PropertyStringLength = (int) f.get(null);
Method serializeProperties = Services.class.getDeclaredMethod("serializeProperties", Map.class);
Method deserializeProperties = Services.class.getDeclaredMethod("deserializeProperties", byte[].class);
serializeProperties.setAccessible(true);
deserializeProperties.setAccessible(true);
Map<String, String> props = new HashMap<>(Services.getSavedProperties());
String[] names = {
new String(new char[maxUtf8PropertyStringLength - 100]).replace('\0', 'x'),
new String(new char[maxUtf8PropertyStringLength - 1]).replace('\0', 'x'),
new String(new char[maxUtf8PropertyStringLength]).replace('\0', 'y'),
new String(new char[maxUtf8PropertyStringLength + 1]).replace('\0', 'z'),
new String(new char[maxUtf8PropertyStringLength + 100]).replace('\0', 'z')
};
String[] values = {
new String(new char[maxUtf8PropertyStringLength - 100]).replace('\0', '1'),
new String(new char[maxUtf8PropertyStringLength - 1]).replace('\0', '1'),
new String(new char[maxUtf8PropertyStringLength]).replace('\0', '2'),
new String(new char[maxUtf8PropertyStringLength + 1]).replace('\0', '1'),
new String(new char[maxUtf8PropertyStringLength + 100]).replace('\0', '3')
};
for (String name : names) {
for (String value : values) {
props.put(name, value);
}
}
byte[] data = (byte[]) serializeProperties.invoke(null, props);
Map<String, String> newProps = (Map<String, String>) deserializeProperties.invoke(null, data);
Assert.assertEquals(props.size(), newProps.size());
for (String name : props.keySet()) {
String expect = props.get(name);
String actual = newProps.get(name);
Assert.assertEquals(expect, actual);
}
}
}

@ -23,16 +23,12 @@
/*
* @test
* @requires vm.jvmci
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot:+open
* @modules java.base/jdk.internal.vm
* java.base/jdk.internal.misc
* @library /compiler/jvmci/jdk.vm.ci.hotspot.test/src
* @run testng/othervm
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
* jdk.vm.ci.hotspot.test.TestTranslatedException
* jdk.internal.vm.test.TestTranslatedException
*/
package jdk.vm.ci.hotspot.test;
package jdk.internal.vm.test;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
@ -43,7 +39,7 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.internal.vm.VMSupport;
public class TestTranslatedException {
@SuppressWarnings("serial")
@ -56,52 +52,37 @@ public class TestTranslatedException {
@SuppressWarnings("unchecked")
@Test
public void encodeDecodeTest() throws Exception {
Class<?> translatedExceptionClass = Class.forName("jdk.vm.ci.hotspot.TranslatedException");
Method encode = translatedExceptionClass.getDeclaredMethod("encodeThrowable", Throwable.class);
Method decode = translatedExceptionClass.getDeclaredMethod("decodeThrowable", byte[].class);
encode.setAccessible(true);
decode.setAccessible(true);
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
for (int i = 0; i < 10; i++) {
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
}
byte[] encoding = (byte[]) encode.invoke(null, throwable);
Throwable decoded = (Throwable) decode.invoke(null, encoding);
assertThrowableEquals(throwable, decoded);
encodeDecode(throwable);
}
@SuppressWarnings("unchecked")
@Test
public void encodeDecodeTest2() throws Exception {
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
for (int i = 0; i < 10; i++) {
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
}
encodeDecode(throwable);
}
private void encodeDecode(Throwable throwable) throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();
int bufferSize = 512;
long buffer = 0L;
while (true) {
buffer = unsafe.allocateMemory(bufferSize);
try {
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
for (int i = 0; i < 10; i++) {
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
}
Method encode = HotSpotJVMCIRuntime.class.getDeclaredMethod("encodeThrowable", Throwable.class, long.class, int.class);
Method decode = HotSpotJVMCIRuntime.class.getDeclaredMethod("decodeAndThrowThrowable", long.class);
encode.setAccessible(true);
decode.setAccessible(true);
int res = (Integer) encode.invoke(null, throwable, buffer, bufferSize);
int res = VMSupport.encodeThrowable(throwable, buffer, bufferSize);
if (res < 0) {
bufferSize = -res;
} else {
try {
decode.invoke(null, buffer);
VMSupport.decodeAndThrowThrowable(buffer);
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
} catch (InvocationTargetException e) {
Throwable decoded = e.getCause();
} catch (Throwable decoded) {
assertThrowableEquals(throwable, decoded);
}
return;
@ -117,12 +98,12 @@ public class TestTranslatedException {
Assert.assertEquals(original == null, decoded == null);
while (original != null) {
if (Untranslatable.class.equals(original.getClass())) {
Assert.assertEquals("jdk.vm.ci.hotspot.TranslatedException", decoded.getClass().getName());
Assert.assertEquals("jdk.vm.ci.hotspot.TranslatedException[jdk.vm.ci.hotspot.test.TestTranslatedException$Untranslatable]: test exception", decoded.toString());
Assert.assertEquals("test exception", original.getMessage());
Assert.assertEquals(decoded.getClass().getName(), "jdk.internal.vm.TranslatedException");
Assert.assertEquals(decoded.toString(), "jdk.internal.vm.TranslatedException[jdk.internal.vm.test.TestTranslatedException$Untranslatable]: test exception");
Assert.assertEquals(original.getMessage(), "test exception");
} else {
Assert.assertEquals(original.getClass().getName(), decoded.getClass().getName());
Assert.assertEquals(original.getMessage(), decoded.getMessage());
Assert.assertEquals(decoded.getClass().getName(), original.getClass().getName());
Assert.assertEquals(decoded.getMessage(), original.getMessage());
}
StackTraceElement[] originalStack = original.getStackTrace();
StackTraceElement[] decodedStack = decoded.getStackTrace();
@ -130,12 +111,12 @@ public class TestTranslatedException {
for (int i = 0, n = originalStack.length; i < n; ++i) {
StackTraceElement originalStackElement = originalStack[i];
StackTraceElement decodedStackElement = decodedStack[i];
Assert.assertEquals(originalStackElement.getClassLoaderName(), decodedStackElement.getClassLoaderName());
Assert.assertEquals(originalStackElement.getModuleName(), decodedStackElement.getModuleName());
Assert.assertEquals(originalStackElement.getClassName(), decodedStackElement.getClassName());
Assert.assertEquals(originalStackElement.getMethodName(), decodedStackElement.getMethodName());
Assert.assertEquals(originalStackElement.getFileName(), decodedStackElement.getFileName());
Assert.assertEquals(originalStackElement.getLineNumber(), decodedStackElement.getLineNumber());
Assert.assertEquals(decodedStackElement.getClassLoaderName(), originalStackElement.getClassLoaderName());
Assert.assertEquals(decodedStackElement.getModuleName(), originalStackElement.getModuleName());
Assert.assertEquals(decodedStackElement.getClassName(), originalStackElement.getClassName());
Assert.assertEquals(decodedStackElement.getMethodName(), originalStackElement.getMethodName());
Assert.assertEquals(decodedStackElement.getFileName(), originalStackElement.getFileName());
Assert.assertEquals(decodedStackElement.getLineNumber(), originalStackElement.getLineNumber());
}
original = original.getCause();
decoded = decoded.getCause();