mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8316694: Implement relocation of nmethod within CodeCache
Reviewed-by: kvn, eosterlund, never, eastigeevich, bulasevich
This commit is contained in:
parent
76dba201fa
commit
f740cd2aad
@ -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)) {
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 },
|
||||
|
||||
@ -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 " \
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
146
test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java
Normal file
146
test/hotspot/jtreg/compiler/whitebox/RelocateNMethod.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user