mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-09 14:38:42 +00:00
Merge
This commit is contained in:
commit
be20d6384d
@ -33,6 +33,7 @@
|
||||
#define ELF_NHDR Elf64_Nhdr
|
||||
#define ELF_DYN Elf64_Dyn
|
||||
#define ELF_ADDR Elf64_Addr
|
||||
#define ELF_AUXV Elf64_auxv_t
|
||||
|
||||
#define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
|
||||
@ -45,6 +46,7 @@
|
||||
#define ELF_NHDR Elf32_Nhdr
|
||||
#define ELF_DYN Elf32_Dyn
|
||||
#define ELF_ADDR Elf32_Addr
|
||||
#define ELF_AUXV Elf32_auxv_t
|
||||
|
||||
#define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
|
||||
|
||||
@ -642,6 +642,18 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) {
|
||||
if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) {
|
||||
return false;
|
||||
}
|
||||
} else if (notep->n_type == NT_AUXV) {
|
||||
// Get first segment from entry point
|
||||
ELF_AUXV *auxv = (ELF_AUXV *)descdata;
|
||||
while (auxv->a_type != AT_NULL) {
|
||||
if (auxv->a_type == AT_ENTRY) {
|
||||
// Set entry point address to address of dynamic section.
|
||||
// We will adjust it in read_exec_segments().
|
||||
ph->core->dynamic_addr = auxv->a_un.a_val;
|
||||
break;
|
||||
}
|
||||
auxv++;
|
||||
}
|
||||
}
|
||||
p = descdata + ROUNDUP(notep->n_descsz, 4);
|
||||
}
|
||||
@ -832,7 +844,13 @@ static bool read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehdr) {
|
||||
|
||||
// from PT_DYNAMIC we want to read address of first link_map addr
|
||||
case PT_DYNAMIC: {
|
||||
ph->core->dynamic_addr = exec_php->p_vaddr;
|
||||
if (exec_ehdr->e_type == ET_EXEC) {
|
||||
ph->core->dynamic_addr = exec_php->p_vaddr;
|
||||
} else { // ET_DYN
|
||||
// dynamic_addr has entry point of executable.
|
||||
// Thus we should substract it.
|
||||
ph->core->dynamic_addr += exec_php->p_vaddr - exec_ehdr->e_entry;
|
||||
}
|
||||
print_debug("address of _DYNAMIC is 0x%lx\n", ph->core->dynamic_addr);
|
||||
break;
|
||||
}
|
||||
@ -1030,8 +1048,9 @@ struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || exec_ehdr.e_type != ET_EXEC) {
|
||||
print_debug("executable file is not a valid ELF ET_EXEC file\n");
|
||||
if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true ||
|
||||
((exec_ehdr.e_type != ET_EXEC) && (exec_ehdr.e_type != ET_DYN))) {
|
||||
print_debug("executable file is not a valid ELF file\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
@ -240,6 +240,11 @@ public:
|
||||
assert(result == CodeHeap::contains(p), "");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool contains_blob(const CodeBlob* blob) const {
|
||||
return CodeHeap::contains(blob->code_begin());
|
||||
}
|
||||
|
||||
AOTCompiledMethod* find_aot(address p) const;
|
||||
|
||||
virtual void* find_start(void* p) const;
|
||||
|
||||
@ -3995,10 +3995,14 @@ bool GraphBuilder::try_method_handle_inline(ciMethod* callee, bool ignore_return
|
||||
ciMethod* target = type->as_ObjectType()->constant_value()->as_method_handle()->get_vmtarget();
|
||||
// We don't do CHA here so only inline static and statically bindable methods.
|
||||
if (target->is_static() || target->can_be_statically_bound()) {
|
||||
Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
|
||||
ignore_return = ignore_return || (callee->return_type()->is_void() && !target->return_type()->is_void());
|
||||
if (try_inline(target, /*holder_known*/ true, ignore_return, bc)) {
|
||||
return true;
|
||||
if (ciMethod::is_consistent_info(callee, target)) {
|
||||
Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual;
|
||||
ignore_return = ignore_return || (callee->return_type()->is_void() && !target->return_type()->is_void());
|
||||
if (try_inline(target, /*holder_known*/ true, ignore_return, bc)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
print_inlining(target, "signatures mismatch", /*success*/ false);
|
||||
}
|
||||
} else {
|
||||
print_inlining(target, "not static or statically bindable", /*success*/ false);
|
||||
@ -4026,6 +4030,8 @@ bool GraphBuilder::try_method_handle_inline(ciMethod* callee, bool ignore_return
|
||||
if (try_method_handle_inline(target, ignore_return)) {
|
||||
return true;
|
||||
}
|
||||
} else if (!ciMethod::is_consistent_info(callee, target)) {
|
||||
print_inlining(target, "signatures mismatch", /*success*/ false);
|
||||
} else {
|
||||
ciSignature* signature = target->signature();
|
||||
const int receiver_skip = target->is_static() ? 0 : 1;
|
||||
|
||||
@ -1409,6 +1409,97 @@ void ciMethod::print_impl(outputStream* st) {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
static BasicType erase_to_word_type(BasicType bt) {
|
||||
if (is_subword_type(bt)) return T_INT;
|
||||
if (bt == T_ARRAY) return T_OBJECT;
|
||||
return bt;
|
||||
}
|
||||
|
||||
static bool basic_types_match(ciType* t1, ciType* t2) {
|
||||
if (t1 == t2) return true;
|
||||
return erase_to_word_type(t1->basic_type()) == erase_to_word_type(t2->basic_type());
|
||||
}
|
||||
|
||||
bool ciMethod::is_consistent_info(ciMethod* declared_method, ciMethod* resolved_method) {
|
||||
bool invoke_through_mh_intrinsic = declared_method->is_method_handle_intrinsic() &&
|
||||
!resolved_method->is_method_handle_intrinsic();
|
||||
|
||||
if (!invoke_through_mh_intrinsic) {
|
||||
// Method name & descriptor should stay the same.
|
||||
// Signatures may reference unloaded types and thus they may be not strictly equal.
|
||||
ciSymbol* declared_signature = declared_method->signature()->as_symbol();
|
||||
ciSymbol* resolved_signature = resolved_method->signature()->as_symbol();
|
||||
|
||||
return (declared_method->name()->equals(resolved_method->name())) &&
|
||||
(declared_signature->equals(resolved_signature));
|
||||
}
|
||||
|
||||
ciMethod* linker = declared_method;
|
||||
ciMethod* target = resolved_method;
|
||||
// Linkers have appendix argument which is not passed to callee.
|
||||
int has_appendix = MethodHandles::has_member_arg(linker->intrinsic_id()) ? 1 : 0;
|
||||
if (linker->arg_size() != (target->arg_size() + has_appendix)) {
|
||||
return false; // argument slot count mismatch
|
||||
}
|
||||
|
||||
ciSignature* linker_sig = linker->signature();
|
||||
ciSignature* target_sig = target->signature();
|
||||
|
||||
if (linker_sig->count() + (linker->is_static() ? 0 : 1) !=
|
||||
target_sig->count() + (target->is_static() ? 0 : 1) + has_appendix) {
|
||||
return false; // argument count mismatch
|
||||
}
|
||||
|
||||
int sbase = 0, rbase = 0;
|
||||
switch (linker->intrinsic_id()) {
|
||||
case vmIntrinsics::_linkToVirtual:
|
||||
case vmIntrinsics::_linkToInterface:
|
||||
case vmIntrinsics::_linkToSpecial: {
|
||||
if (target->is_static()) {
|
||||
return false;
|
||||
}
|
||||
if (linker_sig->type_at(0)->is_primitive_type()) {
|
||||
return false; // receiver should be an oop
|
||||
}
|
||||
sbase = 1; // skip receiver
|
||||
break;
|
||||
}
|
||||
case vmIntrinsics::_linkToStatic: {
|
||||
if (!target->is_static()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case vmIntrinsics::_invokeBasic: {
|
||||
if (target->is_static()) {
|
||||
if (target_sig->type_at(0)->is_primitive_type()) {
|
||||
return false; // receiver should be an oop
|
||||
}
|
||||
rbase = 1; // skip receiver
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(target_sig->count() - rbase == linker_sig->count() - sbase - has_appendix, "argument count mismatch");
|
||||
int arg_count = target_sig->count() - rbase;
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
if (!basic_types_match(linker_sig->type_at(sbase + i), target_sig->type_at(rbase + i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Only check the return type if the symbolic info has non-void return type.
|
||||
// I.e. the return value of the resolved method can be dropped.
|
||||
if (!linker->return_type()->is_void() &&
|
||||
!basic_types_match(linker->return_type(), target->return_type())) {
|
||||
return false;
|
||||
}
|
||||
return true; // no mismatch found
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
#if INCLUDE_TRACE
|
||||
TraceStructCalleeMethod ciMethod::to_trace_struct() const {
|
||||
TraceStructCalleeMethod result;
|
||||
|
||||
@ -353,6 +353,8 @@ class ciMethod : public ciMetadata {
|
||||
void print_name(outputStream* st = tty);
|
||||
void print_short_name(outputStream* st = tty);
|
||||
|
||||
static bool is_consistent_info(ciMethod* declared_method, ciMethod* resolved_method);
|
||||
|
||||
#if INCLUDE_TRACE
|
||||
TraceStructCalleeMethod to_trace_struct() const;
|
||||
#endif
|
||||
|
||||
@ -1152,21 +1152,26 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
|
||||
Symbol* h_name = k->name();
|
||||
assert(class_name == NULL || class_name == h_name, "name mismatch");
|
||||
|
||||
bool define_succeeded = false;
|
||||
// Add class just loaded
|
||||
// If a class loader supports parallel classloading handle parallel define requests
|
||||
// find_or_define_instance_class may return a different InstanceKlass
|
||||
if (is_parallelCapable(class_loader)) {
|
||||
instanceKlassHandle defined_k = find_or_define_instance_class(h_name, class_loader, k, CHECK_NULL);
|
||||
if (k() == defined_k()) {
|
||||
// we have won over other concurrent threads (if any) that are
|
||||
// competing to define the same class.
|
||||
define_succeeded = true;
|
||||
instanceKlassHandle defined_k = find_or_define_instance_class(h_name, class_loader, k, THREAD);
|
||||
if (!HAS_PENDING_EXCEPTION && defined_k() != k()) {
|
||||
// If a parallel capable class loader already defined this class, register 'k' for cleanup.
|
||||
assert(defined_k.not_null(), "Should have a klass if there's no exception");
|
||||
loader_data->add_to_deallocate_list(k());
|
||||
k = defined_k;
|
||||
}
|
||||
k = defined_k;
|
||||
} else {
|
||||
define_instance_class(k, CHECK_NULL);
|
||||
define_succeeded = true;
|
||||
define_instance_class(k, THREAD);
|
||||
}
|
||||
|
||||
// If defining the class throws an exception register 'k' for cleanup.
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
assert(k.not_null(), "Must have an instance klass here!");
|
||||
loader_data->add_to_deallocate_list(k());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Make sure we have an entry in the SystemDictionary on success
|
||||
@ -1518,8 +1523,16 @@ instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Ha
|
||||
// find_or_define_instance_class may return a different InstanceKlass
|
||||
if (!k.is_null()) {
|
||||
instanceKlassHandle defined_k =
|
||||
find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
|
||||
k = defined_k;
|
||||
find_or_define_instance_class(class_name, class_loader, k, THREAD);
|
||||
if (!HAS_PENDING_EXCEPTION && defined_k() != k()) {
|
||||
// If a parallel capable class loader already defined this class, register 'k' for cleanup.
|
||||
assert(defined_k.not_null(), "Should have a klass if there's no exception");
|
||||
loader_data->add_to_deallocate_list(k());
|
||||
k = defined_k;
|
||||
} else if (HAS_PENDING_EXCEPTION) {
|
||||
loader_data->add_to_deallocate_list(k());
|
||||
return nh;
|
||||
}
|
||||
}
|
||||
return k;
|
||||
} else {
|
||||
|
||||
@ -417,7 +417,7 @@ void CodeCache::add_heap(ReservedSpace rs, const char* name, int code_blob_type)
|
||||
CodeHeap* CodeCache::get_code_heap(const CodeBlob* cb) {
|
||||
assert(cb != NULL, "CodeBlob is null");
|
||||
FOR_ALL_HEAPS(heap) {
|
||||
if ((*heap)->contains(cb->code_begin())) {
|
||||
if ((*heap)->contains_blob(cb)) {
|
||||
return *heap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,11 +304,10 @@ template <class T, class Filter> class CodeBlobIterator : public StackObj {
|
||||
// If set to NULL, initialized by first call to next()
|
||||
_code_blob = (CodeBlob*)nm;
|
||||
if (nm != NULL) {
|
||||
address start = nm->code_begin();
|
||||
while(!(*_heap)->contains(start)) {
|
||||
while(!(*_heap)->contains_blob(_code_blob)) {
|
||||
++_heap;
|
||||
}
|
||||
assert((*_heap)->contains(start), "match not found");
|
||||
assert((*_heap)->contains_blob(_code_blob), "match not found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -190,6 +190,10 @@ void* CodeHeap::allocate(size_t instance_size) {
|
||||
if (block != NULL) {
|
||||
assert(block->length() >= number_of_segments && block->length() < number_of_segments + CodeCacheMinBlockLength, "sanity check");
|
||||
assert(!block->free(), "must be marked free");
|
||||
guarantee((char*) block >= _memory.low_boundary() && (char*) block < _memory.high(),
|
||||
"The newly allocated block " INTPTR_FORMAT " is not within the heap "
|
||||
"starting with " INTPTR_FORMAT " and ending with " INTPTR_FORMAT,
|
||||
p2i(block), p2i(_memory.low_boundary()), p2i(_memory.high()));
|
||||
DEBUG_ONLY(memset((void*)block->allocated_space(), badCodeHeapNewVal, instance_size));
|
||||
_max_allocated_capacity = MAX2(_max_allocated_capacity, allocated_capacity());
|
||||
_blob_count++;
|
||||
@ -204,6 +208,10 @@ void* CodeHeap::allocate(size_t instance_size) {
|
||||
HeapBlock* b = block_at(_next_segment);
|
||||
b->initialize(number_of_segments);
|
||||
_next_segment += number_of_segments;
|
||||
guarantee((char*) b >= _memory.low_boundary() && (char*) block < _memory.high(),
|
||||
"The newly allocated block " INTPTR_FORMAT " is not within the heap "
|
||||
"starting with " INTPTR_FORMAT " and ending with " INTPTR_FORMAT,
|
||||
p2i(b), p2i(_memory.low_boundary()), p2i(_memory.high()));
|
||||
DEBUG_ONLY(memset((void *)b->allocated_space(), badCodeHeapNewVal, instance_size));
|
||||
_max_allocated_capacity = MAX2(_max_allocated_capacity, allocated_capacity());
|
||||
_blob_count++;
|
||||
@ -219,6 +227,10 @@ void CodeHeap::deallocate(void* p) {
|
||||
// Find start of HeapBlock
|
||||
HeapBlock* b = (((HeapBlock *)p) - 1);
|
||||
assert(b->allocated_space() == p, "sanity check");
|
||||
guarantee((char*) b >= _memory.low_boundary() && (char*) b < _memory.high(),
|
||||
"The block to be deallocated " INTPTR_FORMAT " is not within the heap "
|
||||
"starting with " INTPTR_FORMAT " and ending with " INTPTR_FORMAT,
|
||||
p2i(b), p2i(_memory.low_boundary()), p2i(_memory.high()));
|
||||
DEBUG_ONLY(memset((void *)b->allocated_space(), badCodeHeapFreeVal,
|
||||
segments_to_size(b->length()) - sizeof(HeapBlock)));
|
||||
add_to_freelist(b);
|
||||
|
||||
@ -153,7 +153,9 @@ class CodeHeap : public CHeapObj<mtCode> {
|
||||
char* high() const { return _memory.high(); }
|
||||
char* high_boundary() const { return _memory.high_boundary(); }
|
||||
|
||||
virtual bool contains(const void* p) const { return low_boundary() <= p && p < high(); }
|
||||
virtual bool contains(const void* p) const { return low_boundary() <= p && p < high(); }
|
||||
virtual bool contains_blob(const CodeBlob* blob) const { return low_boundary() <= (char*) blob && (char*) blob < high(); }
|
||||
|
||||
virtual void* find_start(void* p) const; // returns the block containing p or NULL
|
||||
virtual CodeBlob* find_blob_unsafe(void* start) const;
|
||||
size_t alignment_unit() const; // alignment of any block
|
||||
|
||||
@ -46,7 +46,7 @@ const TypeFunc* CallGenerator::tf() const {
|
||||
return TypeFunc::make(method());
|
||||
}
|
||||
|
||||
bool CallGenerator::is_inlined_mh_linker(JVMState* jvms, ciMethod* callee) {
|
||||
bool CallGenerator::is_inlined_method_handle_intrinsic(JVMState* jvms, ciMethod* callee) {
|
||||
ciMethod* symbolic_info = jvms->method()->get_method_at_bci(jvms->bci());
|
||||
return symbolic_info->is_method_handle_intrinsic() && !callee->is_method_handle_intrinsic();
|
||||
}
|
||||
@ -142,7 +142,7 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) {
|
||||
}
|
||||
|
||||
CallStaticJavaNode *call = new CallStaticJavaNode(kit.C, tf(), target, method(), kit.bci());
|
||||
if (is_inlined_mh_linker(jvms, method())) {
|
||||
if (is_inlined_method_handle_intrinsic(jvms, method())) {
|
||||
// To be able to issue a direct call and skip a call to MH.linkTo*/invokeBasic adapter,
|
||||
// additional information about the method being invoked should be attached
|
||||
// to the call site to make resolution logic work
|
||||
@ -241,7 +241,7 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) {
|
||||
address target = SharedRuntime::get_resolve_virtual_call_stub();
|
||||
// Normal inline cache used for call
|
||||
CallDynamicJavaNode *call = new CallDynamicJavaNode(tf(), target, method(), _vtable_index, kit.bci());
|
||||
if (is_inlined_mh_linker(jvms, method())) {
|
||||
if (is_inlined_method_handle_intrinsic(jvms, method())) {
|
||||
// To be able to issue a direct call (optimized virtual or virtual)
|
||||
// and skip a call to MH.linkTo*/invokeBasic adapter, additional information
|
||||
// about the method being invoked should be attached to the call site to
|
||||
@ -785,8 +785,7 @@ JVMState* PredictedCallGenerator::generate(JVMState* jvms) {
|
||||
|
||||
|
||||
CallGenerator* CallGenerator::for_method_handle_call(JVMState* jvms, ciMethod* caller, ciMethod* callee, bool delayed_forbidden) {
|
||||
assert(callee->is_method_handle_intrinsic() ||
|
||||
callee->is_compiled_lambda_form(), "for_method_handle_call mismatch");
|
||||
assert(callee->is_method_handle_intrinsic(), "for_method_handle_call mismatch");
|
||||
bool input_not_const;
|
||||
CallGenerator* cg = CallGenerator::for_method_handle_inline(jvms, caller, callee, input_not_const);
|
||||
Compile* C = Compile::current();
|
||||
@ -826,6 +825,13 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
const TypeOopPtr* oop_ptr = receiver->bottom_type()->is_oopptr();
|
||||
ciMethod* target = oop_ptr->const_oop()->as_method_handle()->get_vmtarget();
|
||||
const int vtable_index = Method::invalid_vtable_index;
|
||||
|
||||
if (!ciMethod::is_consistent_info(callee, target)) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"signatures mismatch");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CallGenerator* cg = C->call_generator(target, vtable_index,
|
||||
false /* call_does_dispatch */,
|
||||
jvms,
|
||||
@ -833,9 +839,8 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
PROB_ALWAYS);
|
||||
return cg;
|
||||
} else {
|
||||
const char* msg = "receiver not constant";
|
||||
if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg);
|
||||
C->log_inline_failure(msg);
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"receiver not constant");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -852,6 +857,12 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
const TypeOopPtr* oop_ptr = member_name->bottom_type()->is_oopptr();
|
||||
ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget();
|
||||
|
||||
if (!ciMethod::is_consistent_info(callee, target)) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"signatures mismatch");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// In lambda forms we erase signature types to avoid resolving issues
|
||||
// involving class loaders. When we optimize a method handle invoke
|
||||
// to a direct call we must cast the receiver and arguments to its
|
||||
@ -912,9 +923,8 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
speculative_receiver_type);
|
||||
return cg;
|
||||
} else {
|
||||
const char* msg = "member_name not constant";
|
||||
if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg);
|
||||
C->log_inline_failure(msg);
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"member_name not constant");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -170,7 +170,12 @@ class CallGenerator : public ResourceObj {
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_inlined_mh_linker(JVMState* jvms, ciMethod* m);
|
||||
static void print_inlining_failure(Compile* C, ciMethod* callee, int inline_level, int bci, const char* msg) {
|
||||
print_inlining(C, callee, inline_level, bci, msg);
|
||||
C->log_inline_failure(msg);
|
||||
}
|
||||
|
||||
static bool is_inlined_method_handle_intrinsic(JVMState* jvms, ciMethod* m);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -400,92 +400,10 @@ bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* kl
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool check_type(ciType* t1, ciType* t2) {
|
||||
// Either oop-oop or prim-prim pair.
|
||||
if (t1->is_primitive_type() && t2->is_primitive_type()) {
|
||||
return t1->size() == t2->size(); // argument sizes should match
|
||||
} else {
|
||||
return !t1->is_primitive_type() && !t2->is_primitive_type(); // oop-oop
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_inlined_mh_linker_info(ciMethod* symbolic_info, ciMethod* resolved_method) {
|
||||
assert(symbolic_info->is_method_handle_intrinsic(), "sanity");
|
||||
assert(!resolved_method->is_method_handle_intrinsic(), "sanity");
|
||||
|
||||
if (!symbolic_info->is_loaded() || !resolved_method->is_loaded()) {
|
||||
return true; // Don't compare unloaded methods.
|
||||
}
|
||||
// Linkers have appendix argument which is not passed to callee.
|
||||
int has_appendix = MethodHandles::has_member_arg(symbolic_info->intrinsic_id()) ? 1 : 0;
|
||||
if (symbolic_info->arg_size() != (resolved_method->arg_size() + has_appendix)) {
|
||||
return false; // Total size of arguments on stack mismatch.
|
||||
}
|
||||
if (!symbolic_info->return_type()->is_void()) {
|
||||
// Only check the return type if the symbolic method is not void
|
||||
// i.e. the return value of the resolved method can be dropped
|
||||
if (!check_type(symbolic_info->return_type(), resolved_method->return_type())) {
|
||||
return false; // Return value size or type mismatch encountered.
|
||||
}
|
||||
}
|
||||
|
||||
switch (symbolic_info->intrinsic_id()) {
|
||||
case vmIntrinsics::_linkToVirtual:
|
||||
case vmIntrinsics::_linkToInterface:
|
||||
case vmIntrinsics::_linkToSpecial: {
|
||||
if (resolved_method->is_static()) return false;
|
||||
break;
|
||||
}
|
||||
case vmIntrinsics::_linkToStatic: {
|
||||
if (!resolved_method->is_static()) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ciSignature* symbolic_sig = symbolic_info->signature();
|
||||
ciSignature* resolved_sig = resolved_method->signature();
|
||||
|
||||
if (symbolic_sig->count() + (symbolic_info->is_static() ? 0 : 1) !=
|
||||
resolved_sig->count() + (resolved_method->is_static() ? 0 : 1) + has_appendix) {
|
||||
return false; // Argument count mismatch
|
||||
}
|
||||
|
||||
int sbase = 0, rbase = 0;
|
||||
int arg_count = MIN2(symbolic_sig->count() - has_appendix, resolved_sig->count());
|
||||
ciType* recv_type = NULL;
|
||||
if (symbolic_info->is_static() && !resolved_method->is_static()) {
|
||||
recv_type = symbolic_sig->type_at(0);
|
||||
sbase = 1;
|
||||
} else if (!symbolic_info->is_static() && resolved_method->is_static()) {
|
||||
recv_type = resolved_sig->type_at(0);
|
||||
rbase = 1;
|
||||
}
|
||||
if (recv_type != NULL && recv_type->is_primitive_type()) {
|
||||
return false; // Receiver should be an oop.
|
||||
}
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
if (!check_type(symbolic_sig->type_at(sbase + i), resolved_sig->type_at(rbase + i))) {
|
||||
return false; // Argument size or type mismatch encountered.
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_call_consistent_with_jvms(JVMState* jvms, CallGenerator* cg) {
|
||||
static bool check_call_consistency(JVMState* jvms, CallGenerator* cg) {
|
||||
ciMethod* symbolic_info = jvms->method()->get_method_at_bci(jvms->bci());
|
||||
ciMethod* resolved_method = cg->method();
|
||||
|
||||
if (CallGenerator::is_inlined_mh_linker(jvms, resolved_method)) {
|
||||
return check_inlined_mh_linker_info(symbolic_info, resolved_method);
|
||||
} else {
|
||||
// Method name & descriptor should stay the same.
|
||||
return (symbolic_info->get_Method()->name() == resolved_method->get_Method()->name()) &&
|
||||
(symbolic_info->get_Method()->signature() == resolved_method->get_Method()->signature());
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_call_consistency(JVMState* jvms, CallGenerator* cg) {
|
||||
if (!is_call_consistent_with_jvms(jvms, cg)) {
|
||||
if (!ciMethod::is_consistent_info(symbolic_info, resolved_method)) {
|
||||
tty->print_cr("JVMS:");
|
||||
jvms->dump();
|
||||
tty->print_cr("Bytecode info:");
|
||||
|
||||
@ -1486,6 +1486,10 @@ jvmtiEnv *jvmti;
|
||||
<description>
|
||||
Get the current thread.
|
||||
The current thread is the Java programming language thread which has called the function.
|
||||
The function may return <code>NULL</code> in the start phase if the
|
||||
<internallink id="jvmtiCapabilities.can_generate_early_vmstart">
|
||||
<code>can_generate_early_vmstart</code></internallink> capability is enabled
|
||||
and the <code>java.lang.Thread</code> class has not been initialized yet.
|
||||
<p/>
|
||||
Note that most <jvmti/> functions that take a thread
|
||||
as an argument will accept <code>NULL</code> to mean
|
||||
@ -1498,7 +1502,7 @@ jvmtiEnv *jvmti;
|
||||
<param id="thread_ptr">
|
||||
<outptr><jthread/></outptr>
|
||||
<description>
|
||||
On return, points to the current thread.
|
||||
On return, points to the current thread, or <code>NULL</code>.
|
||||
</description>
|
||||
</param>
|
||||
</parameters>
|
||||
@ -14735,6 +14739,11 @@ typedef void (JNICALL *jvmtiEventVMInit)
|
||||
Clarified can_redefine_any_classes, can_retransform_any_classes and IsModifiableClass API to
|
||||
disallow some implementation defined classes.
|
||||
</change>
|
||||
<change date="12 February 2017" version="9.0.0">
|
||||
Minor update for GetCurrentThread function:
|
||||
- The function may return NULL in the start phase if the
|
||||
can_generate_early_vmstart capability is enabled.
|
||||
</change>
|
||||
</changehistory>
|
||||
|
||||
</specification>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2017, 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
|
||||
@ -51,6 +51,7 @@
|
||||
#include "services/serviceUtil.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
#if INCLUDE_ALL_GCS
|
||||
#include "gc/g1/g1SATBCardTableModRefBS.hpp"
|
||||
#include "gc/parallel/parallelScavengeHeap.hpp"
|
||||
#endif // INCLUDE_ALL_GCS
|
||||
|
||||
@ -1534,6 +1535,14 @@ class TagObjectCollector : public JvmtiTagHashmapEntryClosure {
|
||||
if (_tags[i] == entry->tag()) {
|
||||
oop o = entry->object();
|
||||
assert(o != NULL && Universe::heap()->is_in_reserved(o), "sanity check");
|
||||
#if INCLUDE_ALL_GCS
|
||||
if (UseG1GC) {
|
||||
// The reference in this tag map could be the only (implicitly weak)
|
||||
// reference to that object. If we hand it out, we need to keep it live wrt
|
||||
// SATB marking similar to other j.l.ref.Reference referents.
|
||||
G1SATBCardTableModRefBS::enqueue(o);
|
||||
}
|
||||
#endif
|
||||
jobject ref = JNIHandles::make_local(JavaThread::current(), o);
|
||||
_object_results->append(ref);
|
||||
_tag_results->append((uint64_t)entry->tag());
|
||||
|
||||
@ -3374,7 +3374,7 @@ public:
|
||||
"Code cache expansion size (in bytes)") \
|
||||
range(0, max_uintx) \
|
||||
\
|
||||
develop_pd(uintx, CodeCacheMinBlockLength, \
|
||||
diagnostic_pd(uintx, CodeCacheMinBlockLength, \
|
||||
"Minimum number of segments in a code cache block") \
|
||||
range(1, 100) \
|
||||
\
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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 ReturnBlobToWrongHeapTest
|
||||
* @key stress
|
||||
* @summary Test if VM attempts to return code blobs to an incorrect code heap or to outside of the code cache.
|
||||
* @library /test/lib /
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
*
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:+WhiteBoxAPI
|
||||
* -XX:CompileCommand=dontinline,compiler.codecache.stress.Helper$TestCase::method
|
||||
* -XX:+SegmentedCodeCache
|
||||
* -XX:ReservedCodeCacheSize=16M
|
||||
* -XX:CodeCacheMinBlockLength=1
|
||||
* compiler.codecache.stress.ReturnBlobToWrongHeapTest
|
||||
*/
|
||||
|
||||
package compiler.codecache.stress;
|
||||
|
||||
import sun.hotspot.code.BlobType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ReturnBlobToWrongHeapTest {
|
||||
private static final long largeBlobSize = Helper.WHITE_BOX.getUintxVMFlag("ReservedCodeCacheSize") >> 6;
|
||||
private static final long codeCacheMinBlockLength = Helper.WHITE_BOX.getUintxVMFlag("CodeCacheMinBlockLength");
|
||||
private static final BlobType[] BLOB_TYPES = BlobType.getAvailable().toArray(new BlobType[0]);
|
||||
|
||||
// Allocate blob in first code heap (the code heap with index 0).
|
||||
private static long allocate(int size) {
|
||||
return Helper.WHITE_BOX.allocateCodeBlob(size, BLOB_TYPES[0].id);
|
||||
}
|
||||
|
||||
// Free blob.
|
||||
private static void free(long address) {
|
||||
Helper.WHITE_BOX.freeCodeBlob(address);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (codeCacheMinBlockLength == 1) {
|
||||
// Fill first code heap with large blobs until allocation fails.
|
||||
long address;
|
||||
while ((address = allocate((int)largeBlobSize)) != 0) {
|
||||
}
|
||||
|
||||
// Allocate segment-sized blocks in first code heap.
|
||||
long lastSegmentSizedAddress = 0; // Address of the last segment-sized blob allocated
|
||||
while ((address = allocate(0)) != 0) {
|
||||
lastSegmentSizedAddress = address;
|
||||
}
|
||||
|
||||
if (lastSegmentSizedAddress == 0) {
|
||||
throw new RuntimeException("Test failed: Not possible to allocate segment-sized blob");
|
||||
}
|
||||
|
||||
// Remove last segment-sized block from the first code heap.
|
||||
free(lastSegmentSizedAddress);
|
||||
} else {
|
||||
throw new RuntimeException("Test requires CodeCacheMinBlockLength==1; CodeCacheMinBlockLength is " +
|
||||
codeCacheMinBlockLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
hotspot/test/compiler/jsr292/InvokerSignatureMismatch.java
Normal file
56
hotspot/test/compiler/jsr292/InvokerSignatureMismatch.java
Normal file
@ -0,0 +1,56 @@
|
||||
package compiler.jsr292;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.MethodHandleHelper;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8166110
|
||||
* @library /test/lib / patches
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.vm.annotation
|
||||
*
|
||||
* @build java.base/java.lang.invoke.MethodHandleHelper
|
||||
* @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation
|
||||
* compiler.jsr292.InvokerSignatureMismatch
|
||||
*/
|
||||
public class InvokerSignatureMismatch {
|
||||
|
||||
static final MethodHandle INT_MH;
|
||||
|
||||
static {
|
||||
MethodHandle mhI = null;
|
||||
try {
|
||||
mhI = MethodHandles.lookup().findStatic(InvokerSignatureMismatch.class, "bodyI", MethodType.methodType(void.class, int.class));
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
INT_MH = mhI;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
for (int i = 0; i < 50_000; i++) { // OSR
|
||||
mainLink(i);
|
||||
mainInvoke(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void mainLink(int i) throws Throwable {
|
||||
Object name = MethodHandleHelper.internalMemberName(INT_MH);
|
||||
MethodHandleHelper.linkToStatic(INT_MH, (float) i, name);
|
||||
}
|
||||
|
||||
static void mainInvoke(int i) throws Throwable {
|
||||
MethodHandleHelper.invokeBasicV(INT_MH, (float) i);
|
||||
}
|
||||
|
||||
static int cnt = 0;
|
||||
static void bodyI(int x) {
|
||||
if ((x & 1023) == 0) { // already optimized x % 1024 == 0
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -43,6 +43,21 @@ public class MethodHandleHelper {
|
||||
mh.customize();
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static Object internalMemberName(MethodHandle mh) throws Throwable {
|
||||
return mh.internalMemberName();
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void linkToStatic(MethodHandle mh, float arg, Object name) throws Throwable {
|
||||
MethodHandle.linkToStatic(mh, arg, name);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void invokeBasicV(MethodHandle mh, float arg) throws Throwable {
|
||||
mh.invokeBasic(arg);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static Object invokeBasicL(MethodHandle mh) throws Throwable {
|
||||
return mh.invokeBasic();
|
||||
|
||||
380
hotspot/test/runtime/Metaspace/DefineClass.java
Normal file
380
hotspot/test/runtime/Metaspace/DefineClass.java
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (c) 2017 SAP SE. 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 8173743
|
||||
* @summary Failures during class definition can lead to memory leaks in metaspace
|
||||
* @library /test/lib
|
||||
* @run main/othervm test.DefineClass defineClass
|
||||
* @run main/othervm test.DefineClass defineSystemClass
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
-XX:+UnsyncloadClass -XX:+AllowParallelDefineClass
|
||||
test.DefineClass defineClassParallel
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
-XX:+UnsyncloadClass -XX:-AllowParallelDefineClass
|
||||
test.DefineClass defineClassParallel
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
-XX:-UnsyncloadClass -XX:+AllowParallelDefineClass
|
||||
test.DefineClass defineClassParallel
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
-XX:-UnsyncloadClass -XX:-AllowParallelDefineClass
|
||||
test.DefineClass defineClassParallel
|
||||
* @run main/othervm test.DefineClass redefineClass
|
||||
* @run main/othervm test.DefineClass redefineClassWithError
|
||||
* @author volker.simonis@gmail.com
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassDefinition;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class DefineClass {
|
||||
|
||||
private static Instrumentation instrumentation;
|
||||
|
||||
public void getID(CountDownLatch start, CountDownLatch stop) {
|
||||
String id = "AAAAAAAA";
|
||||
System.out.println(id);
|
||||
try {
|
||||
// Signal that we've entered the activation..
|
||||
start.countDown();
|
||||
//..and wait until we can leave it.
|
||||
stop.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println(id);
|
||||
return;
|
||||
}
|
||||
|
||||
private static class MyThread extends Thread {
|
||||
private DefineClass dc;
|
||||
private CountDownLatch start, stop;
|
||||
|
||||
public MyThread(DefineClass dc, CountDownLatch start, CountDownLatch stop) {
|
||||
this.dc = dc;
|
||||
this.start = start;
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
dc.getID(start, stop);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParallelLoadingThread extends Thread {
|
||||
private MyParallelClassLoader pcl;
|
||||
private CountDownLatch stop;
|
||||
private byte[] buf;
|
||||
|
||||
public ParallelLoadingThread(MyParallelClassLoader pcl, byte[] buf, CountDownLatch stop) {
|
||||
this.pcl = pcl;
|
||||
this.stop = stop;
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
stop.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<DefineClass> dc = (Class<DefineClass>) pcl.myDefineClass(DefineClass.class.getName(), buf, 0, buf.length);
|
||||
}
|
||||
catch (LinkageError jle) {
|
||||
// Expected with a parallel capable class loader and
|
||||
// -XX:+UnsyncloadClass or -XX:+AllowParallelDefineClass
|
||||
pcl.incrementLinkageErrors();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static private class MyClassLoader extends ClassLoader {
|
||||
public Class<?> myDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
|
||||
return defineClass(name, b, off, len, null);
|
||||
}
|
||||
}
|
||||
|
||||
static private class MyParallelClassLoader extends ClassLoader {
|
||||
static {
|
||||
System.out.println("parallelCapable : " + registerAsParallelCapable());
|
||||
}
|
||||
public Class<?> myDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
|
||||
return defineClass(name, b, off, len, null);
|
||||
}
|
||||
public synchronized void incrementLinkageErrors() {
|
||||
linkageErrors++;
|
||||
}
|
||||
public int getLinkageErrors() {
|
||||
return linkageErrors;
|
||||
}
|
||||
private volatile int linkageErrors;
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation inst) {
|
||||
System.out.println("Loading Java Agent.");
|
||||
instrumentation = inst;
|
||||
}
|
||||
|
||||
|
||||
private static void loadInstrumentationAgent(String myName, byte[] buf) throws Exception {
|
||||
// Create agent jar file on the fly
|
||||
Manifest m = new Manifest();
|
||||
m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
m.getMainAttributes().put(new Attributes.Name("Agent-Class"), myName);
|
||||
m.getMainAttributes().put(new Attributes.Name("Can-Redefine-Classes"), "true");
|
||||
File jarFile = File.createTempFile("agent", ".jar");
|
||||
jarFile.deleteOnExit();
|
||||
JarOutputStream jar = new JarOutputStream(new FileOutputStream(jarFile), m);
|
||||
jar.putNextEntry(new JarEntry(myName.replace('.', '/') + ".class"));
|
||||
jar.write(buf);
|
||||
jar.close();
|
||||
String pid = Long.toString(ProcessTools.getProcessId());
|
||||
System.out.println("Our pid is = " + pid);
|
||||
VirtualMachine vm = VirtualMachine.attach(pid);
|
||||
vm.loadAgent(jarFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
private static byte[] getBytecodes(String myName) throws Exception {
|
||||
InputStream is = DefineClass.class.getResourceAsStream(myName + ".class");
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
while ((len = is.read(buf)) != -1) baos.write(buf, 0, len);
|
||||
buf = baos.toByteArray();
|
||||
System.out.println("sizeof(" + myName + ".class) == " + buf.length);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static int getStringIndex(String needle, byte[] buf) {
|
||||
return getStringIndex(needle, buf, 0);
|
||||
}
|
||||
|
||||
private static int getStringIndex(String needle, byte[] buf, int offset) {
|
||||
outer:
|
||||
for (int i = offset; i < buf.length - offset - needle.length(); i++) {
|
||||
for (int j = 0; j < needle.length(); j++) {
|
||||
if (buf[i + j] != (byte)needle.charAt(j)) continue outer;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void replaceString(byte[] buf, String name, int index) {
|
||||
for (int i = index; i < index + name.length(); i++) {
|
||||
buf[i] = (byte)name.charAt(i - index);
|
||||
}
|
||||
}
|
||||
|
||||
private static MBeanServer mbserver = ManagementFactory.getPlatformMBeanServer();
|
||||
|
||||
private static int getClassStats(String pattern) {
|
||||
try {
|
||||
ObjectName diagCmd = new ObjectName("com.sun.management:type=DiagnosticCommand");
|
||||
|
||||
String result = (String)mbserver.invoke(diagCmd , "gcClassStats" , new Object[] { null }, new String[] {String[].class.getName()});
|
||||
int count = 0;
|
||||
try (Scanner s = new Scanner(result)) {
|
||||
if (s.hasNextLine()) {
|
||||
System.out.println(s.nextLine());
|
||||
}
|
||||
while (s.hasNextLine()) {
|
||||
String l = s.nextLine();
|
||||
if (l.endsWith(pattern)) {
|
||||
count++;
|
||||
System.out.println(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Test failed because we can't read the class statistics!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printClassStats(int expectedCount, boolean reportError) {
|
||||
int count = getClassStats("DefineClass");
|
||||
String res = "Should have " + expectedCount +
|
||||
" DefineClass instances and we have: " + count;
|
||||
System.out.println(res);
|
||||
if (reportError && count != expectedCount) {
|
||||
throw new RuntimeException(res);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int ITERATIONS = 10;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String myName = DefineClass.class.getName();
|
||||
byte[] buf = getBytecodes(myName.substring(myName.lastIndexOf(".") + 1));
|
||||
int iterations = (args.length > 1 ? Integer.parseInt(args[1]) : ITERATIONS);
|
||||
|
||||
if (args.length == 0 || "defineClass".equals(args[0])) {
|
||||
MyClassLoader cl = new MyClassLoader();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<DefineClass> dc = (Class<DefineClass>) cl.myDefineClass(myName, buf, 0, buf.length);
|
||||
System.out.println(dc);
|
||||
}
|
||||
catch (LinkageError jle) {
|
||||
// Can only define once!
|
||||
if (i == 0) throw new Exception("Should succeed the first time.");
|
||||
}
|
||||
}
|
||||
// We expect to have two instances of DefineClass here: the initial version in which we are
|
||||
// executing and another version which was loaded into our own classloader 'MyClassLoader'.
|
||||
// All the subsequent attempts to reload DefineClass into our 'MyClassLoader' should have failed.
|
||||
printClassStats(2, false);
|
||||
System.gc();
|
||||
System.out.println("System.gc()");
|
||||
// At least after System.gc() the failed loading attempts should leave no instances around!
|
||||
printClassStats(2, true);
|
||||
}
|
||||
else if ("defineSystemClass".equals(args[0])) {
|
||||
MyClassLoader cl = new MyClassLoader();
|
||||
int index = getStringIndex("test/DefineClass", buf);
|
||||
replaceString(buf, "java/DefineClass", index);
|
||||
while ((index = getStringIndex("Ltest/DefineClass;", buf, index + 1)) != 0) {
|
||||
replaceString(buf, "Ljava/DefineClass;", index);
|
||||
}
|
||||
index = getStringIndex("test.DefineClass", buf);
|
||||
replaceString(buf, "java.DefineClass", index);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<DefineClass> dc = (Class<DefineClass>) cl.myDefineClass(null, buf, 0, buf.length);
|
||||
throw new RuntimeException("Defining a class in the 'java' package should fail!");
|
||||
}
|
||||
catch (java.lang.SecurityException jlse) {
|
||||
// Expected, because we're not allowed to define a class in the 'java' package
|
||||
}
|
||||
}
|
||||
// We expect to stay with one (the initial) instances of DefineClass.
|
||||
// All the subsequent attempts to reload DefineClass into the 'java' package should have failed.
|
||||
printClassStats(1, false);
|
||||
System.gc();
|
||||
System.out.println("System.gc()");
|
||||
// At least after System.gc() the failed loading attempts should leave no instances around!
|
||||
printClassStats(1, true);
|
||||
}
|
||||
else if ("defineClassParallel".equals(args[0])) {
|
||||
MyParallelClassLoader pcl = new MyParallelClassLoader();
|
||||
CountDownLatch stop = new CountDownLatch(1);
|
||||
|
||||
Thread[] threads = new Thread[iterations];
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
(threads[i] = new ParallelLoadingThread(pcl, buf, stop)).start();
|
||||
}
|
||||
stop.countDown(); // start parallel class loading..
|
||||
// ..and wait until all threads loaded the class
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
System.out.print("Counted " + pcl.getLinkageErrors() + " LinkageErrors ");
|
||||
System.out.println(pcl.getLinkageErrors() == 0 ?
|
||||
"" : "(use -XX:+UnsyncloadClass and/or -XX:+AllowParallelDefineClass to avoid this)");
|
||||
System.gc();
|
||||
System.out.println("System.gc()");
|
||||
// After System.gc() we expect to remain with two instances: one is the initial version which is
|
||||
// kept alive by this main method and another one in the parallel class loader.
|
||||
printClassStats(2, true);
|
||||
}
|
||||
else if ("redefineClass".equals(args[0])) {
|
||||
loadInstrumentationAgent(myName, buf);
|
||||
int index = getStringIndex("AAAAAAAA", buf);
|
||||
CountDownLatch stop = new CountDownLatch(1);
|
||||
|
||||
Thread[] threads = new Thread[iterations];
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
buf[index] = (byte) ('A' + i + 1); // Change string constant in getID() which is legal in redefinition
|
||||
instrumentation.redefineClasses(new ClassDefinition(DefineClass.class, buf));
|
||||
DefineClass dc = DefineClass.class.newInstance();
|
||||
CountDownLatch start = new CountDownLatch(1);
|
||||
(threads[i] = new MyThread(dc, start, stop)).start();
|
||||
start.await(); // Wait until the new thread entered the getID() method
|
||||
}
|
||||
// We expect to have one instance for each redefinition because they are all kept alive by an activation
|
||||
// plus the initial version which is kept active by this main method.
|
||||
printClassStats(iterations + 1, false);
|
||||
stop.countDown(); // Let all threads leave the DefineClass.getID() activation..
|
||||
// ..and wait until really all of them returned from DefineClass.getID()
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
System.gc();
|
||||
System.out.println("System.gc()");
|
||||
// After System.gc() we expect to remain with two instances: one is the initial version which is
|
||||
// kept alive by this main method and another one which is the latest redefined version.
|
||||
printClassStats(2, true);
|
||||
}
|
||||
else if ("redefineClassWithError".equals(args[0])) {
|
||||
loadInstrumentationAgent(myName, buf);
|
||||
int index = getStringIndex("getID", buf);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
buf[index] = (byte) 'X'; // Change getID() to XetID() which is illegal in redefinition
|
||||
try {
|
||||
instrumentation.redefineClasses(new ClassDefinition(DefineClass.class, buf));
|
||||
throw new RuntimeException("Class redefinition isn't allowed to change method names!");
|
||||
}
|
||||
catch (UnsupportedOperationException uoe) {
|
||||
// Expected because redefinition can't change the name of methods
|
||||
}
|
||||
}
|
||||
// We expect just a single DefineClass instance because failed redefinitions should
|
||||
// leave no garbage around.
|
||||
printClassStats(1, false);
|
||||
System.gc();
|
||||
System.out.println("System.gc()");
|
||||
// At least after a System.gc() we should definitely stay with a single instance!
|
||||
printClassStats(1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, 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
|
||||
@ -51,7 +51,6 @@ import jdk.test.lib.Asserts;
|
||||
* jdk.hotspot.agent/sun.jvm.hotspot.oops
|
||||
* jdk.hotspot.agent/sun.jvm.hotspot.debugger
|
||||
* jdk.hotspot.agent/sun.jvm.hotspot.ui.classbrowser
|
||||
* @ignore 8169232
|
||||
* @run main/othervm TestCpoolForInvokeDynamic
|
||||
*/
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user