| /* |
| * Copyright (c) 2019, 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. |
| */ |
| |
| #include "precompiled.hpp" |
| #include "classfile/systemDictionary.hpp" |
| #include "compiler/compileTask.hpp" |
| #include "compiler/compilerThread.hpp" |
| #include "gc/shared/collectedHeap.hpp" |
| #include "jvmci/jvmci.hpp" |
| #include "jvmci/jvmciJavaClasses.hpp" |
| #include "jvmci/jvmciEnv.hpp" |
| #include "jvmci/jvmciRuntime.hpp" |
| #include "jvmci/metadataHandles.hpp" |
| #include "memory/resourceArea.hpp" |
| #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" |
| |
| JVMCIRuntime* JVMCI::_compiler_runtimes = nullptr; |
| JVMCIRuntime* JVMCI::_java_runtime = nullptr; |
| JVMCIRuntime* JVMCI::_shutdown_compiler_runtime = nullptr; |
| volatile bool JVMCI::_is_initialized = false; |
| bool JVMCI::_box_caches_initialized = false; |
| void* JVMCI::_shared_library_handle = nullptr; |
| char* JVMCI::_shared_library_path = nullptr; |
| volatile bool JVMCI::_in_shutdown = false; |
| StringEventLog* JVMCI::_events = nullptr; |
| StringEventLog* JVMCI::_verbose_events = nullptr; |
| volatile intx JVMCI::_fatal_log_init_thread = -1; |
| volatile int JVMCI::_fatal_log_fd = -1; |
| const char* JVMCI::_fatal_log_filename = nullptr; |
| |
| void jvmci_vmStructs_init() NOT_DEBUG_RETURN; |
| |
| bool JVMCI::can_initialize_JVMCI() { |
| // Initializing JVMCI requires the module system to be initialized past phase 3. |
| // The JVMCI API itself isn't available until phase 2 and ServiceLoader (which |
| // JVMCI initialization requires) isn't usable until after phase 3. Testing |
| // whether the system loader is initialized satisfies all these invariants. |
| if (SystemDictionary::java_system_loader() == nullptr) { |
| return false; |
| } |
| assert(Universe::is_module_initialized(), "must be"); |
| return true; |
| } |
| |
| void* JVMCI::get_shared_library(char*& path, bool load) { |
| void* sl_handle = _shared_library_handle; |
| if (sl_handle != nullptr || !load) { |
| path = _shared_library_path; |
| return sl_handle; |
| } |
| MutexLocker locker(JVMCI_lock); |
| path = nullptr; |
| if (_shared_library_handle == nullptr) { |
| char path[JVM_MAXPATHLEN]; |
| char ebuf[1024]; |
| if (JVMCILibPath != nullptr) { |
| if (!os::dll_locate_lib(path, sizeof(path), JVMCILibPath, JVMCI_SHARED_LIBRARY_NAME)) { |
| fatal("Unable to create path to JVMCI shared library based on value of JVMCILibPath (%s)", JVMCILibPath); |
| } |
| } else { |
| if (!os::dll_locate_lib(path, sizeof(path), Arguments::get_dll_dir(), JVMCI_SHARED_LIBRARY_NAME)) { |
| fatal("Unable to create path to JVMCI shared library"); |
| } |
| } |
| |
| void* handle = os::dll_load(path, ebuf, sizeof ebuf); |
| if (handle == nullptr) { |
| fatal("Unable to load JVMCI shared library from %s: %s", path, ebuf); |
| } |
| _shared_library_handle = handle; |
| _shared_library_path = os::strdup(path); |
| |
| JVMCI_event_1("loaded JVMCI shared library from %s", path); |
| } |
| path = _shared_library_path; |
| return _shared_library_handle; |
| } |
| |
| void JVMCI::initialize_compiler(TRAPS) { |
| if (JVMCILibDumpJNIConfig) { |
| JNIJVMCI::initialize_ids(nullptr); |
| ShouldNotReachHere(); |
| } |
| JVMCIRuntime* runtime; |
| if (UseJVMCINativeLibrary) { |
| runtime = JVMCI::compiler_runtime(THREAD); |
| } else { |
| runtime = JVMCI::java_runtime(); |
| } |
| runtime->call_getCompiler(CHECK); |
| } |
| |
| void JVMCI::initialize_globals() { |
| jvmci_vmStructs_init(); |
| if (LogEvents) { |
| if (JVMCIEventLogLevel > 0) { |
| _events = new StringEventLog("JVMCI Events", "jvmci"); |
| if (JVMCIEventLogLevel > 1) { |
| int count = LogEventsBufferEntries; |
| for (int i = 1; i < JVMCIEventLogLevel && i < max_EventLog_level; i++) { |
| // Expand event buffer by 10x for each level above 1 |
| count = count * 10; |
| } |
| _verbose_events = new StringEventLog("Verbose JVMCI Events", "verbose-jvmci", count); |
| } |
| } |
| } |
| _java_runtime = new JVMCIRuntime(nullptr, -1, false); |
| if (using_singleton_shared_library_runtime()) { |
| JVMCI::_compiler_runtimes = new JVMCIRuntime(nullptr, 0, true); |
| } |
| } |
| |
| void JVMCI::ensure_box_caches_initialized(TRAPS) { |
| if (_box_caches_initialized) { |
| return; |
| } |
| |
| // While multiple threads may reach here, that's fine |
| // since class initialization is synchronized. |
| Symbol* box_classes[] = { |
| java_lang_Boolean::symbol(), |
| java_lang_Byte_ByteCache::symbol(), |
| java_lang_Short_ShortCache::symbol(), |
| java_lang_Character_CharacterCache::symbol(), |
| java_lang_Integer_IntegerCache::symbol(), |
| java_lang_Long_LongCache::symbol() |
| }; |
| |
| for (unsigned i = 0; i < sizeof(box_classes) / sizeof(Symbol*); i++) { |
| Klass* k = SystemDictionary::resolve_or_fail(box_classes[i], true, CHECK); |
| InstanceKlass* ik = InstanceKlass::cast(k); |
| if (ik->is_not_initialized()) { |
| ik->initialize(CHECK); |
| } |
| } |
| _box_caches_initialized = true; |
| } |
| |
| JVMCIRuntime* JVMCI::compiler_runtime(JavaThread* thread, bool create) { |
| assert(thread->is_Java_thread(), "must be") ; |
| assert(UseJVMCINativeLibrary, "must be"); |
| JVMCIRuntime* runtime = thread->libjvmci_runtime(); |
| if (runtime == nullptr && create) { |
| runtime = JVMCIRuntime::for_thread(thread); |
| } |
| return runtime; |
| } |
| |
| JavaThread* JVMCI::compilation_tick(JavaThread* thread) { |
| if (thread->is_Compiler_thread()) { |
| CompileTask *task = CompilerThread::cast(thread)->task(); |
| if (task != nullptr) { |
| JVMCICompileState *state = task->blocking_jvmci_compile_state(); |
| if (state != nullptr) { |
| state->inc_compilation_ticks(); |
| } |
| } |
| } |
| return thread; |
| } |
| |
| void JVMCI::metadata_do(void f(Metadata*)) { |
| if (_java_runtime != nullptr) { |
| _java_runtime->_metadata_handles->metadata_do(f); |
| } |
| for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) { |
| runtime->_metadata_handles->metadata_do(f); |
| } |
| if (_shutdown_compiler_runtime != nullptr) { |
| _shutdown_compiler_runtime->_metadata_handles->metadata_do(f); |
| } |
| } |
| |
| void JVMCI::do_unloading(bool unloading_occurred) { |
| if (unloading_occurred) { |
| if (_java_runtime != nullptr) { |
| _java_runtime->_metadata_handles->do_unloading(); |
| } |
| for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) { |
| runtime->_metadata_handles->do_unloading(); |
| } |
| if (_shutdown_compiler_runtime != nullptr) { |
| _shutdown_compiler_runtime->_metadata_handles->do_unloading(); |
| } |
| } |
| } |
| |
| bool JVMCI::is_compiler_initialized() { |
| return _is_initialized; |
| } |
| |
| void JVMCI::vlog(int level, const char* format, va_list ap) { |
| if (LogEvents && JVMCIEventLogLevel >= level) { |
| StringEventLog* events = level == 1 ? _events : _verbose_events; |
| guarantee(events != nullptr, "JVMCI event log not yet initialized"); |
| Thread* thread = Thread::current_or_null_safe(); |
| if (thread != nullptr) { |
| events->logv(thread, format, 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 && 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[" PTR_FORMAT " \"%s\"]:%*c", level, p2i(thread), 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[" PTR_FORMAT " <%s>]:%*c", level, p2i(thread), thread->type_name(), level, ' '); |
| } |
| } else { |
| tty->print("JVMCITrace-%d[?]:%*c", level, level, ' '); |
| } |
| tty->vprint_cr(format, ap); |
| } |
| } |
| |
| #define LOG_TRACE(level) { va_list ap; \ |
| va_start(ap, format); vlog(level, format, ap); va_end(ap); \ |
| va_start(ap, format); vtrace(level, format, ap); va_end(ap); \ |
| } |
| |
| void JVMCI::event(int level, const char* format, ...) LOG_TRACE(level) |
| void JVMCI::event1(const char* format, ...) LOG_TRACE(1) |
| void JVMCI::event2(const char* format, ...) LOG_TRACE(2) |
| void JVMCI::event3(const char* format, ...) LOG_TRACE(3) |
| void JVMCI::event4(const char* format, ...) LOG_TRACE(4) |
| |
| #undef LOG_TRACE |
| |
| void JVMCI::shutdown(JavaThread* thread) { |
| ResourceMark rm; |
| { |
| MutexLocker locker(JVMCI_lock); |
| _in_shutdown = true; |
| JVMCI_event_1("shutting down JVMCI"); |
| } |
| JVMCIRuntime* java_runtime = _java_runtime; |
| if (java_runtime != nullptr) { |
| java_runtime->shutdown(); |
| } |
| JVMCIRuntime* runtime = thread->libjvmci_runtime(); |
| if (runtime != nullptr) { |
| runtime->detach_thread(thread, "JVMCI shutdown"); |
| } |
| { |
| // Attach to JVMCI initialized runtimes that are not already shutting down |
| // and shut them down. This ensures HotSpotJVMCIRuntime.shutdown() is called |
| // for each JVMCI runtime. |
| MutexLocker locker(JVMCI_lock); |
| for (JVMCIRuntime* rt = JVMCI::_compiler_runtimes; rt != nullptr; rt = rt->_next) { |
| if (rt->is_HotSpotJVMCIRuntime_initialized() && rt->_num_attached_threads != JVMCIRuntime::cannot_be_attached) { |
| rt->_num_attached_threads++; |
| { |
| MutexUnlocker unlocker(JVMCI_lock); |
| rt->attach_thread(thread); |
| rt->shutdown(); |
| rt->detach_thread(thread, "JVMCI shutdown"); |
| } |
| } |
| } |
| } |
| } |
| |
| bool JVMCI::in_shutdown() { |
| return _in_shutdown; |
| } |
| |
| void JVMCI::fatal_log(const char* buf, size_t count) { |
| intx current_thread_id = os::current_thread_id(); |
| intx invalid_id = -1; |
| int log_fd; |
| if (_fatal_log_init_thread == invalid_id && Atomic::cmpxchg(&_fatal_log_init_thread, invalid_id, current_thread_id) == invalid_id) { |
| if (ErrorFileToStdout) { |
| log_fd = 1; |
| } else if (ErrorFileToStderr) { |
| log_fd = 2; |
| } else { |
| static char name_buffer[O_BUFLEN]; |
| log_fd = VMError::prepare_log_file(JVMCINativeLibraryErrorFile, LIBJVMCI_ERR_FILE, true, name_buffer, sizeof(name_buffer)); |
| if (log_fd != -1) { |
| _fatal_log_filename = name_buffer; |
| } else { |
| int e = errno; |
| tty->print("Can't open JVMCI shared library error report file. Error: "); |
| tty->print_raw_cr(os::strerror(e)); |
| tty->print_cr("JVMCI shared library error report will be written to console."); |
| |
| // See notes in VMError::report_and_die about hard coding tty to 1 |
| log_fd = 1; |
| } |
| } |
| _fatal_log_fd = log_fd; |
| } else { |
| // Another thread won the race to initialize the stream. Give it time |
| // to complete initialization. VM locks cannot be used as the current |
| // thread might not be attached to the VM (e.g. a native thread started |
| // within libjvmci). |
| while (_fatal_log_fd == -1) { |
| os::naked_short_sleep(50); |
| } |
| } |
| fdStream log(_fatal_log_fd); |
| log.write(buf, count); |
| log.flush(); |
| } |