mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-15 08:34:30 +02:00
Merge
This commit is contained in:
commit
0b7c6dc57c
24 changed files with 889 additions and 136 deletions
|
@ -36,6 +36,7 @@
|
|||
#include "interpreter/bytecodes.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "opto/compile.hpp"
|
||||
#include "opto/node.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
|
|
|
@ -936,7 +936,7 @@ address Method::make_adapters(methodHandle mh, TRAPS) {
|
|||
// so making them eagerly shouldn't be too expensive.
|
||||
AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh);
|
||||
if (adapter == NULL ) {
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(), "out of space in CodeCache for adapters");
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(), "Out of space in CodeCache for adapters");
|
||||
}
|
||||
|
||||
mh->set_adapter_entry(adapter);
|
||||
|
|
|
@ -1115,6 +1115,9 @@ bool ConnectionGraph::complete_connection_graph(
|
|||
// Each 4 iterations calculate how much time it will take
|
||||
// to complete graph construction.
|
||||
time.stop();
|
||||
// Poll for requests from shutdown mechanism to quiesce compiler
|
||||
// because Connection graph construction may take long time.
|
||||
CompileBroker::maybe_block();
|
||||
double stop_time = time.seconds();
|
||||
double time_per_iter = (stop_time - start_time) / (double)SAMPLE_SIZE;
|
||||
double time_until_end = time_per_iter * (double)(java_objects_length - next);
|
||||
|
|
|
@ -2809,7 +2809,8 @@ Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj,
|
|||
*/
|
||||
Node* GraphKit::maybe_cast_profiled_obj(Node* obj,
|
||||
ciKlass* type,
|
||||
bool not_null) {
|
||||
bool not_null,
|
||||
SafePointNode* sfpt) {
|
||||
// type == NULL if profiling tells us this object is always null
|
||||
if (type != NULL) {
|
||||
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
|
||||
|
@ -2831,7 +2832,13 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj,
|
|||
ciKlass* exact_kls = type;
|
||||
Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0,
|
||||
&exact_obj);
|
||||
{
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
PreserveJVMState pjvms(&kit);
|
||||
kit.set_control(slow_ctl);
|
||||
kit.uncommon_trap(class_reason,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(slow_ctl);
|
||||
uncommon_trap(class_reason,
|
||||
|
|
|
@ -418,7 +418,8 @@ class GraphKit : public Phase {
|
|||
// Cast obj to type and emit guard unless we had too many traps here already
|
||||
Node* maybe_cast_profiled_obj(Node* obj,
|
||||
ciKlass* type,
|
||||
bool not_null = false);
|
||||
bool not_null = false,
|
||||
SafePointNode* sfpt = NULL);
|
||||
|
||||
// Cast obj to not-null on this path
|
||||
Node* cast_not_null(Node* obj, bool do_replace_in_map = true);
|
||||
|
|
|
@ -4697,10 +4697,6 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
Node* dest_offset = argument(3); // type: int
|
||||
Node* length = argument(4); // type: int
|
||||
|
||||
// Check for allocation before we add nodes that would confuse
|
||||
// tightly_coupled_allocation()
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL);
|
||||
|
||||
// The following tests must be performed
|
||||
// (1) src and dest are arrays.
|
||||
// (2) src and dest arrays must have elements of the same BasicType
|
||||
|
@ -4717,6 +4713,36 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
src = null_check(src, T_ARRAY);
|
||||
dest = null_check(dest, T_ARRAY);
|
||||
|
||||
// Check for allocation before we add nodes that would confuse
|
||||
// tightly_coupled_allocation()
|
||||
AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL);
|
||||
|
||||
SafePointNode* sfpt = NULL;
|
||||
if (alloc != NULL) {
|
||||
// The JVM state for uncommon traps between the allocation and
|
||||
// arraycopy is set to the state before the allocation: if the
|
||||
// initialization is performed by the array copy, we don't want to
|
||||
// go back to the interpreter with an unitialized array.
|
||||
JVMState* old_jvms = alloc->jvms();
|
||||
JVMState* jvms = old_jvms->clone_shallow(C);
|
||||
uint size = alloc->req();
|
||||
sfpt = new SafePointNode(size, jvms);
|
||||
jvms->set_map(sfpt);
|
||||
for (uint i = 0; i < size; i++) {
|
||||
sfpt->init_req(i, alloc->in(i));
|
||||
}
|
||||
// re-push array length for deoptimization
|
||||
sfpt->ins_req(jvms->stkoff() + jvms->sp(), alloc->in(AllocateNode::ALength));
|
||||
jvms->set_sp(jvms->sp()+1);
|
||||
jvms->set_monoff(jvms->monoff()+1);
|
||||
jvms->set_scloff(jvms->scloff()+1);
|
||||
jvms->set_endoff(jvms->endoff()+1);
|
||||
jvms->set_should_reexecute(true);
|
||||
|
||||
sfpt->set_i_o(map()->i_o());
|
||||
sfpt->set_memory(map()->memory());
|
||||
}
|
||||
|
||||
bool notest = false;
|
||||
|
||||
const Type* src_type = _gvn.type(src);
|
||||
|
@ -4762,14 +4788,14 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
if (could_have_src && could_have_dest) {
|
||||
// This is going to pay off so emit the required guards
|
||||
if (!has_src) {
|
||||
src = maybe_cast_profiled_obj(src, src_k);
|
||||
src = maybe_cast_profiled_obj(src, src_k, true, sfpt);
|
||||
src_type = _gvn.type(src);
|
||||
top_src = src_type->isa_aryptr();
|
||||
has_src = (top_src != NULL && top_src->klass() != NULL);
|
||||
src_spec = true;
|
||||
}
|
||||
if (!has_dest) {
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k);
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k, true);
|
||||
dest_type = _gvn.type(dest);
|
||||
top_dest = dest_type->isa_aryptr();
|
||||
has_dest = (top_dest != NULL && top_dest->klass() != NULL);
|
||||
|
@ -4810,10 +4836,10 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
if (could_have_src && could_have_dest) {
|
||||
// If we can have both exact types, emit the missing guards
|
||||
if (could_have_src && !src_spec) {
|
||||
src = maybe_cast_profiled_obj(src, src_k);
|
||||
src = maybe_cast_profiled_obj(src, src_k, true, sfpt);
|
||||
}
|
||||
if (could_have_dest && !dest_spec) {
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k);
|
||||
dest = maybe_cast_profiled_obj(dest, dest_k, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4855,13 +4881,28 @@ bool LibraryCallKit::inline_arraycopy() {
|
|||
Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass);
|
||||
|
||||
if (not_subtype_ctrl != top()) {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(not_subtype_ctrl);
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(stopped(), "Should be stopped");
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
PreserveJVMState pjvms(&kit);
|
||||
kit.set_control(not_subtype_ctrl);
|
||||
kit.uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(kit.stopped(), "Should be stopped");
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(not_subtype_ctrl);
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(stopped(), "Should be stopped");
|
||||
}
|
||||
}
|
||||
{
|
||||
if (sfpt != NULL) {
|
||||
GraphKit kit(sfpt->jvms());
|
||||
kit.set_control(_gvn.transform(slow_region));
|
||||
kit.uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_make_not_entrant);
|
||||
assert(kit.stopped(), "Should be stopped");
|
||||
} else {
|
||||
PreserveJVMState pjvms(this);
|
||||
set_control(_gvn.transform(slow_region));
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
|
|
|
@ -1431,7 +1431,7 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) {
|
|||
Node* castii = in1->raw_out(i);
|
||||
if (castii->in(0) != NULL && castii->in(0)->in(0) != NULL && castii->in(0)->in(0)->is_If()) {
|
||||
Node* ifnode = castii->in(0)->in(0);
|
||||
if (ifnode->in(1) != NULL && ifnode->in(1)->in(1) == use) {
|
||||
if (ifnode->in(1) != NULL && ifnode->in(1)->is_Bool() && ifnode->in(1)->in(1) == use) {
|
||||
// Reprocess a CastII node that may depend on an
|
||||
// opaque node value when the opaque node is
|
||||
// removed. In case it carries a dependency we can do
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "runtime/interfaceSupport.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/sweeper.hpp"
|
||||
#include "runtime/javaCalls.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "utilities/array.hpp"
|
||||
|
@ -771,8 +772,8 @@ WB_ENTRY(void, WB_UnlockCompilation(JNIEnv* env, jobject o))
|
|||
mo.notify_all();
|
||||
WB_END
|
||||
|
||||
void WhiteBox::force_sweep() {
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
|
||||
void WhiteBox::sweeper_thread_entry(JavaThread* thread, TRAPS) {
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
|
||||
{
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
NMethodSweeper::_should_sweep = true;
|
||||
|
@ -780,8 +781,37 @@ void WhiteBox::force_sweep() {
|
|||
NMethodSweeper::possibly_sweep();
|
||||
}
|
||||
|
||||
WB_ENTRY(void, WB_ForceNMethodSweep(JNIEnv* env, jobject o))
|
||||
WhiteBox::force_sweep();
|
||||
JavaThread* WhiteBox::create_sweeper_thread(TRAPS) {
|
||||
// create sweeper thread w/ custom entry -- one iteration instead of loop
|
||||
CodeCacheSweeperThread* sweeper_thread = new CodeCacheSweeperThread();
|
||||
sweeper_thread->set_entry_point(&WhiteBox::sweeper_thread_entry);
|
||||
|
||||
// create j.l.Thread object and associate it w/ sweeper thread
|
||||
{
|
||||
// inherit deamon property from current thread
|
||||
bool is_daemon = java_lang_Thread::is_daemon(JavaThread::current()->threadObj());
|
||||
|
||||
HandleMark hm(THREAD);
|
||||
Handle thread_group(THREAD, Universe::system_thread_group());
|
||||
const char* name = "WB Sweeper thread";
|
||||
sweeper_thread->allocate_threadObj(thread_group, name, is_daemon, THREAD);
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker mu(Threads_lock, THREAD);
|
||||
Threads::add(sweeper_thread);
|
||||
}
|
||||
return sweeper_thread;
|
||||
}
|
||||
|
||||
WB_ENTRY(jobject, WB_ForceNMethodSweep(JNIEnv* env, jobject o))
|
||||
JavaThread* sweeper_thread = WhiteBox::create_sweeper_thread(Thread::current());
|
||||
if (sweeper_thread == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
jobject result = JNIHandles::make_local(env, sweeper_thread->threadObj());
|
||||
Thread::start(sweeper_thread);
|
||||
return result;
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString))
|
||||
|
@ -831,12 +861,12 @@ WB_ENTRY(jstring, WB_GetCPUFeatures(JNIEnv* env, jobject o))
|
|||
WB_END
|
||||
|
||||
int WhiteBox::get_blob_type(const CodeBlob* code) {
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
|
||||
return CodeCache::get_code_heap(code)->code_blob_type();
|
||||
}
|
||||
|
||||
CodeHeap* WhiteBox::get_code_heap(int blob_type) {
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
|
||||
return CodeCache::get_code_heap(blob_type);
|
||||
}
|
||||
|
||||
|
@ -912,7 +942,7 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
|
|||
WB_END
|
||||
|
||||
CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) {
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
|
||||
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
|
||||
BufferBlob* blob;
|
||||
int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob));
|
||||
if (full_size < size) {
|
||||
|
@ -921,10 +951,10 @@ CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) {
|
|||
{
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type);
|
||||
::new (blob) BufferBlob("WB::DummyBlob", full_size);
|
||||
}
|
||||
// Track memory usage statistic after releasing CodeCache_lock
|
||||
MemoryService::track_code_cache_memory_usage();
|
||||
::new (blob) BufferBlob("WB::DummyBlob", full_size);
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
@ -1235,7 +1265,7 @@ static JNINativeMethod methods[] = {
|
|||
{CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures },
|
||||
{CC"getNMethod", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
|
||||
(void*)&WB_GetNMethod },
|
||||
{CC"forceNMethodSweep", CC"()V", (void*)&WB_ForceNMethodSweep },
|
||||
{CC"forceNMethodSweep0", CC"()Ljava/lang/Thread;", (void*)&WB_ForceNMethodSweep },
|
||||
{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 },
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "prims/jni.h"
|
||||
|
||||
#include "utilities/exceptions.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
|
@ -56,6 +57,7 @@
|
|||
|
||||
class CodeBlob;
|
||||
class CodeHeap;
|
||||
class JavaThread;
|
||||
|
||||
class WhiteBox : public AllStatic {
|
||||
private:
|
||||
|
@ -68,7 +70,8 @@ class WhiteBox : public AllStatic {
|
|||
Symbol* signature_symbol);
|
||||
static const char* lookup_jstring(const char* field_name, oop object);
|
||||
static bool lookup_bool(const char* field_name, oop object);
|
||||
static void force_sweep();
|
||||
static void sweeper_thread_entry(JavaThread* thread, TRAPS);
|
||||
static JavaThread* create_sweeper_thread(TRAPS);
|
||||
static int get_blob_type(const CodeBlob* code);
|
||||
static CodeHeap* get_code_heap(int blob_type);
|
||||
static CodeBlob* allocate_code_blob(int blob_type, int size);
|
||||
|
|
|
@ -142,9 +142,6 @@ long NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number o
|
|||
long NMethodSweeper::_time_counter = 0; // Virtual time used to periodically invoke sweeper
|
||||
long NMethodSweeper::_last_sweep = 0; // Value of _time_counter when the last sweep happened
|
||||
int NMethodSweeper::_seen = 0; // Nof. nmethod we have currently processed in current pass of CodeCache
|
||||
int NMethodSweeper::_flushed_count = 0; // Nof. nmethods flushed in current sweep
|
||||
int NMethodSweeper::_zombified_count = 0; // Nof. nmethods made zombie in current sweep
|
||||
int NMethodSweeper::_marked_for_reclamation_count = 0; // Nof. nmethods marked for reclaim in current sweep
|
||||
|
||||
volatile bool NMethodSweeper::_should_sweep = true; // Indicates if we should invoke the sweeper
|
||||
volatile int NMethodSweeper::_bytes_changed = 0; // Counts the total nmethod size if the nmethod changed from:
|
||||
|
@ -161,6 +158,7 @@ Tickspan NMethodSweeper::_total_time_this_sweep; // Total time thi
|
|||
Tickspan NMethodSweeper::_peak_sweep_time; // Peak time for a full sweep
|
||||
Tickspan NMethodSweeper::_peak_sweep_fraction_time; // Peak time sweeping one fraction
|
||||
|
||||
Monitor* NMethodSweeper::_stat_lock = new Monitor(Mutex::special, "Sweeper::Statistics", true);
|
||||
|
||||
class MarkActivationClosure: public CodeBlobClosure {
|
||||
public:
|
||||
|
@ -370,9 +368,10 @@ void NMethodSweeper::sweep_code_cache() {
|
|||
ResourceMark rm;
|
||||
Ticks sweep_start_counter = Ticks::now();
|
||||
|
||||
_flushed_count = 0;
|
||||
_zombified_count = 0;
|
||||
_marked_for_reclamation_count = 0;
|
||||
int flushed_count = 0;
|
||||
int zombified_count = 0;
|
||||
int marked_for_reclamation_count = 0;
|
||||
int flushed_c2_count = 0;
|
||||
|
||||
if (PrintMethodFlushing && Verbose) {
|
||||
tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods());
|
||||
|
@ -386,10 +385,8 @@ void NMethodSweeper::sweep_code_cache() {
|
|||
{
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
// The last invocation iterates until there are no more nmethods
|
||||
while (!_current.end()) {
|
||||
swept_count++;
|
||||
handle_safepoint_request();
|
||||
// Since we will give up the CodeCache_lock, always skip ahead
|
||||
// to the next nmethod. Other blobs can be deleted by other
|
||||
// threads but nmethods are only reclaimed by the sweeper.
|
||||
|
@ -399,9 +396,32 @@ void NMethodSweeper::sweep_code_cache() {
|
|||
// Now ready to process nmethod and give up CodeCache_lock
|
||||
{
|
||||
MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
freed_memory += process_nmethod(nm);
|
||||
int size = nm->total_size();
|
||||
bool is_c2_method = nm->is_compiled_by_c2();
|
||||
|
||||
MethodStateChange type = process_nmethod(nm);
|
||||
switch (type) {
|
||||
case Flushed:
|
||||
freed_memory += size;
|
||||
++flushed_count;
|
||||
if (is_c2_method) {
|
||||
++flushed_c2_count;
|
||||
}
|
||||
break;
|
||||
case MarkedForReclamation:
|
||||
++marked_for_reclamation_count;
|
||||
break;
|
||||
case MadeZombie:
|
||||
++zombified_count;
|
||||
break;
|
||||
case None:
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
_seen++;
|
||||
handle_safepoint_request();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,21 +429,25 @@ void NMethodSweeper::sweep_code_cache() {
|
|||
|
||||
const Ticks sweep_end_counter = Ticks::now();
|
||||
const Tickspan sweep_time = sweep_end_counter - sweep_start_counter;
|
||||
_total_time_sweeping += sweep_time;
|
||||
_total_time_this_sweep += sweep_time;
|
||||
_peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time);
|
||||
_total_flushed_size += freed_memory;
|
||||
_total_nof_methods_reclaimed += _flushed_count;
|
||||
|
||||
{
|
||||
MutexLockerEx mu(_stat_lock, Mutex::_no_safepoint_check_flag);
|
||||
_total_time_sweeping += sweep_time;
|
||||
_total_time_this_sweep += sweep_time;
|
||||
_peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time);
|
||||
_total_flushed_size += freed_memory;
|
||||
_total_nof_methods_reclaimed += flushed_count;
|
||||
_total_nof_c2_methods_reclaimed += flushed_c2_count;
|
||||
_peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep);
|
||||
}
|
||||
EventSweepCodeCache event(UNTIMED);
|
||||
if (event.should_commit()) {
|
||||
event.set_starttime(sweep_start_counter);
|
||||
event.set_endtime(sweep_end_counter);
|
||||
event.set_sweepIndex(_traversals);
|
||||
event.set_sweptCount(swept_count);
|
||||
event.set_flushedCount(_flushed_count);
|
||||
event.set_markedCount(_marked_for_reclamation_count);
|
||||
event.set_zombifiedCount(_zombified_count);
|
||||
event.set_flushedCount(flushed_count);
|
||||
event.set_markedCount(marked_for_reclamation_count);
|
||||
event.set_zombifiedCount(zombified_count);
|
||||
event.commit();
|
||||
}
|
||||
|
||||
|
@ -433,7 +457,6 @@ void NMethodSweeper::sweep_code_cache() {
|
|||
}
|
||||
#endif
|
||||
|
||||
_peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep);
|
||||
log_sweep("finished");
|
||||
|
||||
// Sweeper is the only case where memory is released, check here if it
|
||||
|
@ -511,10 +534,11 @@ void NMethodSweeper::release_nmethod(nmethod* nm) {
|
|||
nm->flush();
|
||||
}
|
||||
|
||||
int NMethodSweeper::process_nmethod(nmethod* nm) {
|
||||
NMethodSweeper::MethodStateChange NMethodSweeper::process_nmethod(nmethod* nm) {
|
||||
assert(nm != NULL, "sanity");
|
||||
assert(!CodeCache_lock->owned_by_self(), "just checking");
|
||||
|
||||
int freed_memory = 0;
|
||||
MethodStateChange result = None;
|
||||
// Make sure this nmethod doesn't get unloaded during the scan,
|
||||
// since safepoints may happen during acquired below locks.
|
||||
NMethodMarker nmm(nm);
|
||||
|
@ -529,7 +553,7 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
nm->cleanup_inline_caches();
|
||||
SWEEP(nm);
|
||||
}
|
||||
return freed_memory;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (nm->is_zombie()) {
|
||||
|
@ -541,12 +565,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
if (PrintMethodFlushing && Verbose) {
|
||||
tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (marked for reclamation) being flushed", nm->compile_id(), nm);
|
||||
}
|
||||
freed_memory = nm->total_size();
|
||||
if (nm->is_compiled_by_c2()) {
|
||||
_total_nof_c2_methods_reclaimed++;
|
||||
}
|
||||
release_nmethod(nm);
|
||||
_flushed_count++;
|
||||
assert(result == None, "sanity");
|
||||
result = Flushed;
|
||||
} else {
|
||||
if (PrintMethodFlushing && Verbose) {
|
||||
tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (zombie) being marked for reclamation", nm->compile_id(), nm);
|
||||
|
@ -554,8 +575,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
nm->mark_for_reclamation();
|
||||
// Keep track of code cache state change
|
||||
_bytes_changed += nm->total_size();
|
||||
_marked_for_reclamation_count++;
|
||||
SWEEP(nm);
|
||||
assert(result == None, "sanity");
|
||||
result = MarkedForReclamation;
|
||||
}
|
||||
} else if (nm->is_not_entrant()) {
|
||||
// If there are no current activations of this method on the
|
||||
|
@ -576,8 +598,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
}
|
||||
// Code cache state change is tracked in make_zombie()
|
||||
nm->make_zombie();
|
||||
_zombified_count++;
|
||||
SWEEP(nm);
|
||||
assert(result == None, "sanity");
|
||||
result = MadeZombie;
|
||||
}
|
||||
assert(nm->is_zombie(), "nmethod must be zombie");
|
||||
} else {
|
||||
|
@ -594,17 +617,15 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
if (nm->is_osr_method()) {
|
||||
SWEEP(nm);
|
||||
// No inline caches will ever point to osr methods, so we can just remove it
|
||||
freed_memory = nm->total_size();
|
||||
if (nm->is_compiled_by_c2()) {
|
||||
_total_nof_c2_methods_reclaimed++;
|
||||
}
|
||||
release_nmethod(nm);
|
||||
_flushed_count++;
|
||||
assert(result == None, "sanity");
|
||||
result = Flushed;
|
||||
} else {
|
||||
// Code cache state change is tracked in make_zombie()
|
||||
nm->make_zombie();
|
||||
_zombified_count++;
|
||||
SWEEP(nm);
|
||||
assert(result == None, "sanity");
|
||||
result = MadeZombie;
|
||||
}
|
||||
} else {
|
||||
possibly_flush(nm);
|
||||
|
@ -613,7 +634,7 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
|
|||
nm->cleanup_inline_caches();
|
||||
SWEEP(nm);
|
||||
}
|
||||
return freed_memory;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -56,15 +56,18 @@ class WhiteBox;
|
|||
class NMethodSweeper : public AllStatic {
|
||||
friend class WhiteBox;
|
||||
private:
|
||||
enum MethodStateChange {
|
||||
None,
|
||||
MadeZombie,
|
||||
MarkedForReclamation,
|
||||
Flushed
|
||||
};
|
||||
static long _traversals; // Stack scan count, also sweep ID.
|
||||
static long _total_nof_code_cache_sweeps; // Total number of full sweeps of the code cache
|
||||
static long _time_counter; // Virtual time used to periodically invoke sweeper
|
||||
static long _last_sweep; // Value of _time_counter when the last sweep happened
|
||||
static NMethodIterator _current; // Current nmethod
|
||||
static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache
|
||||
static int _flushed_count; // Nof. nmethods flushed in current sweep
|
||||
static int _zombified_count; // Nof. nmethods made zombie in current sweep
|
||||
static int _marked_for_reclamation_count; // Nof. nmethods marked for reclaim in current sweep
|
||||
|
||||
static volatile int _sweep_started; // Flag to control conc sweeper
|
||||
static volatile bool _should_sweep; // Indicates if we should invoke the sweeper
|
||||
|
@ -83,8 +86,10 @@ class NMethodSweeper : public AllStatic {
|
|||
static Tickspan _peak_sweep_time; // Peak time for a full sweep
|
||||
static Tickspan _peak_sweep_fraction_time; // Peak time sweeping one fraction
|
||||
|
||||
static int process_nmethod(nmethod *nm);
|
||||
static void release_nmethod(nmethod* nm);
|
||||
static Monitor* _stat_lock;
|
||||
|
||||
static MethodStateChange process_nmethod(nmethod *nm);
|
||||
static void release_nmethod(nmethod* nm);
|
||||
|
||||
static void init_sweeper_log() NOT_DEBUG_RETURN;
|
||||
static bool wait_for_stack_scanning();
|
||||
|
|
|
@ -1076,7 +1076,7 @@ static void reset_vm_info_property(TRAPS) {
|
|||
}
|
||||
|
||||
|
||||
void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
|
||||
void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name,
|
||||
bool daemon, TRAPS) {
|
||||
assert(thread_group.not_null(), "thread group should be specified");
|
||||
assert(threadObj() == NULL, "should only create Java thread object once");
|
||||
|
@ -1123,8 +1123,8 @@ void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
|
|||
return;
|
||||
}
|
||||
|
||||
KlassHandle group(this, SystemDictionary::ThreadGroup_klass());
|
||||
Handle threadObj(this, this->threadObj());
|
||||
KlassHandle group(THREAD, SystemDictionary::ThreadGroup_klass());
|
||||
Handle threadObj(THREAD, this->threadObj());
|
||||
|
||||
JavaCalls::call_special(&result,
|
||||
thread_group,
|
||||
|
@ -1133,8 +1133,6 @@ void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
|
|||
vmSymbols::thread_void_signature(),
|
||||
threadObj, // Arg 1
|
||||
THREAD);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// NamedThread -- non-JavaThread subclasses with multiple
|
||||
|
|
|
@ -749,6 +749,7 @@ typedef void (*ThreadFunction)(JavaThread*, TRAPS);
|
|||
|
||||
class JavaThread: public Thread {
|
||||
friend class VMStructs;
|
||||
friend class WhiteBox;
|
||||
private:
|
||||
JavaThread* _next; // The next thread in the Threads list
|
||||
oop _threadObj; // The Java level thread object
|
||||
|
@ -1000,7 +1001,7 @@ class JavaThread: public Thread {
|
|||
ThreadFunction entry_point() const { return _entry_point; }
|
||||
|
||||
// Allocates a new Java level thread object for this thread. thread_name may be NULL.
|
||||
void allocate_threadObj(Handle thread_group, char* thread_name, bool daemon, TRAPS);
|
||||
void allocate_threadObj(Handle thread_group, const char* thread_name, bool daemon, TRAPS);
|
||||
|
||||
// Last frame anchor routines
|
||||
|
||||
|
|
244
hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java
Normal file
244
hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8064703
|
||||
* @summary Deoptimization between array allocation and arraycopy may result in non initialized array
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:TypeProfileLevel=020 TestArrayCopyNoInit
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class TestArrayCopyNoInit {
|
||||
|
||||
static int[] m1(int[] src) {
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m2(Object src, boolean flag) {
|
||||
Class tmp = src.getClass();
|
||||
if (flag) {
|
||||
return null;
|
||||
}
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (ArrayStoreException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m3(int[] src, int src_offset) {
|
||||
int tmp = src[0];
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, src_offset, dest, 0, 10);
|
||||
} catch (IndexOutOfBoundsException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static int[] m4(int[] src, int length) {
|
||||
int tmp = src[0];
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, length);
|
||||
} catch (IndexOutOfBoundsException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static TestArrayCopyNoInit[] m5(Object[] src) {
|
||||
Object tmp = src[0];
|
||||
TestArrayCopyNoInit[] dest = new TestArrayCopyNoInit[10];
|
||||
System.arraycopy(src, 0, dest, 0, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static class A {
|
||||
}
|
||||
|
||||
static class B extends A {
|
||||
}
|
||||
|
||||
static class C extends B {
|
||||
}
|
||||
|
||||
static class D extends C {
|
||||
}
|
||||
|
||||
static class E extends D {
|
||||
}
|
||||
|
||||
static class F extends E {
|
||||
}
|
||||
|
||||
static class G extends F {
|
||||
}
|
||||
|
||||
static class H extends G {
|
||||
}
|
||||
|
||||
static class I extends H {
|
||||
}
|
||||
|
||||
static H[] m6(Object[] src) {
|
||||
Object tmp = src[0];
|
||||
H[] dest = new H[10];
|
||||
System.arraycopy(src, 0, dest, 0, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static Object m7_src(Object src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
static int[] m7(Object src, boolean flag) {
|
||||
Class tmp = src.getClass();
|
||||
if (flag) {
|
||||
return null;
|
||||
}
|
||||
src = m7_src(src);
|
||||
int[] dest = new int[10];
|
||||
try {
|
||||
System.arraycopy(src, 0, dest, 0, 10);
|
||||
} catch (ArrayStoreException npe) {
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static public void main(String[] args) throws Throwable {
|
||||
boolean success = true;
|
||||
int[] src = new int[10];
|
||||
TestArrayCopyNoInit[] src2 = new TestArrayCopyNoInit[10];
|
||||
int[] res = null;
|
||||
TestArrayCopyNoInit[] res2 = null;
|
||||
Object src_obj = new Object();
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m1(src);
|
||||
}
|
||||
|
||||
res = m1(null);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following NPE");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
if ((i%2) == 0) {
|
||||
m2(src, false);
|
||||
} else {
|
||||
m2(src_obj, true);
|
||||
}
|
||||
}
|
||||
res = m2(src_obj, false);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed array check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m3(src, 0);
|
||||
}
|
||||
res = m3(src, -1);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed src offset check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m4(src, 0);
|
||||
}
|
||||
res = m4(src, -1);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed length check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m5(src2);
|
||||
}
|
||||
res2 = m5(new Object[10]);
|
||||
for (int i = 0; i < res2.length; i++) {
|
||||
if (res2[i] != null) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed type check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
H[] src3 = new H[10];
|
||||
I b = new I();
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
m6(src3);
|
||||
}
|
||||
H[] res3 = m6(new Object[10]);
|
||||
for (int i = 0; i < res3.length; i++) {
|
||||
if (res3[i] != null) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed full type check");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
if ((i%2) == 0) {
|
||||
m7(src, false);
|
||||
} else {
|
||||
m7(src_obj, true);
|
||||
}
|
||||
}
|
||||
res = m7(src_obj, false);
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
if (res[i] != 0) {
|
||||
success = false;
|
||||
System.out.println("Uninitialized array following failed type check with return value profiling");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Some tests failed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,22 +24,29 @@
|
|||
/*
|
||||
* @test
|
||||
* @bug 8023014
|
||||
* @summary Test ensures that there is no crash if there is not enough ReservedCodeacacheSize
|
||||
* @summary Test ensures that there is no crash if there is not enough ReservedCodeCacheSize
|
||||
* to initialize all compiler threads. The option -Xcomp gives the VM more time to
|
||||
* to trigger the old bug.
|
||||
* trigger the old bug.
|
||||
* @library /testlibrary
|
||||
*/
|
||||
import com.oracle.java.testlibrary.*;
|
||||
import static com.oracle.java.testlibrary.Asserts.assertTrue;
|
||||
|
||||
public class SmallCodeCacheStartup {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:ReservedCodeCacheSize=3m",
|
||||
"-XX:CICompilerCount=64",
|
||||
"-Xcomp",
|
||||
"-version");
|
||||
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
|
||||
analyzer.shouldHaveExitValue(0);
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:ReservedCodeCacheSize=3m",
|
||||
"-XX:CICompilerCount=64",
|
||||
"-Xcomp",
|
||||
"-version");
|
||||
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
|
||||
try {
|
||||
analyzer.shouldHaveExitValue(0);
|
||||
} catch (RuntimeException e) {
|
||||
// Error occurred during initialization, did we run out of adapter space?
|
||||
assertTrue(analyzer.getOutput().contains("VirtualMachineError: Out of space in CodeCache"),
|
||||
"Expected VirtualMachineError");
|
||||
}
|
||||
|
||||
System.out.println("TEST PASSED");
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,11 @@ import java.util.ArrayList;
|
|||
import sun.hotspot.WhiteBox;
|
||||
import sun.hotspot.code.BlobType;
|
||||
import com.oracle.java.testlibrary.Asserts;
|
||||
import com.oracle.java.testlibrary.InfiniteLoop;
|
||||
|
||||
/*
|
||||
* @test AllocationCodeBlobTest
|
||||
* @bug 8059624
|
||||
* @bug 8059624 8064669
|
||||
* @library /testlibrary /testlibrary/whitebox
|
||||
* @build AllocationCodeBlobTest
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
|
@ -53,11 +54,32 @@ public class AllocationCodeBlobTest {
|
|||
|
||||
public static void main(String[] args) {
|
||||
// check that Sweeper handels dummy blobs correctly
|
||||
new ForcedSweeper(500).start();
|
||||
Thread t = new Thread(
|
||||
new InfiniteLoop(WHITE_BOX::forceNMethodSweep, 1L),
|
||||
"ForcedSweeper");
|
||||
t.setDaemon(true);
|
||||
System.out.println("Starting " + t.getName());
|
||||
t.start();
|
||||
|
||||
EnumSet<BlobType> blobTypes = BlobType.getAvailable();
|
||||
for (BlobType type : blobTypes) {
|
||||
new AllocationCodeBlobTest(type).test();
|
||||
}
|
||||
|
||||
// check that deoptimization works well w/ dummy blobs
|
||||
t = new Thread(
|
||||
new InfiniteLoop(WHITE_BOX::deoptimizeAll, 1L),
|
||||
"Deoptimize Thread");
|
||||
t.setDaemon(true);
|
||||
System.out.println("Starting " + t.getName());
|
||||
t.start();
|
||||
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
for (BlobType type : blobTypes) {
|
||||
long addr = WHITE_BOX.allocateCodeBlob(SIZE, type.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final BlobType type;
|
||||
|
@ -105,24 +127,4 @@ public class AllocationCodeBlobTest {
|
|||
private long getUsage() {
|
||||
return bean.getUsage().getUsed();
|
||||
}
|
||||
|
||||
private static class ForcedSweeper extends Thread {
|
||||
private final int millis;
|
||||
public ForcedSweeper(int millis) {
|
||||
super("ForcedSweeper");
|
||||
setDaemon(true);
|
||||
this.millis = millis;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
WHITE_BOX.forceNMethodSweep();
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
97
hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java
Normal file
97
hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import sun.hotspot.code.BlobType;
|
||||
|
||||
import com.oracle.java.testlibrary.Asserts;
|
||||
import com.oracle.java.testlibrary.InfiniteLoop;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8059624 8064669
|
||||
* @library /testlibrary /testlibrary/whitebox
|
||||
* @build ForceNMethodSweepTest
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:-TieredCompilation -XX:+WhiteBoxAPI
|
||||
* -XX:CompileCommand=compileonly,SimpleTestCase$Helper::*
|
||||
* ForceNMethodSweepTest
|
||||
* @summary testing of WB::forceNMethodSweep
|
||||
*/
|
||||
public class ForceNMethodSweepTest extends CompilerWhiteBoxTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
CompilerWhiteBoxTest.main(ForceNMethodSweepTest::new, args);
|
||||
}
|
||||
private final EnumSet<BlobType> blobTypes;
|
||||
private ForceNMethodSweepTest(TestCase testCase) {
|
||||
super(testCase);
|
||||
// to prevent inlining of #method
|
||||
WHITE_BOX.testSetDontInlineMethod(method, true);
|
||||
blobTypes = BlobType.getAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void test() throws Exception {
|
||||
checkNotCompiled();
|
||||
guaranteedSweep();
|
||||
int usage = getTotalUsage();
|
||||
|
||||
compile();
|
||||
checkCompiled();
|
||||
int afterCompilation = getTotalUsage();
|
||||
Asserts.assertGT(afterCompilation, usage,
|
||||
"compilation should increase usage");
|
||||
|
||||
guaranteedSweep();
|
||||
int afterSweep = getTotalUsage();
|
||||
Asserts.assertLTE(afterSweep, afterCompilation,
|
||||
"sweep shouldn't increase usage");
|
||||
|
||||
deoptimize();
|
||||
guaranteedSweep();
|
||||
int afterDeoptAndSweep = getTotalUsage();
|
||||
Asserts.assertLT(afterDeoptAndSweep, afterSweep,
|
||||
"sweep after deoptimization should decrease usage");
|
||||
}
|
||||
|
||||
private int getTotalUsage() {
|
||||
int usage = 0;
|
||||
for (BlobType type : blobTypes) {
|
||||
usage += type.getMemoryPool().getUsage().getUsed();
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
private void guaranteedSweep() {
|
||||
// not entrant -> ++stack_traversal_mark -> zombie -> reclamation -> flushed
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
WHITE_BOX.fullGC();
|
||||
WHITE_BOX.forceNMethodSweep();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,7 +75,7 @@ public class GetNMethodTest extends CompilerWhiteBoxTest {
|
|||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
checkBlockType(nmethod, BlobType.MethodNonProfiled);
|
||||
checkBlockType(nmethod, BlobType.MethodProfiled);
|
||||
break;
|
||||
default:
|
||||
throw new Error("unexpected comp level " + nmethod);
|
||||
|
|
|
@ -68,8 +68,7 @@ public class Asserts {
|
|||
* @see #assertLessThan(T, T, String)
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertLessThan(T lhs, T rhs) {
|
||||
String msg = "Expected that " + format(lhs) + " < " + format(rhs);
|
||||
assertLessThan(lhs, rhs, msg);
|
||||
assertLessThan(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +80,7 @@ public class Asserts {
|
|||
* @throws RuntimeException if the assertion isn't valid.
|
||||
*/
|
||||
public static <T extends Comparable<T>>void assertLessThan(T lhs, T rhs, String msg) {
|
||||
assertTrue(compare(lhs, rhs, msg) < 0, msg);
|
||||
assertTrue(compare(lhs, rhs, msg) < 0, getMessage(lhs, rhs, "<", msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,8 +107,7 @@ public class Asserts {
|
|||
* @see #assertLessThanOrEqual(T, T, String)
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs) {
|
||||
String msg = "Expected that " + format(lhs) + " <= " + format(rhs);
|
||||
assertLessThanOrEqual(lhs, rhs, msg);
|
||||
assertLessThanOrEqual(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,7 +119,7 @@ public class Asserts {
|
|||
* @throws RuntimeException if the assertion isn't valid.
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs, String msg) {
|
||||
assertTrue(compare(lhs, rhs, msg) <= 0, msg);
|
||||
assertTrue(compare(lhs, rhs, msg) <= 0, getMessage(lhs, rhs, "<=", msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,8 +146,7 @@ public class Asserts {
|
|||
* @see #assertEquals(T, T, String)
|
||||
*/
|
||||
public static void assertEquals(Object lhs, Object rhs) {
|
||||
String msg = "Expected " + format(lhs) + " to equal " + format(rhs);
|
||||
assertEquals(lhs, rhs, msg);
|
||||
assertEquals(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,7 +163,7 @@ public class Asserts {
|
|||
error(msg);
|
||||
}
|
||||
} else {
|
||||
assertTrue(lhs.equals(rhs), msg);
|
||||
assertTrue(lhs.equals(rhs), getMessage(lhs, rhs, "==", msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,8 +191,7 @@ public class Asserts {
|
|||
* @see #assertGreaterThanOrEqual(T, T, String)
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs) {
|
||||
String msg = "Expected that " + format(lhs) + " >= " + format(rhs);
|
||||
assertGreaterThanOrEqual(lhs, rhs, msg);
|
||||
assertGreaterThanOrEqual(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,7 +203,7 @@ public class Asserts {
|
|||
* @throws RuntimeException if the assertion isn't valid.
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs, String msg) {
|
||||
assertTrue(compare(lhs, rhs, msg) >= 0, msg);
|
||||
assertTrue(compare(lhs, rhs, msg) >= 0, getMessage(lhs, rhs, ">=", msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,8 +230,7 @@ public class Asserts {
|
|||
* @see #assertGreaterThan(T, T, String)
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs) {
|
||||
String msg = "Expected that " + format(lhs) + " > " + format(rhs);
|
||||
assertGreaterThan(lhs, rhs, msg);
|
||||
assertGreaterThan(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,7 +242,7 @@ public class Asserts {
|
|||
* @throws RuntimeException if the assertion isn't valid.
|
||||
*/
|
||||
public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs, String msg) {
|
||||
assertTrue(compare(lhs, rhs, msg) > 0, msg);
|
||||
assertTrue(compare(lhs, rhs, msg) > 0, getMessage(lhs, rhs, ">", msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,8 +269,7 @@ public class Asserts {
|
|||
* @see #assertNotEquals(T, T, String)
|
||||
*/
|
||||
public static void assertNotEquals(Object lhs, Object rhs) {
|
||||
String msg = "Expected " + format(lhs) + " to not equal " + format(rhs);
|
||||
assertNotEquals(lhs, rhs, msg);
|
||||
assertNotEquals(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,7 +286,7 @@ public class Asserts {
|
|||
error(msg);
|
||||
}
|
||||
} else {
|
||||
assertFalse(lhs.equals(rhs), msg);
|
||||
assertFalse(lhs.equals(rhs), getMessage(lhs, rhs,"!=", msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,4 +444,8 @@ public class Asserts {
|
|||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
private static String getMessage(Object lhs, Object rhs, String op, String msg) {
|
||||
return (msg == null ? "" : msg + " ") + "(assert failed: " + format(lhs) + " " + op + " " + format(rhs) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.oracle.java.testlibrary;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class which runs another Runnable in infinite loop with certain pauses
|
||||
* between cycles.
|
||||
*/
|
||||
public class InfiniteLoop implements Runnable {
|
||||
private final Runnable target;
|
||||
private final long mills;
|
||||
|
||||
|
||||
/**
|
||||
* @param target a target to run in a loop
|
||||
* @param mills the length of pause time in milliseconds
|
||||
* @throws NullPointerException if target is null
|
||||
* @throws IllegalArgumentException if the value of millis is negative
|
||||
*/
|
||||
public InfiniteLoop(Runnable target, long mills) {
|
||||
Objects.requireNonNull(target);
|
||||
if (mills < 0) {
|
||||
throw new IllegalArgumentException("mills < 0");
|
||||
}
|
||||
this.target = target;
|
||||
this.mills = mills;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
target.run();
|
||||
Thread.sleep(mills);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
package com.oracle.java.testlibrary;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import com.oracle.java.testlibrary.Utils;
|
||||
|
||||
public class Platform {
|
||||
|
@ -97,29 +98,31 @@ public class Platform {
|
|||
|
||||
// Returns true for sparc and sparcv9.
|
||||
public static boolean isSparc() {
|
||||
return isArch("sparc");
|
||||
return isArch("sparc.*");
|
||||
}
|
||||
|
||||
public static boolean isARM() {
|
||||
return isArch("arm");
|
||||
return isArch("arm.*");
|
||||
}
|
||||
|
||||
public static boolean isPPC() {
|
||||
return isArch("ppc");
|
||||
return isArch("ppc.*");
|
||||
}
|
||||
|
||||
public static boolean isX86() {
|
||||
// On Linux it's 'i386', Windows 'x86'
|
||||
return (isArch("i386") || isArch("x86"));
|
||||
// On Linux it's 'i386', Windows 'x86' without '_64' suffix.
|
||||
return isArch("(i386)|(x86(?!_64))");
|
||||
}
|
||||
|
||||
public static boolean isX64() {
|
||||
// On OSX it's 'x86_64' and on other (Linux, Windows and Solaris) platforms it's 'amd64'
|
||||
return (isArch("amd64") || isArch("x86_64"));
|
||||
return isArch("(amd64)|(x86_64)");
|
||||
}
|
||||
|
||||
private static boolean isArch(String archname) {
|
||||
return osArch.toLowerCase().startsWith(archname.toLowerCase());
|
||||
private static boolean isArch(String archnameRE) {
|
||||
return Pattern.compile(archnameRE, Pattern.CASE_INSENSITIVE)
|
||||
.matcher(osArch)
|
||||
.matches();
|
||||
}
|
||||
|
||||
public static String getOsArch() {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.oracle.java.testlibrary;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Auxiliary class to run target w/ given timeout.
|
||||
*/
|
||||
public class TimeLimitedRunner implements Callable<Void> {
|
||||
private final long stoptime;
|
||||
private final long timeout;
|
||||
private final double factor;
|
||||
private final Callable<Boolean> target;
|
||||
|
||||
/**
|
||||
* @param timeout a timeout. zero means no time limitation
|
||||
* @param factor a multiplier used to estimate next iteration time
|
||||
* @param target a target to run
|
||||
* @throws NullPointerException if target is null
|
||||
* @throws IllegalArgumentException if timeout is negative or
|
||||
factor isn't positive
|
||||
*/
|
||||
public TimeLimitedRunner(long timeout, double factor,
|
||||
Callable<Boolean> target) {
|
||||
Objects.requireNonNull(target, "target must not be null");
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("timeout[" + timeout + "] < 0");
|
||||
}
|
||||
if (factor <= 0d) {
|
||||
throw new IllegalArgumentException("factor[" + factor + "] <= 0");
|
||||
}
|
||||
this.stoptime = System.currentTimeMillis() + timeout;
|
||||
this.timeout = timeout;
|
||||
this.factor = factor;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs @{linkplan target} while it returns true and timeout isn't exceeded
|
||||
*/
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
long maxDuration = 0L;
|
||||
long iterStart = System.currentTimeMillis();
|
||||
if (timeout != 0 && iterStart > stoptime) {
|
||||
return null;
|
||||
}
|
||||
while (target.call()) {
|
||||
if (timeout != 0) {
|
||||
long iterDuration = System.currentTimeMillis() - iterStart;
|
||||
maxDuration = Math.max(maxDuration, iterDuration);
|
||||
iterStart = System.currentTimeMillis();
|
||||
if (iterStart + (maxDuration * factor) > stoptime) {
|
||||
System.out.println("Not enough time to continue execution. "
|
||||
+ "Interrupted.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -154,7 +154,14 @@ public class WhiteBox {
|
|||
public native Object[] getNMethod(Executable method, boolean isOsr);
|
||||
public native long allocateCodeBlob(int size, int type);
|
||||
public native void freeCodeBlob(long addr);
|
||||
public native void forceNMethodSweep();
|
||||
public void forceNMethodSweep() {
|
||||
try {
|
||||
forceNMethodSweep0().join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
public native Thread forceNMethodSweep0();
|
||||
public native Object[] getCodeHeapEntries(int type);
|
||||
public native int getCompilationActivityMode();
|
||||
public native Object[] getCodeBlob(long addr);
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.oracle.java.testlibrary.Asserts;
|
||||
import com.oracle.java.testlibrary.Platform;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Verify that for each group of mutually exclusive predicates defined
|
||||
* in com.oracle.java.testlibrary.Platform one and only one predicate
|
||||
* evaluates to true.
|
||||
* @library /testlibrary
|
||||
* @run main TestMutuallyExclusivePlatformPredicates
|
||||
*/
|
||||
public class TestMutuallyExclusivePlatformPredicates {
|
||||
private static enum MethodGroup {
|
||||
ARCH("isARM", "isPPC", "isSparc", "isX86", "isX64"),
|
||||
BITNESS("is32bit", "is64bit"),
|
||||
OS("isLinux", "isSolaris", "isWindows", "isOSX"),
|
||||
VM_TYPE("isClient", "isServer", "isGraal", "isMinimal"),
|
||||
IGNORED("isEmbedded", "isDebugBuild");
|
||||
|
||||
public final List<String> methodNames;
|
||||
|
||||
private MethodGroup(String... methodNames) {
|
||||
this.methodNames = Collections.unmodifiableList(
|
||||
Arrays.asList(methodNames));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
EnumSet<MethodGroup> notIgnoredMethodGroups
|
||||
= EnumSet.complementOf(EnumSet.of(MethodGroup.IGNORED));
|
||||
|
||||
notIgnoredMethodGroups.forEach(
|
||||
TestMutuallyExclusivePlatformPredicates::verifyPredicates);
|
||||
|
||||
TestMutuallyExclusivePlatformPredicates.verifyCoverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that one and only one predicate method defined in
|
||||
* {@link com.oracle.java.testlibrary.Platform}, whose name included into
|
||||
* methodGroup will return {@code true}.
|
||||
* @param methodGroup The group of methods that should be tested.
|
||||
*/
|
||||
private static void verifyPredicates(MethodGroup methodGroup) {
|
||||
System.out.println("Verifying method group: " + methodGroup.name());
|
||||
long truePredicatesCount = methodGroup.methodNames.stream()
|
||||
.filter(TestMutuallyExclusivePlatformPredicates
|
||||
::evaluatePredicate)
|
||||
.count();
|
||||
|
||||
Asserts.assertEQ(truePredicatesCount, 1L, String.format(
|
||||
"Only one predicate from group %s should be evaluated to true "
|
||||
+ "(Actually %d predicates were evaluated to true).",
|
||||
methodGroup.name(), truePredicatesCount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that all predicates defined in
|
||||
* {@link com.oracle.java.testlibrary.Platform} were either tested or
|
||||
* explicitly ignored.
|
||||
*/
|
||||
private static void verifyCoverage() {
|
||||
Set<String> allMethods = new HashSet<>();
|
||||
for (MethodGroup group : MethodGroup.values()) {
|
||||
allMethods.addAll(group.methodNames);
|
||||
}
|
||||
|
||||
for (Method m : Platform.class.getMethods()) {
|
||||
if (m.getParameterCount() == 0
|
||||
&& m.getReturnType() == boolean.class) {
|
||||
Asserts.assertTrue(allMethods.contains(m.getName()),
|
||||
"All Platform's methods with signature '():Z' should "
|
||||
+ "be tested ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates predicate method with name {@code name} defined in
|
||||
* {@link com.oracle.java.testlibrary.Platform}.
|
||||
*
|
||||
* @param name The name of a predicate to be evaluated.
|
||||
* @return evaluated predicate's value.
|
||||
* @throws java.lang.Error if predicate is not defined or could not be
|
||||
* evaluated.
|
||||
*/
|
||||
private static boolean evaluatePredicate(String name) {
|
||||
try {
|
||||
System.out.printf("Trying to evaluate predicate with name %s%n",
|
||||
name);
|
||||
boolean value
|
||||
= (Boolean) Platform.class.getMethod(name).invoke(null);
|
||||
System.out.printf("Predicate evaluated to: %s%n", value);
|
||||
return value;
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new Error("Predicate with name " + name
|
||||
+ " is not defined in " + Platform.class.getName(), e);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new Error("Unable to evaluate predicate " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue