This commit is contained in:
Jesper Wilhelmsson 2017-02-14 01:32:38 +01:00
commit be20d6384d
22 changed files with 765 additions and 124 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);
};

View File

@ -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:");

View File

@ -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>

View File

@ -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());

View File

@ -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) \
\

View File

@ -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);
}
}
}

View 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;
}
}
}

View File

@ -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();

View 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);
}
}
}

View File

@ -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
*/