8316694: Implement relocation of nmethod within CodeCache

Reviewed-by: kvn, eosterlund, never, eastigeevich, bulasevich
This commit is contained in:
Chad Rakoczy 2025-10-04 21:17:26 +00:00 committed by Evgeny Astigeevich
parent 76dba201fa
commit f740cd2aad
26 changed files with 1591 additions and 64 deletions

View File

@ -90,7 +90,6 @@ void Relocation::pd_set_call_destination(address x) {
void trampoline_stub_Relocation::pd_fix_owner_after_move() {
NativeCall* call = nativeCall_at(owner());
assert(call->raw_destination() == owner(), "destination should be empty");
address trampoline = addr();
address dest = nativeCallTrampolineStub_at(trampoline)->destination();
if (!Assembler::reachable_from_branch_at(owner(), dest)) {

View File

@ -23,23 +23,24 @@
*/
#include "code/codeBehaviours.hpp"
#include "code/nmethod.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
CompiledICProtectionBehaviour* CompiledICProtectionBehaviour::_current = nullptr;
bool DefaultICProtectionBehaviour::lock(nmethod* method) {
if (is_safe(method)) {
bool DefaultICProtectionBehaviour::lock(nmethod* nm) {
if (is_safe(nm)) {
return false;
}
CompiledIC_lock->lock_without_safepoint_check();
return true;
}
void DefaultICProtectionBehaviour::unlock(nmethod* method) {
void DefaultICProtectionBehaviour::unlock(nmethod* nm) {
CompiledIC_lock->unlock();
}
bool DefaultICProtectionBehaviour::is_safe(nmethod* method) {
return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self();
bool DefaultICProtectionBehaviour::is_safe(nmethod* nm) {
return SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->owned_by_self() || (NMethodState_lock->owned_by_self() && nm->is_not_installed());
}

View File

@ -33,18 +33,18 @@ class CompiledICProtectionBehaviour {
static CompiledICProtectionBehaviour* _current;
public:
virtual bool lock(nmethod* method) = 0;
virtual void unlock(nmethod* method) = 0;
virtual bool is_safe(nmethod* method) = 0;
virtual bool lock(nmethod* nm) = 0;
virtual void unlock(nmethod* nm) = 0;
virtual bool is_safe(nmethod* nm) = 0;
static CompiledICProtectionBehaviour* current() { return _current; }
static void set_current(CompiledICProtectionBehaviour* current) { _current = current; }
};
class DefaultICProtectionBehaviour: public CompiledICProtectionBehaviour, public CHeapObj<mtInternal> {
virtual bool lock(nmethod* method);
virtual void unlock(nmethod* method);
virtual bool is_safe(nmethod* method);
virtual bool lock(nmethod* nm);
virtual void unlock(nmethod* nm);
virtual bool is_safe(nmethod* nm);
};
#endif // SHARE_CODE_CODEBEHAVIOURS_HPP

View File

@ -259,7 +259,7 @@ class CodeCache : AllStatic {
static bool heap_available(CodeBlobType code_blob_type);
// Returns the CodeBlobType for the given nmethod
static CodeBlobType get_code_blob_type(nmethod* nm) {
static CodeBlobType get_code_blob_type(const nmethod* nm) {
return get_code_heap(nm)->code_blob_type();
}

View File

@ -55,8 +55,8 @@ CompiledICLocker::~CompiledICLocker() {
}
}
bool CompiledICLocker::is_safe(nmethod* method) {
return CompiledICProtectionBehaviour::current()->is_safe(method);
bool CompiledICLocker::is_safe(nmethod* nm) {
return CompiledICProtectionBehaviour::current()->is_safe(nm);
}
bool CompiledICLocker::is_safe(address code) {

View File

@ -50,7 +50,7 @@ class CompiledICLocker: public StackObj {
public:
CompiledICLocker(nmethod* method);
~CompiledICLocker();
static bool is_safe(nmethod* method);
static bool is_safe(nmethod* nm);
static bool is_safe(address code);
};

View File

@ -754,7 +754,7 @@ Method* nmethod::attached_method_before_pc(address pc) {
}
void nmethod::clear_inline_caches() {
assert(SafepointSynchronize::is_at_safepoint(), "clearing of IC's only allowed at safepoint");
assert(SafepointSynchronize::is_at_safepoint() || (NMethodState_lock->owned_by_self() && is_not_installed()), "clearing of IC's only allowed at safepoint or when not installed");
RelocIterator iter(this);
while (iter.next()) {
iter.reloc()->clear_inline_cache();
@ -1146,7 +1146,8 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
#if INCLUDE_JVMCI
+ align_up(speculations_len , oopSize)
#endif
+ align_up(debug_info->data_size() , oopSize);
+ align_up(debug_info->data_size() , oopSize)
+ align_up(ImmutableDataReferencesCounterSize, oopSize);
// First, allocate space for immutable data in C heap.
address immutable_data = nullptr;
@ -1371,10 +1372,266 @@ nmethod::nmethod(
}
}
nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm._header_size)
{
if (nm._oop_maps != nullptr) {
_oop_maps = nm._oop_maps->clone();
} else {
_oop_maps = nullptr;
}
_size = nm._size;
_relocation_size = nm._relocation_size;
_content_offset = nm._content_offset;
_code_offset = nm._code_offset;
_data_offset = nm._data_offset;
_frame_size = nm._frame_size;
S390_ONLY( _ctable_offset = nm._ctable_offset; )
_header_size = nm._header_size;
_frame_complete_offset = nm._frame_complete_offset;
_kind = nm._kind;
_caller_must_gc_arguments = nm._caller_must_gc_arguments;
#ifndef PRODUCT
_asm_remarks.share(nm._asm_remarks);
_dbg_strings.share(nm._dbg_strings);
#endif
// Allocate memory and copy mutable data to C heap
_mutable_data_size = nm._mutable_data_size;
if (_mutable_data_size > 0) {
_mutable_data = (address)os::malloc(_mutable_data_size, mtCode);
if (_mutable_data == nullptr) {
vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "nmethod: no space for mutable data");
}
memcpy(mutable_data_begin(), nm.mutable_data_begin(), nm.mutable_data_size());
} else {
_mutable_data = nullptr;
}
_deoptimization_generation = 0;
_gc_epoch = CodeCache::gc_epoch();
_method = nm._method;
_osr_link = nullptr;
// Increment number of references to immutable data to share it between nmethods
_immutable_data_size = nm._immutable_data_size;
if (_immutable_data_size > 0) {
_immutable_data = nm._immutable_data;
set_immutable_data_references_counter(get_immutable_data_references_counter() + 1);
} else {
_immutable_data = blob_end();
}
_exception_cache = nullptr;
_gc_data = nullptr;
_oops_do_mark_nmethods = nullptr;
_oops_do_mark_link = nullptr;
_compiled_ic_data = nullptr;
if (nm._osr_entry_point != nullptr) {
_osr_entry_point = (nm._osr_entry_point - (address) &nm) + (address) this;
} else {
_osr_entry_point = nullptr;
}
_entry_offset = nm._entry_offset;
_verified_entry_offset = nm._verified_entry_offset;
_entry_bci = nm._entry_bci;
_skipped_instructions_size = nm._skipped_instructions_size;
_stub_offset = nm._stub_offset;
_exception_offset = nm._exception_offset;
_deopt_handler_offset = nm._deopt_handler_offset;
_unwind_handler_offset = nm._unwind_handler_offset;
_num_stack_arg_slots = nm._num_stack_arg_slots;
_oops_size = nm._oops_size;
#if INCLUDE_JVMCI
_metadata_size = nm._metadata_size;
#endif
_nul_chk_table_offset = nm._nul_chk_table_offset;
_handler_table_offset = nm._handler_table_offset;
_scopes_pcs_offset = nm._scopes_pcs_offset;
_scopes_data_offset = nm._scopes_data_offset;
#if INCLUDE_JVMCI
_speculations_offset = nm._speculations_offset;
#endif
_orig_pc_offset = nm._orig_pc_offset;
_compile_id = nm._compile_id;
_comp_level = nm._comp_level;
_compiler_type = nm._compiler_type;
_is_unloading_state = nm._is_unloading_state;
_state = not_installed;
_has_unsafe_access = nm._has_unsafe_access;
_has_wide_vectors = nm._has_wide_vectors;
_has_monitors = nm._has_monitors;
_has_scoped_access = nm._has_scoped_access;
_has_flushed_dependencies = nm._has_flushed_dependencies;
_is_unlinked = nm._is_unlinked;
_load_reported = nm._load_reported;
_deoptimization_status = nm._deoptimization_status;
if (nm._pc_desc_container != nullptr) {
_pc_desc_container = new PcDescContainer(scopes_pcs_begin());
} else {
_pc_desc_container = nullptr;
}
// Copy nmethod contents excluding header
// - Constant part (doubles, longs and floats used in nmethod)
// - Code part:
// - Code body
// - Exception handler
// - Stub code
// - OOP table
memcpy(consts_begin(), nm.consts_begin(), nm.data_end() - nm.consts_begin());
post_init();
}
nmethod* nmethod::relocate(CodeBlobType code_blob_type) {
assert(NMethodRelocation, "must enable use of function");
// Locks required to be held by caller to ensure the nmethod
// is not modified or purged from code cache during relocation
assert_lock_strong(CodeCache_lock);
assert_lock_strong(Compile_lock);
assert(CompiledICLocker::is_safe(this), "mt unsafe call");
if (!is_relocatable()) {
return nullptr;
}
run_nmethod_entry_barrier();
nmethod* nm_copy = new (size(), code_blob_type) nmethod(*this);
if (nm_copy == nullptr) {
return nullptr;
}
// Fix relocation
RelocIterator iter(nm_copy);
CodeBuffer src(this);
CodeBuffer dst(nm_copy);
while (iter.next()) {
#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
// Direct calls may no longer be in range and the use of a trampoline may now be required.
// Instead, allow trampoline relocations to update their owners and perform the necessary checks.
if (iter.reloc()->is_call()) {
address trampoline = trampoline_stub_Relocation::get_trampoline_for(iter.reloc()->addr(), nm_copy);
if (trampoline != nullptr) {
continue;
}
}
#endif
iter.reloc()->fix_relocation_after_move(&src, &dst);
}
// To make dependency checking during class loading fast, record
// the nmethod dependencies in the classes it is dependent on.
// This allows the dependency checking code to simply walk the
// class hierarchy above the loaded class, checking only nmethods
// which are dependent on those classes. The slow way is to
// check every nmethod for dependencies which makes it linear in
// the number of methods compiled. For applications with a lot
// classes the slow way is too slow.
for (Dependencies::DepStream deps(nm_copy); deps.next(); ) {
if (deps.type() == Dependencies::call_site_target_value) {
// CallSite dependencies are managed on per-CallSite instance basis.
oop call_site = deps.argument_oop(0);
MethodHandles::add_dependent_nmethod(call_site, nm_copy);
} else {
InstanceKlass* ik = deps.context_type();
if (ik == nullptr) {
continue; // ignore things like evol_method
}
// record this nmethod as dependent on this klass
ik->add_dependent_nmethod(nm_copy);
}
}
MutexLocker ml_NMethodState_lock(NMethodState_lock, Mutex::_no_safepoint_check_flag);
// Verify the nm we copied from is still valid
if (!is_marked_for_deoptimization() && is_in_use()) {
assert(method() != nullptr && method()->code() == this, "should be if is in use");
nm_copy->clear_inline_caches();
// Attempt to start using the copy
if (nm_copy->make_in_use()) {
ICache::invalidate_range(nm_copy->code_begin(), nm_copy->code_size());
methodHandle mh(Thread::current(), nm_copy->method());
nm_copy->method()->set_code(mh, nm_copy);
make_not_used();
nm_copy->post_compiled_method_load_event();
nm_copy->log_relocated_nmethod(this);
return nm_copy;
}
}
nm_copy->make_not_used();
return nullptr;
}
bool nmethod::is_relocatable() {
if (!is_java_method()) {
return false;
}
if (!is_in_use()) {
return false;
}
if (is_osr_method()) {
return false;
}
if (is_marked_for_deoptimization()) {
return false;
}
#if INCLUDE_JVMCI
if (jvmci_nmethod_data() != nullptr && jvmci_nmethod_data()->has_mirror()) {
return false;
}
#endif
if (is_unloading()) {
return false;
}
if (has_evol_metadata()) {
return false;
}
return true;
}
void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw () {
return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level));
}
void* nmethod::operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw () {
return CodeCache::allocate(nmethod_size, code_blob_type);
}
void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw () {
// Try MethodNonProfiled and MethodProfiled.
void* return_value = CodeCache::allocate(nmethod_size, CodeBlobType::MethodNonProfiled);
@ -1494,9 +1751,9 @@ nmethod::nmethod(
#if INCLUDE_JVMCI
_speculations_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize);
DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize); )
DEBUG_ONLY( int immutable_data_end_offset = _speculations_offset + align_up(speculations_len, oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); )
#else
DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize); )
DEBUG_ONLY( int immutable_data_end_offset = _scopes_data_offset + align_up(debug_info->data_size(), oopSize) + align_up(ImmutableDataReferencesCounterSize, oopSize); )
#endif
assert(immutable_data_end_offset <= immutable_data_size, "wrong read-only data size: %d > %d",
immutable_data_end_offset, immutable_data_size);
@ -1529,6 +1786,7 @@ nmethod::nmethod(
memcpy(speculations_begin(), speculations, speculations_len);
}
#endif
set_immutable_data_references_counter(1);
post_init();
@ -1595,6 +1853,40 @@ void nmethod::log_new_nmethod() const {
}
}
void nmethod::log_relocated_nmethod(nmethod* original) const {
if (LogCompilation && xtty != nullptr) {
ttyLocker ttyl;
xtty->begin_elem("relocated nmethod");
log_identity(xtty);
xtty->print(" entry='" INTPTR_FORMAT "' size='%d'", p2i(code_begin()), size());
const char* original_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(original));
xtty->print(" original_address='" INTPTR_FORMAT "'", p2i(original));
xtty->print(" original_code_heap='%s'", original_code_heap_name);
const char* new_code_heap_name = CodeCache::get_code_heap_name(CodeCache::get_code_blob_type(this));
xtty->print(" new_address='" INTPTR_FORMAT "'", p2i(this));
xtty->print(" new_code_heap='%s'", new_code_heap_name);
LOG_OFFSET(xtty, relocation);
LOG_OFFSET(xtty, consts);
LOG_OFFSET(xtty, insts);
LOG_OFFSET(xtty, stub);
LOG_OFFSET(xtty, scopes_data);
LOG_OFFSET(xtty, scopes_pcs);
LOG_OFFSET(xtty, dependencies);
LOG_OFFSET(xtty, handler_table);
LOG_OFFSET(xtty, nul_chk_table);
LOG_OFFSET(xtty, oops);
LOG_OFFSET(xtty, metadata);
xtty->method(method());
xtty->stamp();
xtty->end_elem();
}
}
#undef LOG_OFFSET
@ -2127,9 +2419,18 @@ void nmethod::purge(bool unregister_nmethod) {
delete[] _compiled_ic_data;
if (_immutable_data != blob_end()) {
os::free(_immutable_data);
int reference_count = get_immutable_data_references_counter();
assert(reference_count > 0, "immutable data has no references");
set_immutable_data_references_counter(reference_count - 1);
// Free memory if this is the last nmethod referencing immutable data
if (reference_count == 0) {
os::free(_immutable_data);
}
_immutable_data = blob_end(); // Valid not null address
}
if (unregister_nmethod) {
Universe::heap()->unregister_nmethod(this);
}

View File

@ -154,6 +154,7 @@ public:
// - Scopes data array
// - Scopes pcs array
// - JVMCI speculations array
// - Nmethod reference counter
#if INCLUDE_JVMCI
class FailedSpeculation;
@ -167,6 +168,8 @@ class nmethod : public CodeBlob {
friend class JVMCINMethodData;
friend class DeoptimizationScope;
#define ImmutableDataReferencesCounterSize ((int)sizeof(int))
private:
// Used to track in which deoptimize handshake this method will be deoptimized.
@ -330,8 +333,11 @@ class nmethod : public CodeBlob {
#endif
);
nmethod(const nmethod &nm);
// helper methods
void* operator new(size_t size, int nmethod_size, int comp_level) throw();
void* operator new(size_t size, int nmethod_size, CodeBlobType code_blob_type) throw();
// For method handle intrinsics: Try MethodNonProfiled, MethodProfiled and NonNMethod.
// Attention: Only allow NonNMethod space for special nmethods which don't need to be
@ -564,6 +570,12 @@ public:
#endif
);
// Relocate the nmethod to the code heap identified by code_blob_type.
// Returns nullptr if the code heap does not have enough space, the
// nmethod is unrelocatable, or the nmethod is invalidated during relocation,
// otherwise the relocated nmethod. The original nmethod will be marked not entrant.
nmethod* relocate(CodeBlobType code_blob_type);
static nmethod* new_native_nmethod(const methodHandle& method,
int compile_id,
CodeBuffer *code_buffer,
@ -580,6 +592,8 @@ public:
bool is_java_method () const { return _method != nullptr && !_method->is_native(); }
bool is_osr_method () const { return _entry_bci != InvocationEntryBci; }
bool is_relocatable();
// Compiler task identification. Note that all OSR methods
// are numbered in an independent sequence if CICountOSR is true,
// and native method wrappers are also numbered independently if
@ -632,11 +646,13 @@ public:
#if INCLUDE_JVMCI
address scopes_data_end () const { return _immutable_data + _speculations_offset ; }
address speculations_begin () const { return _immutable_data + _speculations_offset ; }
address speculations_end () const { return immutable_data_end(); }
address speculations_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
#else
address scopes_data_end () const { return immutable_data_end(); }
address scopes_data_end () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
#endif
address immutable_data_references_counter_begin () const { return immutable_data_end() - ImmutableDataReferencesCounterSize ; }
// Sizes
int immutable_data_size() const { return _immutable_data_size; }
int consts_size () const { return int( consts_end () - consts_begin ()); }
@ -946,6 +962,9 @@ public:
bool load_reported() const { return _load_reported; }
void set_load_reported() { _load_reported = true; }
inline int get_immutable_data_references_counter() { return *((int*)immutable_data_references_counter_begin()); }
inline void set_immutable_data_references_counter(int count) { *((int*)immutable_data_references_counter_begin()) = count; }
public:
// ScopeDesc retrieval operation
PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); }
@ -1014,6 +1033,7 @@ public:
// Logging
void log_identity(xmlStream* log) const;
void log_new_nmethod() const;
void log_relocated_nmethod(nmethod* original) const;
void log_state_change(InvalidationReason invalidation_reason) const;
// Prints block-level comments, including nmethod specific block labels:

View File

@ -406,11 +406,12 @@ void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer
pd_set_call_destination(callee);
}
#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
void trampoline_stub_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) {
// Finalize owner destination only for nmethods
if (dest->blob() != nullptr) return;
// We either relocate a nmethod residing in CodeCache or just generated code from CodeBuffer
assert(src->blob() == nullptr || nativeCall_at(owner())->raw_destination() == owner(), "destination should be empty");
pd_fix_owner_after_move();
}
#endif

View File

@ -862,6 +862,12 @@ ImmutableOopMapSet* ImmutableOopMapSet::build_from(const OopMapSet* oopmap_set)
return builder.build();
}
ImmutableOopMapSet* ImmutableOopMapSet::clone() const {
address buffer = NEW_C_HEAP_ARRAY(unsigned char, _size, mtCode);
memcpy(buffer, (address)this, _size);
return (ImmutableOopMapSet*)buffer;
}
void ImmutableOopMapSet::operator delete(void* p) {
FREE_C_HEAP_ARRAY(unsigned char, p);
}

View File

@ -348,6 +348,8 @@ public:
static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set);
ImmutableOopMapSet* clone() const;
int find_slot_for_offset(int pc_offset) const;
const ImmutableOopMap* find_map_at_offset(int pc_offset) const;
const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const;

View File

@ -103,7 +103,7 @@ public:
}
virtual bool is_safe(nmethod* nm) {
if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) {
if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) {
return true;
}

View File

@ -100,7 +100,7 @@ public:
}
virtual bool is_safe(nmethod* nm) {
if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading()) {
if (SafepointSynchronize::is_at_safepoint() || nm->is_unloading() || (NMethodState_lock->owned_by_self() && nm->is_not_installed())) {
return true;
}

View File

@ -137,6 +137,11 @@ public:
// Gets the JVMCI name of the nmethod (which may be null).
const char* name() { return has_name() ? (char*)(((address) this) + sizeof(JVMCINMethodData)) : nullptr; }
// Returns true if this nmethod has a mirror
bool has_mirror() const {
return _nmethod_mirror_index != -1;
}
// Clears the HotSpotNmethod.address field in the mirror. If nm
// is dead, the HotSpotNmethod.entryPoint field is also cleared.
void invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason);

View File

@ -39,6 +39,7 @@
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/directivesParser.hpp"
@ -1548,19 +1549,23 @@ struct CodeBlobStub {
name(os::strdup(blob->name())),
size(blob->size()),
blob_type(static_cast<jint>(WhiteBox::get_blob_type(blob))),
address((jlong) blob) { }
address((jlong) blob),
code_begin((jlong) blob->code_begin()),
is_nmethod((jboolean) blob->is_nmethod()) { }
~CodeBlobStub() { os::free((void*) name); }
const char* const name;
const jint size;
const jint blob_type;
const jlong address;
const jlong code_begin;
const jboolean is_nmethod;
};
static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBlobStub* cb) {
ResourceMark rm;
jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string());
CHECK_JNI_EXCEPTION_(env, nullptr);
jobjectArray result = env->NewObjectArray(4, clazz, nullptr);
jobjectArray result = env->NewObjectArray(6, clazz, nullptr);
jstring name = env->NewStringUTF(cb->name);
CHECK_JNI_EXCEPTION_(env, nullptr);
@ -1578,6 +1583,14 @@ static jobjectArray codeBlob2objectArray(JavaThread* thread, JNIEnv* env, CodeBl
CHECK_JNI_EXCEPTION_(env, nullptr);
env->SetObjectArrayElement(result, 3, obj);
obj = longBox(thread, env, cb->code_begin);
CHECK_JNI_EXCEPTION_(env, nullptr);
env->SetObjectArrayElement(result, 4, obj);
obj = booleanBox(thread, env, cb->is_nmethod);
CHECK_JNI_EXCEPTION_(env, nullptr);
env->SetObjectArrayElement(result, 5, obj);
return result;
}
@ -1627,6 +1640,44 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
return result;
WB_END
WB_ENTRY(void, WB_RelocateNMethodFromMethod(JNIEnv* env, jobject o, jobject method, jint blob_type))
ResourceMark rm(THREAD);
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
CHECK_JNI_EXCEPTION(env);
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
nmethod* code = mh->code();
if (code != nullptr) {
MutexLocker ml_Compile_lock(Compile_lock);
CompiledICLocker ic_locker(code);
MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag);
code->relocate(static_cast<CodeBlobType>(blob_type));
}
WB_END
WB_ENTRY(void, WB_RelocateNMethodFromAddr(JNIEnv* env, jobject o, jlong addr, jint blob_type))
ResourceMark rm(THREAD);
CHECK_JNI_EXCEPTION(env);
void* address = (void*) addr;
if (address == nullptr) {
return;
}
MutexLocker ml_Compile_lock(Compile_lock);
MutexLocker ml_CompiledIC_lock(CompiledIC_lock, Mutex::_no_safepoint_check_flag);
MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag);
// Verify that nmethod address is still valid
CodeBlob* blob = CodeCache::find_blob(address);
if (blob != nullptr && blob->is_nmethod()) {
nmethod* code = blob->as_nmethod();
if (code->is_in_use()) {
CompiledICLocker ic_locker(code);
code->relocate(static_cast<CodeBlobType>(blob_type));
}
}
WB_END
CodeBlob* WhiteBox::allocate_code_blob(int size, CodeBlobType blob_type) {
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
BufferBlob* blob;
@ -2916,6 +2967,9 @@ static JNINativeMethod methods[] = {
{CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures },
{CC"getNMethod0", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
(void*)&WB_GetNMethod },
{CC"relocateNMethodFromMethod0", CC"(Ljava/lang/reflect/Executable;I)V",
(void*)&WB_RelocateNMethodFromMethod },
{CC"relocateNMethodFromAddr", CC"(JI)V", (void*)&WB_RelocateNMethodFromAddr },
{CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob },
{CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob },
{CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries },

View File

@ -1565,6 +1565,9 @@ const int ObjectAlignmentInBytes = 8;
"Start aggressive sweeping if less than X[%] of the total code cache is free.")\
range(0, 100) \
\
product(bool, NMethodRelocation, false, EXPERIMENTAL, \
"Enables use of experimental function nmethod::relocate()") \
\
/* interpreter debugging */ \
develop(intx, BinarySwitchThreshold, 5, \
"Minimal number of lookupswitch entries for rewriting to binary " \

View File

@ -37,6 +37,7 @@ import sun.jvm.hotspot.utilities.Observer;
public class NMethod extends CodeBlob {
private static long pcDescSize;
private static long immutableDataReferencesCounterSize;
private static AddressField methodField;
/** != InvocationEntryBci if this nmethod is an on-stack replacement method */
private static CIntegerField entryBCIField;
@ -78,24 +79,25 @@ public class NMethod extends CodeBlob {
private static void initialize(TypeDataBase db) {
Type type = db.lookupType("nmethod");
methodField = type.getAddressField("_method");
entryBCIField = type.getCIntegerField("_entry_bci");
osrLinkField = type.getAddressField("_osr_link");
immutableDataField = type.getAddressField("_immutable_data");
immutableDataSizeField = type.getCIntegerField("_immutable_data_size");
exceptionOffsetField = type.getCIntegerField("_exception_offset");
deoptHandlerOffsetField = type.getCIntegerField("_deopt_handler_offset");
origPCOffsetField = type.getCIntegerField("_orig_pc_offset");
stubOffsetField = type.getCIntegerField("_stub_offset");
scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset");
scopesDataOffsetField = type.getCIntegerField("_scopes_data_offset");
handlerTableOffsetField = new CIntField(type.getCIntegerField("_handler_table_offset"), 0);
nulChkTableOffsetField = new CIntField(type.getCIntegerField("_nul_chk_table_offset"), 0);
entryOffsetField = new CIntField(type.getCIntegerField("_entry_offset"), 0);
verifiedEntryOffsetField = new CIntField(type.getCIntegerField("_verified_entry_offset"), 0);
osrEntryPointField = type.getAddressField("_osr_entry_point");
compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0);
pcDescSize = db.lookupType("PcDesc").getSize();
methodField = type.getAddressField("_method");
entryBCIField = type.getCIntegerField("_entry_bci");
osrLinkField = type.getAddressField("_osr_link");
immutableDataField = type.getAddressField("_immutable_data");
immutableDataSizeField = type.getCIntegerField("_immutable_data_size");
exceptionOffsetField = type.getCIntegerField("_exception_offset");
deoptHandlerOffsetField = type.getCIntegerField("_deopt_handler_offset");
origPCOffsetField = type.getCIntegerField("_orig_pc_offset");
stubOffsetField = type.getCIntegerField("_stub_offset");
scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset");
scopesDataOffsetField = type.getCIntegerField("_scopes_data_offset");
handlerTableOffsetField = new CIntField(type.getCIntegerField("_handler_table_offset"), 0);
nulChkTableOffsetField = new CIntField(type.getCIntegerField("_nul_chk_table_offset"), 0);
entryOffsetField = new CIntField(type.getCIntegerField("_entry_offset"), 0);
verifiedEntryOffsetField = new CIntField(type.getCIntegerField("_verified_entry_offset"), 0);
osrEntryPointField = type.getAddressField("_osr_entry_point");
compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0);
pcDescSize = db.lookupType("PcDesc").getSize();
immutableDataReferencesCounterSize = VM.getVM().getIntSize();
}
public NMethod(Address addr) {
@ -139,7 +141,7 @@ public class NMethod extends CodeBlob {
public Address scopesDataBegin() { return immutableDataBegin().addOffsetTo(getScopesDataOffset()); }
public Address scopesDataEnd() { return immutableDataBegin().addOffsetTo(getScopesPCsOffset()); }
public Address scopesPCsBegin() { return immutableDataBegin().addOffsetTo(getScopesPCsOffset()); }
public Address scopesPCsEnd() { return immutableDataEnd(); }
public Address scopesPCsEnd() { return immutableDataEnd().addOffsetTo(-immutableDataReferencesCounterSize); }
public Address metadataBegin() { return mutableDataBegin().addOffsetTo(getRelocationSize()); }
public Address metadataEnd() { return mutableDataEnd(); }
@ -169,7 +171,8 @@ public class NMethod extends CodeBlob {
scopesPCsSize() +
dependenciesSize() +
handlerTableSize() +
nulChkTableSize();
nulChkTableSize() +
(int) immutableDataReferencesCounterSize;
}
public boolean constantsContains (Address addr) { return constantsBegin() .lessThanOrEqual(addr) && constantsEnd() .greaterThan(addr); }

View File

@ -221,15 +221,28 @@ public abstract class CompilerWhiteBoxTest {
* compilation level.
*/
protected final void checkNotCompiled(boolean isOsr) {
if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
throw new RuntimeException(method + " must not be in queue");
checkNotCompiled(method, isOsr);
}
/**
* Checks, that the specified executable is not (OSR-)compiled.
*
* @param executable The method or constructor to check.
* @param isOsr Check for OSR compilation if true
* @throws RuntimeException if {@linkplain #method} is in compiler queue or
* is compiled, or if {@linkplain #method} has zero
* compilation level.
*/
protected static final void checkNotCompiled(Executable executable, boolean isOsr) {
if (WHITE_BOX.isMethodQueuedForCompilation(executable)) {
throw new RuntimeException(executable + " must not be in queue");
}
if (WHITE_BOX.isMethodCompiled(method, isOsr)) {
throw new RuntimeException(method + " must not be " +
if (WHITE_BOX.isMethodCompiled(executable, isOsr)) {
throw new RuntimeException(executable + " must not be " +
(isOsr ? "osr_" : "") + "compiled");
}
if (WHITE_BOX.getMethodCompilationLevel(method, isOsr) != 0) {
throw new RuntimeException(method + (isOsr ? " osr_" : " ") +
if (WHITE_BOX.getMethodCompilationLevel(executable, isOsr) != 0) {
throw new RuntimeException(executable + (isOsr ? " osr_" : " ") +
"comp_level must be == 0");
}
}
@ -242,21 +255,34 @@ public abstract class CompilerWhiteBoxTest {
* has nonzero compilation level
*/
protected final void checkCompiled() {
checkCompiled(method, testCase.isOsr());
}
/**
* Checks, that the specified executable is compiled.
*
* @param executable The method or constructor to check.
* @param isOsr Check for OSR compilation if true
* @throws RuntimeException if {@linkplain #method} isn't in compiler queue
* and isn't compiled, or if {@linkplain #method}
* has nonzero compilation level
*/
protected static final void checkCompiled(Executable executable, boolean isOsr) {
final long start = System.currentTimeMillis();
waitBackgroundCompilation();
if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
waitBackgroundCompilation(executable);
if (WHITE_BOX.isMethodQueuedForCompilation(executable)) {
System.err.printf("Warning: %s is still in queue after %dms%n",
method, System.currentTimeMillis() - start);
executable, System.currentTimeMillis() - start);
return;
}
if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
throw new RuntimeException(method + " must be "
+ (testCase.isOsr() ? "osr_" : "") + "compiled");
if (!WHITE_BOX.isMethodCompiled(executable, isOsr)) {
throw new RuntimeException(executable + " must be "
+ (isOsr ? "osr_" : "") + "compiled");
}
if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr())
if (WHITE_BOX.getMethodCompilationLevel(executable, isOsr)
== 0) {
throw new RuntimeException(method
+ (testCase.isOsr() ? " osr_" : " ")
throw new RuntimeException(executable
+ (isOsr ? " osr_" : " ")
+ "comp_level must be != 0");
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright Amazon.com Inc. 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 id=Serial
* @bug 8316694
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Serial
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseSerialGC
* -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
*/
/*
* @test id=Parallel
* @bug 8316694
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Parallel
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseParallelGC
* -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
*/
/*
* @test id=G1
* @bug 8316694
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.G1
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseG1GC
* -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
*/
/*
* @test id=Shenandoah
* @bug 8316694
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Shenandoah
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseShenandoahGC
* -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
*/
/*
* @test id=ZGC
* @bug 8316694
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Z
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache -XX:+UseZGC
* -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.DeoptimizeRelocatedNMethod
*/
package compiler.whitebox;
import compiler.whitebox.CompilerWhiteBoxTest;
import java.lang.reflect.Method;
import jdk.test.whitebox.WhiteBox;
import jdk.test.whitebox.code.BlobType;
import jdk.test.whitebox.code.NMethod;
public class DeoptimizeRelocatedNMethod {
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
public static double FUNCTION_RESULT = 0;
public static void main(String [] args) throws Exception {
// Get method that will be relocated
Method method = DeoptimizeRelocatedNMethod.class.getMethod("function");
WHITE_BOX.testSetDontInlineMethod(method, true);
// Verify not initially compiled
CompilerWhiteBoxTest.checkNotCompiled(method, false);
// Call function enough to compile
callFunction();
// Verify now compiled
CompilerWhiteBoxTest.checkCompiled(method, false);
// Get newly created nmethod
NMethod origNmethod = NMethod.get(method, false);
// Relocate nmethod and mark old for cleanup
WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id);
// Trigger GC to clean up old nmethod
WHITE_BOX.fullGC();
// Verify function still compiled after old was cleaned up
CompilerWhiteBoxTest.checkCompiled(method, false);
// Get new nmethod and verify it's actually new
NMethod newNmethod = NMethod.get(method, false);
if (origNmethod.entry_point == newNmethod.entry_point) {
throw new RuntimeException("Did not create new nmethod");
}
// Call to verify everything still works
function();
// Deoptimized method
WHITE_BOX.deoptimizeMethod(method);
CompilerWhiteBoxTest.checkNotCompiled(method, false);
// Call to verify everything still works
function();
}
// Call function multiple times to trigger compilation
private static void callFunction() {
for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) {
function();
}
}
public static void function() {
FUNCTION_RESULT = Math.random();
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright Amazon.com Inc. 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 id=Serial
* @bug 8316694
* @summary test that nmethod::relocate() correctly creates a new nmethod
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Serial
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
* -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
*/
/*
* @test id=Parallel
* @bug 8316694
* @summary test that nmethod::relocate() correctly creates a new nmethod
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Parallel
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
* -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
*/
/*
* @test id=G1
* @bug 8316694
* @summary test that nmethod::relocate() correctly creates a new nmethod
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.G1
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
* -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
*/
/*
* @test id=Shenandoah
* @bug 8316694
* @summary test that nmethod::relocate() correctly creates a new nmethod
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Shenandoah
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
* -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
*/
/*
* @test id=ZGC
* @bug 8316694
* @summary test that nmethod::relocate() correctly creates a new nmethod
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @requires vm.opt.DeoptimizeALot != true
* @requires vm.gc.Z
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache
* -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation compiler.whitebox.RelocateNMethod
*/
package compiler.whitebox;
import java.lang.reflect.Method;
import jdk.test.whitebox.code.BlobType;
import jdk.test.whitebox.code.NMethod;
import jdk.test.whitebox.WhiteBox;
import compiler.whitebox.CompilerWhiteBoxTest;
public class RelocateNMethod extends CompilerWhiteBoxTest {
public static void main(String[] args) throws Exception {
CompilerWhiteBoxTest.main(RelocateNMethod::new, new String[] {"CONSTRUCTOR_TEST", "METHOD_TEST", "STATIC_TEST"});
}
private RelocateNMethod(TestCase testCase) {
super(testCase);
// to prevent inlining of #method
WHITE_BOX.testSetDontInlineMethod(method, true);
}
@Override
protected void test() throws Exception {
checkNotCompiled();
compile();
checkCompiled();
NMethod origNmethod = NMethod.get(method, false);
WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodProfiled.id);
WHITE_BOX.fullGC();
checkCompiled();
NMethod newNmethod = NMethod.get(method, false);
if (origNmethod.entry_point == newNmethod.entry_point) {
throw new RuntimeException("Did not create new nmethod");
}
}
}

View File

@ -0,0 +1,261 @@
/*
* Copyright Amazon.com Inc. 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 id=SerialC1
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Serial
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=SerialC2
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Serial
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ParallelC1
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Parallel
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ParallelC2
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Parallel
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=G1C1
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.G1
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=G1C2
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.G1
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ShenandoahC1
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Shenandoah
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ShenandoahC2
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Shenandoah
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ZGCC1
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Z
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
/*
* @test id=ZGCC2
* @bug 8316694
* @requires vm.debug == true
* @requires vm.gc.Z
* @summary test that relocated nmethod is correctly deoptimized
* @library /test/lib /
* @modules java.base/jdk.internal.misc java.management
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+TieredCompilation
* -XX:+SegmentedCodeCache -XX:-DeoptimizeRandom -XX:+DeoptimizeALot -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+NMethodRelocation
* compiler.whitebox.RelocateNMethodMultiplePaths
*/
package compiler.whitebox;
import compiler.whitebox.CompilerWhiteBoxTest;
import java.lang.reflect.Method;
import jdk.test.whitebox.WhiteBox;
import jdk.test.whitebox.code.BlobType;
import jdk.test.whitebox.code.NMethod;
public class RelocateNMethodMultiplePaths {
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
private static final int PATH_ONE_RESULT = 1;
private static final int PATH_TWO_RESULT = 2;
public static void main(String [] args) throws Exception {
// Get method that will be relocated
Method method = RelocateNMethodMultiplePaths.class.getMethod("function", boolean.class);
WHITE_BOX.testSetDontInlineMethod(method, true);
// Verify not initially compiled
CompilerWhiteBoxTest.checkNotCompiled(method, false);
// Call function enough to compile
callFunction(true);
// Verify now compiled
CompilerWhiteBoxTest.checkCompiled(method, false);
// Get newly created nmethod
NMethod origNmethod = NMethod.get(method, false);
// Relocate nmethod and mark old for cleanup
WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id);
// Trigger GC to clean up old nmethod
WHITE_BOX.fullGC();
// Verify function still compiled after old was cleaned up
CompilerWhiteBoxTest.checkCompiled(method, false);
// Get new nmethod and verify it's actually new
NMethod newNmethod = NMethod.get(method, false);
if (origNmethod.entry_point == newNmethod.entry_point) {
throw new RuntimeException("Did not create new nmethod");
}
// Verify function still produces correct result
if (function(true) != PATH_ONE_RESULT) {
throw new RuntimeException("Relocated function produced incorrect result in path one");
}
// Call function again with different path and verify result
if (function(false) != PATH_TWO_RESULT) {
throw new RuntimeException("Relocated function produced incorrect result in path two");
}
// Verify function can be correctly deoptimized
WHITE_BOX.deoptimizeMethod(method);
CompilerWhiteBoxTest.checkNotCompiled(method, false);
}
// Call function multiple times to trigger compilation
private static void callFunction(boolean pathOne) {
for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) {
function(pathOne);
}
}
public static int function(boolean pathOne) {
if (pathOne) {
return PATH_ONE_RESULT;
} else {
return PATH_TWO_RESULT;
}
}
}

View File

@ -0,0 +1,239 @@
/*
* Copyright Amazon.com Inc. 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 StressNMethodRelocation
* @summary Call and relocate methods concurrently
* @library /test/lib /
* @modules java.base/jdk.internal.misc
* java.management
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions
* -XX:+NMethodRelocation compiler.whitebox.StressNMethodRelocation
*/
package compiler.whitebox;
import jdk.test.whitebox.WhiteBox;
import jdk.test.whitebox.code.BlobType;
import jdk.test.whitebox.code.CodeBlob;
import jdk.test.whitebox.code.NMethod;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Random;
public class StressNMethodRelocation {
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
private static final int C2_LEVEL = 4;
private static final int ACTIVE_METHODS = 1024;
private static TestMethod[] methods;
private static byte[] num1;
private static byte[] num2;
private static long DURATION = 60_000;
public static void main(String[] args) throws Exception {
// Initialize defaults
initNums();
// Generate compiled code
methods = new TestMethod[ACTIVE_METHODS];
generateCode(methods);
// Create thread that runs compiled methods
RunMethods runMethods = new RunMethods();
Thread runMethodsThread = new Thread(runMethods);
// Create thread that relocates compiled methods
RelocateNMethods relocate = new RelocateNMethods();
Thread relocateThread = new Thread(relocate);
// Start theads
runMethodsThread.start();
relocateThread.start();
// Wait for threads to finish
runMethodsThread.join();
relocateThread.join();
}
private static byte[] genNum(Random random, int digitCount) {
byte[] num = new byte[digitCount];
int d;
do {
d = random.nextInt(10);
} while (d == 0);
num[0] = (byte)d;
for (int i = 1; i < digitCount; ++i) {
num[i] = (byte)random.nextInt(10);
}
return num;
}
private static void initNums() {
final long seed = 8374592837465123L;
Random random = new Random(seed);
final int digitCount = 40;
num1 = genNum(random, digitCount);
num2 = genNum(random, digitCount);
}
private static void generateCode(TestMethod[] m) throws Exception {
byte[] result = new byte[num1.length + 1];
for (int i = 0; i < ACTIVE_METHODS; ++i) {
m[i] = new TestMethod();
m[i].profile(num1, num2, result);
m[i].compileWithC2();
}
}
private static final class TestMethod {
private static final String CLASS_NAME = "A";
private static final String METHOD_TO_COMPILE = "sum";
private static final String JAVA_CODE = """
public class A {
public static void sum(byte[] n1, byte[] n2, byte[] out) {
final int digitCount = n1.length;
int carry = 0;
for (int i = digitCount - 1; i >= 0; --i) {
int sum = n1[i] + n2[i] + carry;
out[i] = (byte)(sum % 10);
carry = sum / 10;
}
if (carry != 0) {
for (int i = digitCount; i > 0; --i) {
out[i] = out[i - 1];
}
out[0] = (byte)carry;
}
}
}""";
private static final byte[] BYTE_CODE;
static {
BYTE_CODE = InMemoryJavaCompiler.compile(CLASS_NAME, JAVA_CODE);
}
private final Method method;
private static ClassLoader createClassLoaderFor() {
return new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!name.equals(CLASS_NAME)) {
return super.loadClass(name);
}
return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length);
}
};
}
public TestMethod() throws Exception {
var cl = createClassLoaderFor().loadClass(CLASS_NAME);
method = cl.getMethod(METHOD_TO_COMPILE, byte[].class, byte[].class, byte[].class);
WHITE_BOX.testSetDontInlineMethod(method, true);
}
public void profile(byte[] num1, byte[] num2, byte[] result) throws Exception {
method.invoke(null, num1, num2, result);
WHITE_BOX.markMethodProfiled(method);
}
public void invoke(byte[] num1, byte[] num2, byte[] result) throws Exception {
method.invoke(null, num1, num2, result);
}
public void compileWithC2() throws Exception {
WHITE_BOX.enqueueMethodForCompilation(method, C2_LEVEL);
while (WHITE_BOX.isMethodQueuedForCompilation(method)) {
Thread.onSpinWait();
}
if (WHITE_BOX.getMethodCompilationLevel(method) != C2_LEVEL) {
throw new IllegalStateException("Method " + method + " is not compiled by C2.");
}
}
}
private static final class RelocateNMethods implements Runnable {
public RelocateNMethods() {}
// Move nmethod back and forth between NonProfiled and Profiled code heaps
public void run() {
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < DURATION) {
// Relocate NonProfiled to Profiled
CodeBlob[] nonProfiledBlobs = CodeBlob.getCodeBlobs(BlobType.MethodNonProfiled);
for (CodeBlob blob : nonProfiledBlobs) {
if (blob.isNMethod) {
WHITE_BOX.relocateNMethodFromAddr(blob.address, BlobType.MethodProfiled.id);
}
}
// Relocate Profiled to NonProfiled
CodeBlob[] profiledBlobs = CodeBlob.getCodeBlobs(BlobType.MethodProfiled);
for (CodeBlob blob : nonProfiledBlobs) {
if (blob.isNMethod) {
WHITE_BOX.relocateNMethodFromAddr(blob.address, BlobType.MethodNonProfiled.id);
}
}
}
}
}
private static final class RunMethods implements Runnable {
public RunMethods() {}
public void run() {
try {
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < DURATION) {
callMethods();
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
private void callMethods() throws Exception {
for (var m : methods) {
byte[] result = new byte[num1.length + 1];
m.invoke(num1, num2, result);
}
}
}
}

View File

@ -0,0 +1,178 @@
/*
* Copyright Amazon.com Inc. 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
*
* @bug 8316694
* @summary Verify that nmethod relocation posts the correct JVMTI events
* @requires vm.jvmti
* @library /test/lib /test/hotspot/jtreg
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm/native NMethodRelocationTest
*/
import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
import java.lang.reflect.Executable;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.Asserts;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.whitebox.WhiteBox;
import jdk.test.whitebox.code.BlobType;
import jdk.test.whitebox.code.NMethod;
public class NMethodRelocationTest {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
"-agentlib:NMethodRelocationTest",
"--enable-native-access=ALL-UNNAMED",
"-Xbootclasspath/a:.",
"-XX:+UseSerialGC",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:+SegmentedCodeCache",
"-XX:-TieredCompilation",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+NMethodRelocation",
"DoWork");
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
String output = oa.getOutput();
if (oa.getExitValue() != 0) {
System.err.println(oa.getOutput());
throw new RuntimeException("Non-zero exit code returned from the test");
}
Asserts.assertTrue(oa.getExitValue() == 0);
Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$");
Matcher matcher = pattern.matcher(output);
if (matcher.find()) {
String fromAddr = matcher.group(1);
String toAddr = matcher.group(2);
// Confirm events sent for both original and relocated nmethod
oa.shouldContain("<COMPILED_METHOD_LOAD>: name: compiledMethod, code: " + fromAddr);
oa.shouldContain("<COMPILED_METHOD_LOAD>: name: compiledMethod, code: " + toAddr);
oa.shouldContain("<COMPILED_METHOD_UNLOAD>: name: compiledMethod, code: " + fromAddr);
oa.shouldContain("<COMPILED_METHOD_UNLOAD>: name: compiledMethod, code: " + toAddr);
} else {
System.err.println(oa.getOutput());
throw new RuntimeException("Unable to find relocation information");
}
}
}
class DoWork {
protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
/** Load native library if required. */
static {
try {
System.loadLibrary("NMethodRelocationTest");
} catch (UnsatisfiedLinkError ule) {
System.err.println("Could not load NMethodRelocationTest library");
System.err.println("java.library.path: "
+ System.getProperty("java.library.path"));
throw ule;
}
}
/**
* Returns value of VM option.
*
* @param name option's name
* @return value of option or {@code null}, if option doesn't exist
* @throws NullPointerException if name is null
*/
protected static String getVMOption(String name) {
Objects.requireNonNull(name);
return Objects.toString(WHITE_BOX.getVMFlag(name), null);
}
/**
* Returns value of VM option or default value.
*
* @param name option's name
* @param defaultValue default value
* @return value of option or {@code defaultValue}, if option doesn't exist
* @throws NullPointerException if name is null
* @see #getVMOption(String)
*/
protected static String getVMOption(String name, String defaultValue) {
String result = getVMOption(name);
return result == null ? defaultValue : result;
}
public static void main(String argv[]) throws Exception {
run();
}
public static void run() throws Exception {
Executable method = DoWork.class.getDeclaredMethod("compiledMethod");
WHITE_BOX.testSetDontInlineMethod(method, true);
WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION);
while (WHITE_BOX.isMethodQueuedForCompilation(method)) {
Thread.onSpinWait();
}
NMethod originalNMethod = NMethod.get(method, false);
if (originalNMethod == null) {
throw new AssertionError("Could not find original nmethod");
}
WHITE_BOX.relocateNMethodFromMethod(method, BlobType.MethodNonProfiled.id);
NMethod relocatedNMethod = NMethod.get(method, false);
if (relocatedNMethod == null) {
throw new AssertionError("Could not find relocated nmethod");
}
if (originalNMethod.address == relocatedNMethod.address) {
throw new AssertionError("Relocated nmethod same as original");
}
WHITE_BOX.deoptimizeAll();
WHITE_BOX.fullGC();
WHITE_BOX.fullGC();
WHITE_BOX.lockCompilation();
System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin);
System.out.flush();
}
public static long compiledMethod() {
return 0;
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright Amazon.com Inc. 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 <inttypes.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>
/**
* Callback for COMPILED_METHOD_LOAD event.
*/
JNIEXPORT void JNICALL
callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
jint code_size, const void* code_addr,
jint map_length, const jvmtiAddrLocationMap* map,
const void* compile_info) {
char* name = nullptr;
char* sig = nullptr;
if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
printf(" [Could not retrieve method name]\n");
fflush(stdout);
return;
}
printf("<COMPILED_METHOD_LOAD>: name: %s, code: 0x%016" PRIxPTR "\n",
name, (uintptr_t)code_addr);
fflush(stdout);
}
/**
* Callback for COMPILED_METHOD_UNLOAD event.
*/
JNIEXPORT void JNICALL
callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method,
const void* code_addr) {
char* name = nullptr;
char* sig = nullptr;
if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) {
printf(" [Could not retrieve method name]\n");
fflush(stdout);
return;
}
printf("<COMPILED_METHOD_UNLOAD>: name: %s, code: 0x%016" PRIxPTR "\n",
name, (uintptr_t)code_addr);
fflush(stdout);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
jvmtiEnv* jvmti = nullptr;
jvmtiError error;
if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) {
printf("Unable to access JVMTI!\n");
return JNI_ERR;
}
// Add required capabilities
jvmtiCapabilities caps;
memset(&caps, 0, sizeof(caps));
caps.can_generate_compiled_method_load_events = 1;
error = jvmti->AddCapabilities(&caps);
if (error != JVMTI_ERROR_NONE) {
printf("ERROR: Unable to add capabilities, error=%d\n", error);
return JNI_ERR;
}
// Set event callbacks
jvmtiEventCallbacks eventCallbacks;
memset(&eventCallbacks, 0, sizeof(eventCallbacks));
eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad;
eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload;
error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks));
if (error != JVMTI_ERROR_NONE) {
printf("ERROR: Unable to set event callbacks, error=%d\n", error);
return JNI_ERR;
}
// Enable events
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr);
if (error != JVMTI_ERROR_NONE) {
printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error);
return JNI_ERR;
}
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr);
if (error != JVMTI_ERROR_NONE) {
printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error);
return JNI_ERR;
}
return JNI_OK;
}

View File

@ -490,6 +490,12 @@ public class WhiteBox {
Objects.requireNonNull(method);
return getNMethod0(method, isOsr);
}
private native void relocateNMethodFromMethod0(Executable method, int type);
public void relocateNMethodFromMethod(Executable method, int type) {
Objects.requireNonNull(method);
relocateNMethodFromMethod0(method, type);
}
public native void relocateNMethodFromAddr(long address, int type);
public native long allocateCodeBlob(int size, int type);
public long allocateCodeBlob(long size, int type) {
int intSize = (int) size;

View File

@ -46,18 +46,22 @@ public class CodeBlob {
return new CodeBlob(obj);
}
protected CodeBlob(Object[] obj) {
assert obj.length == 4;
assert obj.length == 6;
name = (String) obj[0];
size = (Integer) obj[1];
int blob_type_index = (Integer) obj[2];
code_blob_type = BlobType.values()[blob_type_index];
assert code_blob_type.id == (Integer) obj[2];
address = (Long) obj[3];
code_begin = (Long) obj[4];
isNMethod = (Boolean) obj[5];
}
public final String name;
public final int size;
public final BlobType code_blob_type;
public final long address;
public final long code_begin;
public final boolean isNMethod;
@Override
public String toString() {
return "CodeBlob{"
@ -65,6 +69,7 @@ public class CodeBlob {
+ ", size=" + size
+ ", code_blob_type=" + code_blob_type
+ ", address=" + address
+ ", code_begin=" + code_begin
+ '}';
}
}