# HG changeset patch # User jcbeyler # Date 1520552855 28800 # Thu Mar 08 15:47:35 2018 -0800 # Node ID f010598bf37a06ac07aa965d6de75cdf11405b1f # Parent 7fa5375fa6fdce5eafe87f0771a6535b9004411c [mq]: event-only diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -72,6 +72,7 @@ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetNamedModule \ + $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/IsModifiableModule \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/AddModuleReads \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/AddModuleExportsAndOpens \ diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -4268,7 +4268,7 @@ } else { lea(end, Address(obj, var_size_in_bytes)); } - ldr(rscratch1, Address(rthread, JavaThread::tlab_end_offset())); + ldr(rscratch1, Address(rthread, JavaThread::tlab_current_end_offset())); cmp(end, rscratch1); br(Assembler::HI, slow_case); @@ -4414,7 +4414,7 @@ should_not_reach_here(); bind(next); - ldr(rscratch2, Address(rthread, in_bytes(JavaThread::tlab_end_offset()))); + ldr(rscratch2, Address(rthread, in_bytes(JavaThread::tlab_current_end_offset()))); ldr(rscratch1, Address(rthread, in_bytes(JavaThread::tlab_top_offset()))); cmp(rscratch2, rscratch1); br(Assembler::HS, ok); diff --git a/src/hotspot/cpu/arm/macroAssembler_arm.cpp b/src/hotspot/cpu/arm/macroAssembler_arm.cpp --- a/src/hotspot/cpu/arm/macroAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/macroAssembler_arm.cpp @@ -1311,7 +1311,7 @@ assert_different_registers(obj, obj_end, tlab_end); ldr(obj, Address(Rthread, JavaThread::tlab_top_offset())); - ldr(tlab_end, Address(Rthread, JavaThread::tlab_end_offset())); + ldr(tlab_end, Address(Rthread, JavaThread::tlab_current_end_offset())); add_rc(obj_end, obj, size_expression); cmp(obj_end, tlab_end); b(slow_case, hi); diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -4397,7 +4397,7 @@ assert_different_registers(Robj, Rsize, Rklass, Rtlab_top, Rtlab_end); __ ldr(Robj, Address(Rthread, JavaThread::tlab_top_offset())); - __ ldr(Rtlab_end, Address(Rthread, in_bytes(JavaThread::tlab_end_offset()))); + __ ldr(Rtlab_end, Address(Rthread, in_bytes(JavaThread::tlab_current_end_offset()))); __ add(Rtlab_top, Robj, Rsize); __ cmp(Rtlab_top, Rtlab_end); __ b(slow_case, hi); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -2314,7 +2314,7 @@ //verify_tlab(); not implemented ld(obj, in_bytes(JavaThread::tlab_top_offset()), R16_thread); - ld(R0, in_bytes(JavaThread::tlab_end_offset()), R16_thread); + ld(R0, in_bytes(JavaThread::tlab_current_end_offset()), R16_thread); if (var_size_in_bytes == noreg) { addi(new_top, obj, con_size_in_bytes); } else { diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3820,7 +3820,7 @@ // Check if we can allocate in the TLAB. __ ld(RoldTopValue, in_bytes(JavaThread::tlab_top_offset()), R16_thread); - __ ld(RendValue, in_bytes(JavaThread::tlab_end_offset()), R16_thread); + __ ld(RendValue, in_bytes(JavaThread::tlab_current_end_offset()), R16_thread); __ add(RnewTopValue, Rinstance_size, RoldTopValue); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -2785,7 +2785,7 @@ } else { z_lay(end, Address(obj, var_size_in_bytes)); } - z_cg(end, Address(thread, JavaThread::tlab_end_offset())); + z_cg(end, Address(thread, JavaThread::tlab_current_end_offset())); branch_optimized(bcondHigh, slow_case); // Update the tlab top pointer. diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -3881,7 +3881,7 @@ Register RnewTopValue = tmp; __ z_lg(RoldTopValue, Address(Z_thread, JavaThread::tlab_top_offset())); __ load_address(RnewTopValue, Address(RoldTopValue, Rsize)); - __ z_cg(RnewTopValue, Address(Z_thread, JavaThread::tlab_end_offset())); + __ z_cg(RnewTopValue, Address(Z_thread, JavaThread::tlab_current_end_offset())); __ z_brh(slow_case); __ z_stg(RnewTopValue, Address(Z_thread, JavaThread::tlab_top_offset())); diff --git a/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp b/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp --- a/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp +++ b/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp @@ -3087,7 +3087,7 @@ bind(next); ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), t1); - ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), t2); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_current_end_offset()), t2); or3(t3, t2, t3); cmp_and_br_short(t1, t2, Assembler::lessEqual, Assembler::pn, next2); STOP("assert(top <= end)"); @@ -3209,7 +3209,7 @@ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), obj); // calculate amount of free space - ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), free); + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_current_end_offset()), free); sub(free, obj, free); Label done; diff --git a/src/hotspot/cpu/sparc/templateTable_sparc.cpp b/src/hotspot/cpu/sparc/templateTable_sparc.cpp --- a/src/hotspot/cpu/sparc/templateTable_sparc.cpp +++ b/src/hotspot/cpu/sparc/templateTable_sparc.cpp @@ -3290,7 +3290,7 @@ // check if we can allocate in the TLAB __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_top_offset()), RoldTopValue); // sets up RalocatedObject - __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_end_offset()), RendValue); + __ ld_ptr(G2_thread, in_bytes(JavaThread::tlab_current_end_offset()), RendValue); __ add(RoldTopValue, Roffset, RnewTopValue); // if there is enough space, we do not CAS and do not clear diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5594,7 +5594,7 @@ } else { lea(end, Address(obj, var_size_in_bytes, Address::times_1)); } - cmpptr(end, Address(thread, JavaThread::tlab_end_offset())); + cmpptr(end, Address(thread, JavaThread::tlab_current_end_offset())); jcc(Assembler::above, slow_case); // update the tlab top pointer @@ -6162,7 +6162,7 @@ should_not_reach_here(); bind(next); - movptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_end_offset()))); + movptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_current_end_offset()))); cmpptr(t1, Address(thread_reg, in_bytes(JavaThread::tlab_top_offset()))); jcc(Assembler::aboveEqual, ok); STOP("assert(top <= end)"); diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -4070,7 +4070,7 @@ if (UseTLAB) { __ movptr(rax, Address(thread, in_bytes(JavaThread::tlab_top_offset()))); __ lea(rbx, Address(rax, rdx, Address::times_1)); - __ cmpptr(rbx, Address(thread, in_bytes(JavaThread::tlab_end_offset()))); + __ cmpptr(rbx, Address(thread, in_bytes(JavaThread::tlab_current_end_offset()))); __ jcc(Assembler::above, slow_case); __ movptr(Address(thread, in_bytes(JavaThread::tlab_top_offset())), rbx); if (ZeroTLAB) { diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -356,6 +356,17 @@ #endif HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, Thread* thread, size_t size) { + HeapWord* obj = NULL; + + if (ThreadHeapSampler::enabled()) { + // Try to allocate the sampled object from TLAB, it is possible a sample + // point was put and the TLAB still has space. + obj = thread->tlab().allocate_sampled_object(size); + + if (obj != NULL) { + return obj; + } + } // Retain tlab and allocate object in shared space if // the amount free in the tlab is too large to discard. @@ -375,7 +386,7 @@ } // Allocate a new TLAB... - HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size); + obj = Universe::heap()->allocate_new_tlab(new_tlab_size); if (obj == NULL) { return NULL; } @@ -395,6 +406,14 @@ Copy::fill_to_words(obj + hdr_size, new_tlab_size - hdr_size, badHeapWordVal); #endif // ASSERT } + + // Send the thread information about this allocation in case a sample is + // requested. + if (ThreadHeapSampler::enabled()) { + size_t tlab_bytes_since_last_sample = thread->tlab().bytes_since_last_sample_point(); + thread->heap_sampler().check_for_sampling(obj, size, tlab_bytes_since_last_sample); + } + thread->tlab().fill(obj, obj + size, new_tlab_size); return obj; } diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp --- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp @@ -154,10 +154,14 @@ check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); - THREAD->incr_allocated_bytes(size * HeapWordSize); + int size_in_bytes = size * HeapWordSize; + THREAD->incr_allocated_bytes(size_in_bytes); - AllocTracer::send_allocation_outside_tlab(klass, result, size * HeapWordSize, THREAD); + AllocTracer::send_allocation_outside_tlab(klass, result, size_in_bytes, THREAD); + if (ThreadHeapSampler::enabled()) { + THREAD->heap_sampler().check_for_sampling(result, size_in_bytes); + } return result; } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -46,6 +46,16 @@ make_parsable(true); // also retire the TLAB } +size_t ThreadLocalAllocBuffer::remaining() { + if (current_end() == NULL) { + return 0; + } + + // TODO: To be deprecated when FastTLABRefill is deprecated. + update_end_pointers(); + return pointer_delta(reserved_end(), top()); +} + void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { global_stats()->initialize(); @@ -108,24 +118,29 @@ // Waste accounting should be done in caller as appropriate; see, // for example, clear_before_allocation(). void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) { - if (end() != NULL) { + if (current_end() != NULL) { invariants(); if (retire) { myThread()->incr_allocated_bytes(used_bytes()); } - CollectedHeap::fill_with_object(top(), hard_end(), retire && zap); + // TODO: To be deprecated when FastTLABRefill is deprecated. + update_end_pointers(); + CollectedHeap::fill_with_object(top(), reserved_end(), retire && zap); if (retire || ZeroTLAB) { // "Reset" the TLAB set_start(NULL); set_top(NULL); set_pf_top(NULL); - set_end(NULL); + set_current_end(NULL); + set_allocation_end(NULL); + set_last_slow_path_end(NULL); } } assert(!(retire || ZeroTLAB) || - (start() == NULL && end() == NULL && top() == NULL), + (start() == NULL && current_end() == NULL && top() == NULL && + _allocation_end == NULL && _last_slow_path_end == NULL), "TLAB must be reset"); } @@ -171,8 +186,13 @@ _number_of_refills++; print_stats("fill"); assert(top <= start + new_size - alignment_reserve(), "size too small"); + initialize(start, top, start + new_size - alignment_reserve()); + if (ThreadHeapSampler::enabled()) { + set_sample_end(); + } + // Reset amount of internal fragmentation set_refill_waste_limit(initial_refill_waste_limit()); } @@ -183,7 +203,9 @@ set_start(start); set_top(top); set_pf_top(top); - set_end(end); + set_current_end(end); + set_allocation_end(end); + set_last_slow_path_end(end); invariants(); } @@ -306,12 +328,60 @@ guarantee(p == top(), "end of last object must match end of space"); } +void ThreadLocalAllocBuffer::set_sample_end() { + size_t heap_words_remaining = pointer_delta(_current_end, _top); + size_t bytes_until_sample = myThread()->heap_sampler().bytes_until_sample(); + size_t words_until_sample = bytes_until_sample / HeapWordSize;; + + if (heap_words_remaining > words_until_sample) { + HeapWord* new_end = _top + words_until_sample; + set_current_end(new_end); + set_last_slow_path_end(new_end); + _bytes_since_last_sample_point = bytes_until_sample; + } else { + _bytes_since_last_sample_point = heap_words_remaining * HeapWordSize;; + } +} + Thread* ThreadLocalAllocBuffer::myThread() { return (Thread*)(((char *)this) + in_bytes(start_offset()) - in_bytes(Thread::tlab_start_offset())); } +void ThreadLocalAllocBuffer::set_back_allocation_end() { + update_end_pointers(); + _current_end = _allocation_end; +} + +void ThreadLocalAllocBuffer::update_end_pointers() { + // Did a fast TLAB refill occur? (This will be deprecated when fast TLAB + // refill disappears). + if (_last_slow_path_end != _current_end) { + // Fix up the last slow path end to be now the end of this TLAB. + _last_slow_path_end = _current_end; + _allocation_end = _current_end; + } +} + +HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) { + Thread* thread = myThread(); + thread->tlab().set_back_allocation_end(); + HeapWord* result = thread->tlab().allocate(size); + + if (result) { + thread->heap_sampler().check_for_sampling(result, size * HeapWordSize, _bytes_since_last_sample_point); + thread->tlab().set_sample_end(); + } + + return result; +} + +HeapWord* ThreadLocalAllocBuffer::reserved_end() { + assert (_last_slow_path_end == _current_end, + "Have to call update_end_pointers before reserved_end."); + return _allocation_end + alignment_reserve(); +} GlobalTLABStats::GlobalTLABStats() : _allocating_threads_avg(TLABAllocationWeight) { diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -37,6 +37,14 @@ // It is thread-private at any time, but maybe multiplexed over // time across multiple threads. The park()/unpark() pair is // used to make it available for such multiplexing. +// +// Heap sampling is performed via the current_end/allocation_end +// fields. +// allocation_end contains the real end of the tlab allocation, +// whereas current_end can be set to an arbitrary spot in the tlab to +// trip the return and sample the allocation. +// last_slow_path_end is used to track if a fast tlab refill occured +// between slowpath calls. class ThreadLocalAllocBuffer: public CHeapObj { friend class VMStructs; friend class JVMCIVMStructs; @@ -44,10 +52,14 @@ HeapWord* _start; // address of TLAB HeapWord* _top; // address after last allocation HeapWord* _pf_top; // allocation prefetch watermark - HeapWord* _end; // allocation end (excluding alignment_reserve) + HeapWord* _current_end; // allocation end (can be the sampling end point or _allocation_end) + HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve) + HeapWord* _last_slow_path_end; // last address for slow_path_end (as opposed to _allocation_end) + size_t _desired_size; // desired size (including alignment_reserve) size_t _refill_waste_limit; // hold onto tlab if free() is larger than this size_t _allocated_before_last_gc; // total bytes allocated up until the last gc + size_t _bytes_since_last_sample_point; // bytes since last sample point. static size_t _max_size; // maximum size of any TLAB static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB @@ -65,7 +77,9 @@ void initialize_statistics(); void set_start(HeapWord* start) { _start = start; } - void set_end(HeapWord* end) { _end = end; } + void set_current_end(HeapWord* current_end) { _current_end = current_end; } + void set_allocation_end(HeapWord* ptr) { _allocation_end = ptr; } + void set_last_slow_path_end(HeapWord* ptr) { _last_slow_path_end = ptr; } void set_top(HeapWord* top) { _top = top; } void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; } void set_desired_size(size_t desired_size) { _desired_size = desired_size; } @@ -76,7 +90,9 @@ static int target_refills() { return _target_refills; } size_t initial_desired_size(); - size_t remaining() const { return end() == NULL ? 0 : pointer_delta(hard_end(), top()); } + size_t remaining(); + + void update_end_pointers(); // Make parsable and release it. void reset(); @@ -84,7 +100,7 @@ // Resize based on amount of allocation, etc. void resize(); - void invariants() const { assert(top() >= start() && top() <= end(), "invalid tlab"); } + void invariants() const { assert(top() >= start() && top() <= current_end(), "invalid tlab"); } void initialize(HeapWord* start, HeapWord* top, HeapWord* end); @@ -114,19 +130,21 @@ static void set_max_size(size_t max_size) { _max_size = max_size; } HeapWord* start() const { return _start; } - HeapWord* end() const { return _end; } - HeapWord* hard_end() const { return _end + alignment_reserve(); } + HeapWord* current_end() const { return _current_end; } HeapWord* top() const { return _top; } + HeapWord* reserved_end(); HeapWord* pf_top() const { return _pf_top; } size_t desired_size() const { return _desired_size; } size_t used() const { return pointer_delta(top(), start()); } size_t used_bytes() const { return pointer_delta(top(), start(), 1); } - size_t free() const { return pointer_delta(end(), top()); } + size_t free() const { return pointer_delta(current_end(), top()); } // Don't discard tlab if remaining space is larger than this. size_t refill_waste_limit() const { return _refill_waste_limit; } + size_t bytes_since_last_sample_point() const { return _bytes_since_last_sample_point; } // Allocate size HeapWords. The memory is NOT initialized to zero. inline HeapWord* allocate(size_t size); + HeapWord* allocate_sampled_object(size_t size); // Reserve space at the end of TLAB static size_t end_reserve() { @@ -162,11 +180,14 @@ void fill(HeapWord* start, HeapWord* top, size_t new_size); void initialize(); + void set_back_allocation_end(); + void set_sample_end(); + static size_t refill_waste_limit_increment() { return TLABWasteIncrement; } // Code generation support static ByteSize start_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _start); } - static ByteSize end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _end ); } + static ByteSize current_end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _current_end ); } static ByteSize top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _top ); } static ByteSize pf_top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _pf_top ); } static ByteSize size_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _desired_size ); } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -34,7 +34,7 @@ inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) { invariants(); HeapWord* obj = top(); - if (pointer_delta(end(), obj) >= size) { + if (pointer_delta(current_end(), obj) >= size) { // successful thread-local allocation #ifdef ASSERT // Skip mangling the space corresponding to the object header to diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -323,7 +323,7 @@ \ nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \ - nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \ + nonstatic_field(ThreadLocalAllocBuffer, _current_end, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1241,9 +1241,9 @@ if (UseTLAB) { // Private allocation: load from TLS Node* thread = transform_later(new ThreadLocalNode()); int tlab_top_offset = in_bytes(JavaThread::tlab_top_offset()); - int tlab_end_offset = in_bytes(JavaThread::tlab_end_offset()); + int tlab_current_end_offset = in_bytes(JavaThread::tlab_current_end_offset()); eden_top_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_top_offset); - eden_end_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_end_offset); + eden_end_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_current_end_offset); } else { // Shared allocation: load from globals CollectedHeap* ch = Universe::heap(); address top_adr = (address)ch->top_addr(); diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -200,6 +200,7 @@ SharedRuntime::_new_instance_ctr++; // new instance requires GC #endif assert(check_compiled_frame(thread), "incorrect caller"); + JvmtiSampledObjectAllocEventCollector collector; // These checks are cheap to make and support reflective allocation. int lh = klass->layout_helper(); @@ -238,6 +239,7 @@ SharedRuntime::_new_array_ctr++; // new array requires GC #endif assert(check_compiled_frame(thread), "incorrect caller"); + JvmtiSampledObjectAllocEventCollector collector; // Scavenge and allocate an instance. oop result; @@ -319,6 +321,7 @@ // multianewarray for 2 dimensions JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread *thread)) + JvmtiSampledObjectAllocEventCollector collector; #ifndef PRODUCT SharedRuntime::_multi2_ctr++; // multianewarray for 1 dimension #endif @@ -335,6 +338,7 @@ // multianewarray for 3 dimensions JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread *thread)) + JvmtiSampledObjectAllocEventCollector collector; #ifndef PRODUCT SharedRuntime::_multi3_ctr++; // multianewarray for 1 dimension #endif @@ -352,6 +356,7 @@ // multianewarray for 4 dimensions JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread *thread)) + JvmtiSampledObjectAllocEventCollector collector; #ifndef PRODUCT SharedRuntime::_multi4_ctr++; // multianewarray for 1 dimension #endif @@ -370,6 +375,7 @@ // multianewarray for 5 dimensions JRT_ENTRY(void, OptoRuntime::multianewarray5_C(Klass* elem_type, int len1, int len2, int len3, int len4, int len5, JavaThread *thread)) + JvmtiSampledObjectAllocEventCollector collector; #ifndef PRODUCT SharedRuntime::_multi5_ctr++; // multianewarray for 1 dimension #endif diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -10353,6 +10353,19 @@ See . + + + Can sample the heap. + If this capability is enabled then the heap sampling methods can be called. + + + + + Can generate sampled object allocation events. + If this capability is enabled then an event for a sampled object allocation is sent + when the heap sampler is enabled as well. + + @@ -11531,6 +11544,47 @@ + + + Set TLAB Heap Sampling + + Set up the TLAB allocation system to sample memory by forcing slow path execution at a given + average rate via the . The + will be the average sampling rate used throughout + the execution. + + Setting to 0 disables the TLAB heap sampling. + Combined with a event, the Java agent can obtain object allocations with a given sample rate. + + new + + + + + + + + The monitoring rate in bytes used for sampling. The sampler will use a statistical approach to + provide in average sampling every allocated bytes. + + Note: a low monitoring rate, such as sampling every 1024 bytes, will probably incur a high overhead. + Due to the incurred overhead, the sampler should only be used when knowing it may impact performance. + On the other hand, sampling however every 1024kB has a far less chance of a high overhead since it will sample + 1024 times less than the 1024-byte sampling. + + + + + + is less than zero. + + + Calling this method without TLAB. + + + + + @@ -11739,6 +11793,9 @@ A method in the new class version has different modifiers than its counterpart in the old class version. + + The Thread Local Allocation Buffer system is disabled and the method requires it enabled. + @@ -13513,8 +13570,56 @@ + + + Sent when an object is sampled via the + Heap Sampling Monitoring system . + The event is sent once the allocation has been done and provides the object, stack trace + for the allocation, the thread allocating, the size of allocation, and class. + + new + + + + + + + JNIEnv + + + The JNI environment of the event (current) thread. + + + + + + Thread allocating the object. + + + + + + JNI local reference to the object that was allocated. + + + + + + JNI local reference to the class of the object + + + + + + Size of the object (in bytes). See . + + + + + + id="ObjectFree" const="JVMTI_EVENT_OBJECT_FREE" num="83"> An Object Free event is sent when the garbage collector frees an object. Events are only sent for tagged objects--see @@ -13534,7 +13639,7 @@ The freed object's tag - + diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -63,6 +63,7 @@ #include "runtime/reflectionUtils.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadHeapSampler.hpp" #include "runtime/threadSMR.hpp" #include "runtime/timerTrace.hpp" #include "runtime/vframe.hpp" @@ -3630,6 +3631,12 @@ return JVMTI_ERROR_NONE; } /* end GetAvailableProcessors */ +jvmtiError +JvmtiEnv::SetTlabHeapSampling(jint monitoring_rate) { + ThreadHeapSampler::set_tlab_heap_sampling(monitoring_rate); + return JVMTI_ERROR_NONE; +} /* end SetTlabHeapSampling */ + // // System Properties functions // diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -84,6 +84,7 @@ static const jlong OBJECT_FREE_BIT = (((jlong)1) << (JVMTI_EVENT_OBJECT_FREE - TOTAL_MIN_EVENT_TYPE_VAL)); static const jlong RESOURCE_EXHAUSTED_BIT = (((jlong)1) << (JVMTI_EVENT_RESOURCE_EXHAUSTED - TOTAL_MIN_EVENT_TYPE_VAL)); static const jlong VM_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_VM_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL)); +static const jlong SAMPLED_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_SAMPLED_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL)); // bits for extension events static const jlong CLASS_UNLOAD_BIT = (((jlong)1) << (EXT_EVENT_CLASS_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL)); @@ -615,6 +616,7 @@ JvmtiExport::set_should_post_compiled_method_load((any_env_thread_enabled & COMPILED_METHOD_LOAD_BIT) != 0); JvmtiExport::set_should_post_compiled_method_unload((any_env_thread_enabled & COMPILED_METHOD_UNLOAD_BIT) != 0); JvmtiExport::set_should_post_vm_object_alloc((any_env_thread_enabled & VM_OBJECT_ALLOC_BIT) != 0); + JvmtiExport::set_should_post_sampled_object_alloc((any_env_thread_enabled & SAMPLED_OBJECT_ALLOC_BIT) != 0); // need this if we want thread events or we need them to init data JvmtiExport::set_should_post_thread_life((any_env_thread_enabled & NEED_THREAD_LIFE_EVENTS) != 0); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1031,12 +1031,12 @@ return k; } -class JvmtiVMObjectAllocEventMark : public JvmtiClassEventMark { +class JvmtiObjectAllocEventMark : public JvmtiClassEventMark { private: jobject _jobj; jlong _size; public: - JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) { + JvmtiObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) { _jobj = (jobject)to_jobject(obj); _size = obj->size() * wordSize; }; @@ -1201,6 +1201,7 @@ bool JvmtiExport::_should_post_object_free = false; bool JvmtiExport::_should_post_resource_exhausted = false; bool JvmtiExport::_should_post_vm_object_alloc = false; +bool JvmtiExport::_should_post_sampled_object_alloc = false; bool JvmtiExport::_should_post_on_exceptions = false; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -2298,6 +2299,26 @@ } } +// Collect all the vm internally allocated objects which are visible to java world +void JvmtiExport::record_sampled_internal_object_allocation(oop obj) { + Thread* thread = Thread::current_or_null(); + if (thread != NULL && thread->is_Java_thread()) { + // Can not take safepoint here. + NoSafepointVerifier no_sfpt; + // Can not take safepoint here so can not use state_for to get + // jvmti thread state. + JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state(); + if (state != NULL ) { + // state is non NULL when SampledObjectAllocEventCollector is enabled. + JvmtiSampledObjectAllocEventCollector *collector; + collector = state->get_sampled_object_alloc_event_collector(); + if (collector != NULL && collector->is_enabled()) { + collector->record_allocation(obj); + } + } + } +} + void JvmtiExport::post_garbage_collection_finish() { Thread *thread = Thread::current(); // this event is posted from VM-Thread. EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, @@ -2499,7 +2520,6 @@ } } - void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered", JvmtiTrace::safe_get_thread_name(thread))); @@ -2515,7 +2535,7 @@ JvmtiTrace::safe_get_thread_name(thread), object==NULL? "NULL" : object->klass()->external_name())); - JvmtiVMObjectAllocEventMark jem(thread, h()); + JvmtiObjectAllocEventMark jem(thread, h()); JvmtiJavaThreadEventTransition jet(thread); jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc; if (callback != NULL) { @@ -2526,6 +2546,37 @@ } } +void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { + EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + ("[%s] Trg sampled object alloc triggered", + JvmtiTrace::safe_get_thread_name(thread))); + + if (object == NULL) { + return; + } + + HandleMark hm(thread); + Handle h(thread, object); + + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) { + EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + ("[%s] Evt sampled object alloc sent %s", + JvmtiTrace::safe_get_thread_name(thread), + object == NULL ? "NULL" : object->klass()->external_name())); + + JvmtiObjectAllocEventMark jem(thread, h()); + JvmtiJavaThreadEventTransition jet(thread); + jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; + if (callback != NULL) { + (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), + jem.jni_jobject(), jem.jni_class(), jem.size()); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////////////////////// void JvmtiExport::cleanup_thread(JavaThread* thread) { @@ -2551,7 +2602,7 @@ void JvmtiExport::oops_do(OopClosure* f) { JvmtiCurrentBreakpoints::oops_do(f); - JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(f); + JvmtiObjectAllocEventCollector::oops_do_for_all_threads(f); } void JvmtiExport::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { @@ -2684,6 +2735,9 @@ } else if (is_dynamic_code_event()) { _prev = state->get_dynamic_code_event_collector(); state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this); + } else if (is_sampled_object_alloc_event()) { + _prev = state->get_sampled_object_alloc_event_collector(); + state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)this); } } @@ -2700,14 +2754,19 @@ // this thread's jvmti state was created during the scope of // the event collector. } - } else { - if (is_dynamic_code_event()) { - if (state->get_dynamic_code_event_collector() == this) { - state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev); - } else { - // this thread's jvmti state was created during the scope of - // the event collector. - } + } else if (is_dynamic_code_event()) { + if (state->get_dynamic_code_event_collector() == this) { + state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. + } + } else if (is_sampled_object_alloc_event()) { + if (state->get_sampled_object_alloc_event_collector() == this) { + state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)_prev); + } else { + // this thread's jvmti state was created during the scope of + // the event collector. } } } @@ -2744,34 +2803,26 @@ _code_blobs->append(new JvmtiCodeBlobDesc(name, start, end)); } -// Setup current thread to record vm allocated objects. -JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() : _allocated(NULL) { - if (JvmtiExport::should_post_vm_object_alloc()) { - _enable = true; - setup_jvmti_thread_state(); - } else { - _enable = false; - } +JvmtiObjectAllocEventCollector::JvmtiObjectAllocEventCollector() : + _allocated(NULL), _enable(false), _post_callback(NULL), + _callback_for_all_oops(false) { } -// Post vm_object_alloc event for vm allocated objects visible to java -// world. -JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { +void JvmtiObjectAllocEventCollector::generate_call_for_allocated() { if (_allocated != NULL) { set_enabled(false); for (int i = 0; i < _allocated->length(); i++) { oop obj = _allocated->at(i); - if (ServiceUtil::visible_oop(obj)) { - JvmtiExport::post_vm_object_alloc(JavaThread::current(), obj); + if (_callback_for_all_oops || ServiceUtil::visible_oop(obj)) { + _post_callback(JavaThread::current(), obj); } } delete _allocated; } - unset_jvmti_thread_state(); } -void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) { - assert(is_enabled(), "VM object alloc event collector is not enabled"); +void JvmtiObjectAllocEventCollector::record_allocation(oop obj) { + assert(is_enabled(), "Object alloc event collector is not enabled"); if (_allocated == NULL) { _allocated = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(1, true); } @@ -2779,7 +2830,7 @@ } // GC support. -void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) { +void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) { if (_allocated != NULL) { for(int i=_allocated->length() - 1; i >= 0; i--) { if (_allocated->at(i) != NULL) { @@ -2789,7 +2840,7 @@ } } -void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { +void JvmtiObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { // no-op if jvmti not enabled if (!JvmtiEnv::environments_might_exist()) { return; @@ -2798,11 +2849,17 @@ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) { JvmtiThreadState *state = jthr->jvmti_thread_state(); if (state != NULL) { - JvmtiVMObjectAllocEventCollector *collector; + JvmtiObjectAllocEventCollector *collector; collector = state->get_vm_object_alloc_event_collector(); while (collector != NULL) { collector->oops_do(f); - collector = (JvmtiVMObjectAllocEventCollector *)collector->get_prev(); + collector = (JvmtiObjectAllocEventCollector*) collector->get_prev(); + } + + collector = state->get_sampled_object_alloc_event_collector(); + while (collector != NULL) { + collector->oops_do(f); + collector = (JvmtiObjectAllocEventCollector*) collector->get_prev(); } } } @@ -2837,6 +2894,35 @@ } }; +// Setup current thread to record vm allocated objects. +JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() { + if (JvmtiExport::should_post_vm_object_alloc()) { + _enable = true; + _post_callback = JvmtiExport::post_vm_object_alloc; + setup_jvmti_thread_state(); + } +} + +JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() { + generate_call_for_allocated(); + unset_jvmti_thread_state(); +} + +// Setup current thread to record sampled allocated objects. +JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() { + if (JvmtiExport::should_post_sampled_object_alloc()) { + _enable = true; + _callback_for_all_oops = true; + _post_callback = JvmtiExport::post_sampled_object_alloc; + setup_jvmti_thread_state(); + } +} + +JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() { + generate_call_for_allocated(); + unset_jvmti_thread_state(); +} + JvmtiGCMarker::JvmtiGCMarker() { // if there aren't any JVMTI environments then nothing to do if (!JvmtiEnv::environments_might_exist()) { diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -123,6 +123,7 @@ // breakpoint info JVMTI_SUPPORT_FLAG(should_clean_up_heap_objects) JVMTI_SUPPORT_FLAG(should_post_vm_object_alloc) + JVMTI_SUPPORT_FLAG(should_post_sampled_object_alloc) // If flag cannot be implemented, give an error if on=true static void report_unsupported(bool on); @@ -376,6 +377,17 @@ record_vm_internal_object_allocation(object); } } + + static void record_sampled_internal_object_allocation(oop object) NOT_JVMTI_RETURN; + // Post objects collected by sampled_object_alloc_event_collector. + static void post_sampled_object_alloc(JavaThread *thread, oop object) NOT_JVMTI_RETURN; + // Collects vm internal objects for later event posting. + inline static void sampled_object_alloc_event_collector(oop object) { + if (should_post_sampled_object_alloc()) { + record_sampled_internal_object_allocation(object); + } + } + inline static void post_array_size_exhausted() { if (should_post_resource_exhausted()) { post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, @@ -437,10 +449,13 @@ JvmtiEventCollector* _prev; // Save previous one to support nested event collector. public: + JvmtiEventCollector() : _prev(NULL) {} + void setup_jvmti_thread_state(); // Set this collector in current thread. void unset_jvmti_thread_state(); // Reset previous collector in current thread. virtual bool is_dynamic_code_event() { return false; } virtual bool is_vm_object_alloc_event(){ return false; } + virtual bool is_sampled_object_alloc_event(){ return false; } JvmtiEventCollector *get_prev() { return _prev; } }; @@ -475,21 +490,18 @@ }; -// Used to record vm internally allocated object oops and post -// vm object alloc event for objects visible to java world. -// Constructor enables JvmtiThreadState flag and all vm allocated -// objects are recorded in a growable array. When destructor is -// called the vm object alloc event is posted for each objects -// visible to java world. -// See jvm.cpp file for its usage. +// Used as a base class for object allocation collection and then posting +// the allocations to any event notification callbacks. // -class JvmtiVMObjectAllocEventCollector : public JvmtiEventCollector { - private: +class JvmtiObjectAllocEventCollector : public JvmtiEventCollector { + protected: GrowableArray* _allocated; // field to record vm internally allocated object oop. bool _enable; // This flag is enabled in constructor and disabled // in destructor before posting event. To avoid // collection of objects allocated while running java code inside - // agent post_vm_object_alloc() event handler. + // agent post_X_object_alloc() event handler. + void (*_post_callback)(JavaThread*, oop); // what callback to use when destroying the collector. + bool _callback_for_all_oops; //GC support void oops_do(OopClosure* f); @@ -502,15 +514,42 @@ static void oops_do_for_all_threads(OopClosure* f); public: - JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; - ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; - bool is_vm_object_alloc_event() { return true; } + JvmtiObjectAllocEventCollector() NOT_JVMTI_RETURN; + + void generate_call_for_allocated(); bool is_enabled() { return _enable; } void set_enabled(bool on) { _enable = on; } }; +// Used to record vm internally allocated object oops and post +// vm object alloc event for objects visible to java world. +// Constructor enables JvmtiThreadState flag and all vm allocated +// objects are recorded in a growable array. When destructor is +// called the vm object alloc event is posted for each object +// visible to java world. +// See jvm.cpp file for its usage. +// +class JvmtiVMObjectAllocEventCollector : public JvmtiObjectAllocEventCollector { + public: + JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; + ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN; + virtual bool is_vm_object_alloc_event() { return true; } +}; +// Used to record sampled allocated object oops and post +// sampled object alloc event. +// Constructor enables JvmtiThreadState flag and all sampled allocated +// objects are recorded in a growable array. When destructor is +// called the sampled object alloc event is posted for each sampled object. +// See jvm.cpp file for its usage. +// +class JvmtiSampledObjectAllocEventCollector : public JvmtiObjectAllocEventCollector { + public: + JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN; + ~JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN; + bool is_sampled_object_alloc_event() { return true; } +}; // Marker class to disable the posting of VMObjectAlloc events // within its scope. diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp --- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp @@ -141,6 +141,8 @@ jc.can_generate_field_modification_events = 1; jc.can_generate_field_access_events = 1; jc.can_generate_breakpoint_events = 1; + jc.can_sample_heap = 1; + jc.can_generate_sampled_object_alloc_events = 1; return jc; } @@ -410,6 +412,10 @@ log_trace(jvmti)("can_generate_frame_pop_events"); if (cap->can_generate_breakpoint_events) log_trace(jvmti)("can_generate_breakpoint_events"); + if (cap->can_sample_heap) + log_trace(jvmti)("can_sample_heap"); + if (cap->can_generate_sampled_object_alloc_events) + log_trace(jvmti)("can_generate_sampled_object_alloc_events"); if (cap->can_suspend) log_trace(jvmti)("can_suspend"); if (cap->can_redefine_any_class ) diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -60,6 +60,7 @@ _head_env_thread_state = NULL; _dynamic_code_event_collector = NULL; _vm_object_alloc_event_collector = NULL; + _sampled_object_alloc_event_collector = NULL; _the_class_for_redefinition_verification = NULL; _scratch_class_for_redefinition_verification = NULL; _cur_stack_depth = UNKNOWN_STACK_DEPTH; diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -113,6 +113,8 @@ JvmtiDynamicCodeEventCollector* _dynamic_code_event_collector; // holds the current vm object alloc event collector, NULL if no event collector in use JvmtiVMObjectAllocEventCollector* _vm_object_alloc_event_collector; + // holds the current sampled object alloc event collector, NULL if no event collector in use + JvmtiSampledObjectAllocEventCollector* _sampled_object_alloc_event_collector; // Should only be created by factory methods JvmtiThreadState(JavaThread *thread); @@ -314,12 +316,18 @@ JvmtiVMObjectAllocEventCollector* get_vm_object_alloc_event_collector() { return _vm_object_alloc_event_collector; } + JvmtiSampledObjectAllocEventCollector* get_sampled_object_alloc_event_collector() { + return _sampled_object_alloc_event_collector; + } void set_dynamic_code_event_collector(JvmtiDynamicCodeEventCollector* collector) { _dynamic_code_event_collector = collector; } void set_vm_object_alloc_event_collector(JvmtiVMObjectAllocEventCollector* collector) { _vm_object_alloc_event_collector = collector; } + void set_sampled_object_alloc_event_collector(JvmtiSampledObjectAllocEventCollector* collector) { + _sampled_object_alloc_event_collector = collector; + } // diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -236,6 +236,7 @@ set_active_handles(NULL); set_free_handle_block(NULL); set_last_handle_mark(NULL); + _heap_sampler.set_thread(this); // This initial value ==> never claimed. _oops_do_parity = 0; @@ -2048,6 +2049,7 @@ tlab().make_parsable(true); // retire TLAB, if any } + BarrierSet::barrier_set()->on_thread_detach(this); Threads::remove(this); @@ -4312,7 +4314,6 @@ assert_locked_or_safepoint(Threads_lock); BarrierSet::barrier_set()->on_thread_attach(p); - p->set_next(_thread_list); _thread_list = p; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -40,6 +40,7 @@ #include "runtime/park.hpp" #include "runtime/safepoint.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadHeapSampler.hpp" #include "runtime/threadLocalStorage.hpp" #include "runtime/unhandledOops.hpp" #include "trace/traceBackend.hpp" @@ -322,6 +323,7 @@ ThreadLocalAllocBuffer _tlab; // Thread-local eden jlong _allocated_bytes; // Cumulative number of bytes allocated on // the Java heap + ThreadHeapSampler _heap_sampler; // For use when sampling the memory. mutable TRACE_DATA _trace_data; // Thread-local data for tracing @@ -501,6 +503,8 @@ void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } inline jlong cooked_allocated_bytes(); + ThreadHeapSampler& heap_sampler() { return _heap_sampler; } + TRACE_DEFINE_THREAD_TRACE_DATA_OFFSET; TRACE_DATA* trace_data() const { return &_trace_data; } bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; } @@ -672,7 +676,7 @@ static ByteSize tlab_##name##_offset() { return byte_offset_of(Thread, _tlab) + ThreadLocalAllocBuffer::name##_offset(); } TLAB_FIELD_OFFSET(start) - TLAB_FIELD_OFFSET(end) + TLAB_FIELD_OFFSET(current_end) TLAB_FIELD_OFFSET(top) TLAB_FIELD_OFFSET(pf_top) TLAB_FIELD_OFFSET(size) // desired_size diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/runtime/threadHeapSampler.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018, Google 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. + * + */ + +#include "runtime/sharedRuntime.hpp" +#include "runtime/threadHeapSampler.hpp" + +// Cheap random number generator +uint64_t ThreadHeapSampler::_rnd; +int ThreadHeapSampler::_monitoring_rate; +int ThreadHeapSampler::_enabled; + +// Statics for the fast log +static const int FastLogNumBits = 10; +static const int FastLogMask = (1 << FastLogNumBits) - 1; +static double _log_table[1<0, "bad value passed to assert"); + uint64_t x = 0; + memcpy(&x, &d, sizeof(uint64_t)); + const uint32_t x_high = x >> 32; + const uint32_t y = x_high >> (20 - FastLogNumBits) & FastLogMask; + const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023; + return exponent + _log_table[y]; +} + +// Generates a geometric variable with the specified mean (512K by default). +// This is done by generating a random number between 0 and 1 and applying +// the inverse cumulative distribution function for an exponential. +// Specifically: Let m be the inverse of the sample rate, then +// the probability distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) { + _rnd = next_random(_rnd); + // Take the top 26 bits as the random number + // (This plus a 1<<58 sampling bound gives a max possible step of + // 5194297183973780480 bytes. In this case, + // for sample_parameter = 1<<19, max possible step is + // 9448372 bytes (24 bits). + const uint64_t PrngModPower = 48; // Number of bits in prng + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(_rnd >> (PrngModPower - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + // For faster performance (save ~1/20th exec time), replace + // min(0.0, FastLog2(q) - 26) by (Fastlog2(q) - 26.000705) + // The value 26.000705 is used rather than 26 to compensate + // for inaccuracies in FastLog2 which otherwise result in a + // negative answer. + double log_val = (fast_log2(q) - 26); + size_t rate = static_cast( + (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (_monitoring_rate)) + 1); + _bytes_until_sample = rate; + + // Try to correct sample size by removing extra space from last allocation. + if (overflowed_bytes > 0 && _bytes_until_sample > overflowed_bytes) { + _bytes_until_sample -= overflowed_bytes; + } +} + +void ThreadHeapSampler::check_for_sampling(HeapWord* ptr, size_t allocation_size, size_t bytes_since_allocation) { + oopDesc* oop = reinterpret_cast(ptr); + size_t total_allocated_bytes = bytes_since_allocation + allocation_size; + + // If not yet time for a sample, skip it. + if (total_allocated_bytes < _bytes_until_sample) { + _bytes_until_sample -= total_allocated_bytes; + return; + } + + JvmtiExport::sampled_object_alloc_event_collector(oop); + + size_t overflow_bytes = total_allocated_bytes - _bytes_until_sample; + pick_next_sample(overflow_bytes); +} + +void ThreadHeapSampler::set_tlab_heap_sampling(int monitoring_rate) { + if (monitoring_rate == 0) { + disable(); + _monitoring_rate = monitoring_rate; + } else { + _monitoring_rate = monitoring_rate; + enable(); + } +} diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/runtime/threadHeapSampler.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Google 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. + * + */ + +#ifndef RUNTIME_THREADHEAPSAMPLER_HPP +#define RUNTIME_THREADHEAPSAMPLER_HPP + +#include "memory/allocation.hpp" + +class ThreadHeapSampler { + private: + size_t _bytes_until_sample; + Thread* _thread; + // Cheap random number generator + static uint64_t _rnd; + + void pick_next_sample(size_t diff = 0); + static int _enabled; + static int _monitoring_rate; + + static void enable() { OrderAccess::release_store(&_enabled, 1); } + static void disable() { OrderAccess::release_store(&_enabled, 0); } + + public: + ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) { + _rnd = static_cast(reinterpret_cast(this)); + if (_rnd == 0) { + _rnd = 1; + } + } + + void set_thread(Thread* t) { _thread = t; } + + size_t bytes_until_sample() { return _bytes_until_sample; } + void set_bytes_until_sample(size_t bytes) { _bytes_until_sample = bytes; } + + void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0); + + static int enabled() { return OrderAccess::load_acquire(&_enabled); } + static void set_tlab_heap_sampling(int monitoring_rate); +}; + +#endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -529,7 +529,7 @@ \ nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \ - nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \ + nonstatic_field(ThreadLocalAllocBuffer, _current_end, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +class Frame { + Frame(String method, String signature, String fileName, int lineNumber) { + this.method = method; + this.signature = signature; + this.fileName = fileName; + this.lineNumber = lineNumber; + } + + public String method; + public String signature; + public String fileName; + public int lineNumber; +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.ArrayList; +import java.util.List; + +/** API for handling the underlying heap sampling monitoring system. */ +public class HeapMonitor { + private static int[][] arrays; + + static { + try { + System.loadLibrary("HeapMonitor"); + } catch (UnsatisfiedLinkError ule) { + System.err.println("Could not load HeapMonitor library"); + System.err.println("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + } + + /** Enable heap monitoring sampling given a rate. */ + public native static void enableSampling(int rate); + + /** Enable heap monitoring sampling with default value for rate. */ + public static void enableSampling() { + enableSampling(1 << 19); + } + + public native static void disableSampling(); + + /** + * Allocate memory but first create a stack trace of a particular depth. + * + * @return list of frames for the allocation. + */ + public static List allocate(int depth) { + List frames = new ArrayList(); + if (depth > 1) { + createStackDepth(depth - 1, frames); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 61)); + } else { + actuallyAllocate(); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 126)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 64)); + } + return frames; + } + + /** + * Allocate memory but first create a stack trace. + * + * @return list of frames for the allocation. + */ + public static List allocate() { + int sum = 0; + List frames = new ArrayList(); + allocate(frames); + frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 79)); + return frames; + } + + private static void createStackDepth(int depth, List frames) { + if (depth > 1) { + createStackDepth(depth - 1, frames); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 86)); + } else { + allocate(frames); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 89)); + } + } + + private static void allocate(List frames) { + int sum = 0; + for (int j = 0; j < 1000; j++) { + sum += actuallyAllocate(); + } + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 124)); + frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 97)); + } + + public static List repeatAllocate(int max) { + List frames = null; + for (int i = 0; i < max; i++) { + frames = allocate(); + } + frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 106)); + return frames; + } + + private static int actuallyAllocate() { + int sum = 0; + + // Let us assume that a 1-element array is 24 bytes of memory and we want + // 2MB allocated. + int iterations = (1 << 19) / 6; + + if (arrays == null) { + arrays = new int[iterations][]; + } + + for (int i = 0; i < iterations; i++) { + int tmp[] = new int[1]; + // Force it to be kept and, at the same time, wipe out any previous data. + arrays[i] = tmp; + sum += arrays[0][0]; + } + return sum; + } + + /** Remove the reference to the global array to free data at the next GC. */ + public static void freeStorage() { + arrays = null; + } + + public native static boolean obtainedEvents(Frame[] frames); + public native static boolean eventStorageIsEmpty(); + public native static void resetEventStorage(); +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor does not work without the required capability. + * @compile HeapMonitorNoCapabilityTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorNoCapabilityTest + */ + +public class HeapMonitorNoCapabilityTest { + static { + try { + System.loadLibrary("HeapMonitor"); + } catch (UnsatisfiedLinkError ule) { + System.err.println("Could not load HeapMonitor library"); + System.err.println("java.library.path: " + + System.getProperty("java.library.path")); + throw ule; + } + } + + private native static int allSamplingMethodsFail(); + + public static void main(String[] args) { + int result = allSamplingMethodsFail(); + + if (result == 0) { + throw new RuntimeException("Some methods could be called without a capability."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.ArrayList; +import java.util.List; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor API + * @build Frame HeapMonitor + * @compile HeapMonitorObjectTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorObjectTest + */ + +public class HeapMonitorObjectTest { + + public static HeapMonitorObjectTest rememberIt; + + private static List allocateObjects() { + int sum = 0; + List frames = new ArrayList(); + allocateObjects(frames); + frames.add(new Frame("allocateObjects", "()Ljava/util/List;", "HeapMonitorObjectTest.java", 44)); + return frames; + } + + private static void allocateObjects(List frames) { + for (int j = 0; j < 1000; j++) { + actuallyAllocateObjects(); + } + frames.add(new Frame("actuallyAllocateObjects", "()V", "HeapMonitorObjectTest.java", 59)); + frames.add(new Frame("allocateObjects", "(Ljava/util/List;)V", "HeapMonitorObjectTest.java", 51)); + } + + private static void actuallyAllocateObjects() { + for (int j = 0; j < 512 * 256; j++) { + rememberIt = new HeapMonitorObjectTest(); + } + } + + public static void main(String[] args) { + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty at test start..."); + } + + HeapMonitor.enableSampling(); + List frameList = allocateObjects(); + frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorObjectTest.java", 69)); + + Frame[] frames = frameList.toArray(new Frame[0]); + if (!HeapMonitor.obtainedEvents(frames)) { + throw new RuntimeException("Events not found with the right frames."); + } + + HeapMonitor.disableSampling(); + HeapMonitor.resetEventStorage(); + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty after reset."); + } + + HeapMonitor.allocate(); + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty after allocation while disabled."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.List; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor API + * @build Frame HeapMonitor + * @compile HeapMonitorTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorTest + */ + +public class HeapMonitorTest { + + private static native boolean framesAreNotLive(Frame[] frames); + + public static void main(String[] args) { + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty at test start..."); + } + + HeapMonitor.enableSampling(); + List frameList = HeapMonitor.allocate(); + frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 46)); + + Frame[] frames = frameList.toArray(new Frame[0]); + if (!HeapMonitor.obtainedEvents(frames)) { + throw new RuntimeException("Events not found with the right frames."); + } + + HeapMonitor.disableSampling(); + HeapMonitor.resetEventStorage(); + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty after reset."); + } + + HeapMonitor.allocate(); + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Storage is not empty after allocation while disabled."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2018, Google 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. + */ + +#include +#include +#include +#include +#include "jvmti.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef JNI_ENV_ARG + +#ifdef __cplusplus +#define JNI_ENV_ARG(x, y) y +#define JNI_ENV_PTR(x) x +#else +#define JNI_ENV_ARG(x,y) x, y +#define JNI_ENV_PTR(x) (*x) +#endif + +#endif + +#define TRUE 1 +#define FALSE 0 +#define PRINT_OUT 1 +#define MAX_TRACES 400 + +static jvmtiEnv *jvmti = NULL; + +typedef struct _LiveObjectTrace{ + jvmtiFrameInfo* frames; + size_t frame_count; +} LiveObjectTrace; + +typedef struct _EventStorage { + int live_object_size; + int live_object_count; + LiveObjectTrace** live_objects; +} EventStorage; + +typedef struct _ExpectedContentFrame { + const char *name; + const char *signature; + const char *file_name; + int line_number; +} ExpectedContentFrame; + +// Given a method and a location, this method gets the line number. +static +jint get_line_number(jvmtiEnv *jvmti, jmethodID method, + jlocation location) { + // Read the line number table. + jvmtiLineNumberEntry *table_ptr = 0; + jint line_number_table_entries; + int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, + &line_number_table_entries, + &table_ptr); + + if (JVMTI_ERROR_NONE != jvmti_error) { + return -1; + } + if (line_number_table_entries <= 0) { + return -1; + } + if (line_number_table_entries == 1) { + return table_ptr[0].line_number; + } + + // Go through all the line numbers... + jint last_location = table_ptr[0].start_location; + for (int l = 1; l < line_number_table_entries; l++) { + // ... and if you see one that is in the right place for your + // location, you've found the line number! + if ((location < table_ptr[l].start_location) && + (location >= last_location)) { + return table_ptr[l - 1].line_number; + } + last_location = table_ptr[l].start_location; + } + + if (location >= last_location) { + return table_ptr[line_number_table_entries - 1].line_number; + } else { + return -1; + } +} + +static jboolean check_sample_content(JNIEnv *env, + LiveObjectTrace* trace, + ExpectedContentFrame *expected, + size_t expected_count, + int print_out_comparisons) { + if (expected_count > trace->frame_count) { + return FALSE; + } + + jvmtiFrameInfo* frames = trace->frames; + + for (size_t i = 0; i < expected_count; i++) { + // Get basic information out of the trace. + int bci = frames[i].location; + jmethodID methodid = frames[i].method; + char *name = NULL, *signature = NULL, *file_name = NULL; + + if (bci < 0) { + return FALSE; + } + + // Transform into usable information. + int line_number = get_line_number(jvmti, methodid, bci); + (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); + + jclass declaring_class; + if (JVMTI_ERROR_NONE != + (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { + return FALSE; + } + + jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, + &file_name); + if (err != JVMTI_ERROR_NONE) { + return FALSE; + } + + // Compare now, none should be NULL. + if (name == NULL) { + return FALSE; + } + + if (file_name == NULL) { + return FALSE; + } + + if (signature == NULL) { + return FALSE; + } + + if (print_out_comparisons) { + fprintf(stderr, "\tComparing:\n"); + fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name); + fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature); + fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name); + fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number); + fprintf(stderr, "\t\tResult is %d\n", + (strcmp(name, expected[i].name) || + strcmp(signature, expected[i].signature) || + strcmp(file_name, expected[i].file_name) || + line_number != expected[i].line_number)); + } + + if (strcmp(name, expected[i].name) || + strcmp(signature, expected[i].signature) || + strcmp(file_name, expected[i].file_name) || + line_number != expected[i].line_number) { + return FALSE; + } + } + + return TRUE; +} + +// Static native API for various tests. +static void fill_native_frames(JNIEnv* env, jobjectArray frames, + ExpectedContentFrame* native_frames, size_t size) { + for (size_t i = 0; i < size; i++) { + jobject obj = (*env)->GetObjectArrayElement(env, frames, i); + jclass frame_class = (*env)->GetObjectClass(env, obj); + jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, + "lineNumber", "I"); + int line_number = (*env)->GetIntField(env, obj, line_number_field_id); + + jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", + "Ljava/lang/String;"); + jstring string_object = (jstring) (*env)->GetObjectField(env, obj, + string_id); + const char* method = (*env)->GetStringUTFChars(env, string_object, 0); + + string_id = (*env)->GetFieldID(env, frame_class, "fileName", + "Ljava/lang/String;"); + string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); + const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); + + string_id = (*env)->GetFieldID(env, frame_class, "signature", + "Ljava/lang/String;"); + string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); + const char* signature= (*env)->GetStringUTFChars(env, string_object, 0); + + native_frames[i].name = method; + native_frames[i].file_name = file_name; + native_frames[i].signature = signature; + native_frames[i].line_number = line_number; + } +} + +// Internal storage system implementation. + +static EventStorage global_event_storage; + +static int event_storage_get_count(EventStorage* storage) { + return storage->live_object_count; +} + +static jboolean event_storage_contains(JNIEnv* env, + EventStorage* storage, + ExpectedContentFrame* frames, + size_t size) { + fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); + for (int i = 0; i < storage->live_object_count; i++) { + LiveObjectTrace* trace = storage->live_objects[i]; + + if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { + return TRUE; + } + } + return FALSE; +} + +static void event_storage_augment_storage(EventStorage* storage) { + int new_max = (storage->live_object_size * 2) + 1; + LiveObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects)); + + int current_count = storage->live_object_count; + memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects)); + free(storage->live_objects); + storage->live_objects = new_objects; + + storage->live_object_size = new_max; +} + +static void event_storage_add(EventStorage* storage, + jthread thread, + jobject object, + jclass klass, + jlong size) { + jvmtiFrameInfo frames[64]; + jint count; + jvmtiError err; + err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count); + if (err == JVMTI_ERROR_NONE && count >= 1) { + if (storage->live_object_count >= storage->live_object_size) { + event_storage_augment_storage(storage); + } + assert(storage->live_object_count < storage->live_object_size); + + jvmtiFrameInfo* allocated_frames = malloc(count * sizeof(*allocated_frames)); + memcpy(allocated_frames, frames, count * sizeof(*allocated_frames)); + + LiveObjectTrace* live_object = malloc(sizeof(*live_object)); + live_object->frames = allocated_frames; + live_object->frame_count = count; + storage->live_objects[storage->live_object_count] = live_object; + storage->live_object_count++; + } +} + +static void event_storage_reset(EventStorage* storage) { + int max = storage->live_object_count; + for (int i = 0; i < max; i++) { + LiveObjectTrace* object = storage->live_objects[i]; + free(object); + } + free(storage->live_objects); + memset(storage, 0, sizeof(*storage)); +} + +// Start of the JVMTI agent code. + +static const char *EXC_CNAME = "java/lang/Exception"; + +static int check_error(jvmtiError err, const char *s) { + if (err != JVMTI_ERROR_NONE) { + printf(" ## %s error: %d\n", s, err); + return 1; + } + return 0; +} + +static int check_capability_error(jvmtiError err, const char *s) { + if (err != JVMTI_ERROR_NONE) { + if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { + return 0; + } + printf(" ## %s error: %d\n", s, err); + return 1; + } + return 1; +} + +static +jint throw_exc(JNIEnv *env, char *msg) { + jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); + + if (exc_class == NULL) { + printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); + return -1; + } + return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); +} + +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); + +JNIEXPORT +jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT +jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + return Agent_Initialize(jvm, options, reserved); +} + +JNIEXPORT +jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + return JNI_VERSION_1_8; +} + +JNIEXPORT +void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size) { + event_storage_add(&global_event_storage, thread, object, object_klass, size); +} + +static +jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { + jint res; + + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), + JVMTI_VERSION_9); + if (res != JNI_OK || jvmti == NULL) { + printf(" Error: wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.SampledObjectAlloc = &SampledObjectAlloc; + + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + // Get line numbers, sample heap, sample events, and filename for the test. + caps.can_get_line_numbers = 1; + caps.can_generate_sampled_object_alloc_events = 1; + caps.can_sample_heap = 1; + caps.can_get_source_file_name = 1; + if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){ + return JNI_ERR; + } + + if (check_error( + (*jvmti)->SetEventNotificationMode(jvmti, + JVMTI_ENABLE, + JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + NULL), + "Set event notifications")) { + return JNI_ERR; + } + + if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, + sizeof(jvmtiEventCallbacks)), + " Set Event Callbacks")) { + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_disableSampling(JNIEnv* env, jclass cls) { + (*jvmti)->SetTlabHeapSampling(jvmti, 0); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_enableSampling(JNIEnv* env, jclass cls, jint value) { + (*jvmti)->SetTlabHeapSampling(jvmti, value); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) { + return event_storage_get_count(&global_event_storage) == 0; +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls, jobjectArray frames) { + jsize size = (*env)->GetArrayLength(env, frames); + ExpectedContentFrame native_frames[size]; + fill_native_frames(env, frames, native_frames, size); + return event_storage_contains(env, &global_event_storage, native_frames, size); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) { + return event_storage_reset(&global_event_storage); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, + jclass cls) { + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_sample_heap= 1; + if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), + "Add capabilities\n")){ + return FALSE; + } + + if (check_capability_error((*jvmti)->SetTlabHeapSampling(jvmti, 1<<19), + "Set Tlab Heap Sampling")) { + return FALSE; + } + return TRUE; +} + +#ifdef __cplusplus +} +#endif # HG changeset patch # User jcbeyler # Date 1521047325 25200 # Wed Mar 14 10:08:45 2018 -0700 # Node ID 5115c44983b6112bd4f05994eb1b3491fa100916 # Parent f010598bf37a06ac07aa965d6de75cdf11405b1f [mq]: event3 diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -10356,14 +10356,9 @@ Can sample the heap. - If this capability is enabled then the heap sampling methods can be called. - - - - - Can generate sampled object allocation events. - If this capability is enabled then an event for a sampled object allocation is sent - when the heap sampler is enabled as well. + If this capability is enabled then the heap sampling method + can be called and the + events can be enabled. @@ -11554,7 +11549,7 @@ the execution. Setting to 0 disables the TLAB heap sampling. - Combined with a event, the Java agent can obtain object allocations with a given sample rate. + Combined with a event, the Java agent can obtain object allocations with a given sample rate. new @@ -11576,7 +11571,7 @@ - is less than zero. + is less than zero. Calling this method without TLAB. @@ -13552,13 +13547,13 @@ - JNI local reference to the object that was allocated + JNI local reference to the object that was allocated. - JNI local reference to the class of the object + JNI local reference to the class of the object. @@ -13575,12 +13570,20 @@ Sent when an object is sampled via the Heap Sampling Monitoring system . + Therefore, this event is sent to the user if there is at least one call to + with a strictly positive integer value. + The event is sent once the allocation has been done and provides the object, stack trace for the allocation, the thread allocating, the size of allocation, and class. +

+ Typical use cases of this system is to determine where most heap allocation are originating from. + Using this in conjunction of weak references and the function + , a user can track which objects were allocated from which + stacktrace and which are still live during the execution of the program.
new - + diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp --- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp @@ -142,7 +142,6 @@ jc.can_generate_field_access_events = 1; jc.can_generate_breakpoint_events = 1; jc.can_sample_heap = 1; - jc.can_generate_sampled_object_alloc_events = 1; return jc; } @@ -414,8 +413,6 @@ log_trace(jvmti)("can_generate_breakpoint_events"); if (cap->can_sample_heap) log_trace(jvmti)("can_sample_heap"); - if (cap->can_generate_sampled_object_alloc_events) - log_trace(jvmti)("can_generate_sampled_object_alloc_events"); if (cap->can_suspend) log_trace(jvmti)("can_suspend"); if (cap->can_redefine_any_class ) # HG changeset patch # User jcbeyler # Date 1521170411 25200 # Thu Mar 15 20:20:11 2018 -0700 # Node ID ab97b97defd1d5bea49b7612ac53b3bdcb8f8f70 # Parent 5115c44983b6112bd4f05994eb1b3491fa100916 [mq]: event4 diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -13575,7 +13575,7 @@ The event is sent once the allocation has been done and provides the object, stack trace for the allocation, the thread allocating, the size of allocation, and class. -

+

Typical use cases of this system is to determine where most heap allocation are originating from. Using this in conjunction of weak references and the function , a user can track which objects were allocated from which diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java @@ -40,15 +40,20 @@ } } - /** Enable heap monitoring sampling given a rate. */ - public native static void enableSampling(int rate); - /** Enable heap monitoring sampling with default value for rate. */ - public static void enableSampling() { - enableSampling(1 << 19); + public static void enableSamplingRate() { + setSamplingRate(1 << 19); } - public native static void disableSampling(); + public static void disableSamplingRate() { + setSamplingRate(0); + } + + /** Set a specific sampling rate, 0 turns off sampling. */ + public native static void setSamplingRate(int rate); + + public native static void enableSamplingEvents(); + public native static void disableSamplingEvents(); /** * Allocate memory but first create a stack trace of a particular depth. diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c @@ -91,7 +91,8 @@ // Go through all the line numbers... jint last_location = table_ptr[0].start_location; - for (int l = 1; l < line_number_table_entries; l++) { + int l; + for (l = 1; l < line_number_table_entries; l++) { // ... and if you see one that is in the right place for your // location, you've found the line number! if ((location < table_ptr[l].start_location) && @@ -119,7 +120,8 @@ jvmtiFrameInfo* frames = trace->frames; - for (size_t i = 0; i < expected_count; i++) { + size_t i; + for (i = 0; i < expected_count; i++) { // Get basic information out of the trace. int bci = frames[i].location; jmethodID methodid = frames[i].method; @@ -185,7 +187,8 @@ // Static native API for various tests. static void fill_native_frames(JNIEnv* env, jobjectArray frames, ExpectedContentFrame* native_frames, size_t size) { - for (size_t i = 0; i < size; i++) { + size_t i; + for (i = 0; i < size; i++) { jobject obj = (*env)->GetObjectArrayElement(env, frames, i); jclass frame_class = (*env)->GetObjectClass(env, obj); jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, @@ -228,7 +231,8 @@ ExpectedContentFrame* frames, size_t size) { fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); - for (int i = 0; i < storage->live_object_count; i++) { + int i; + for (i = 0; i < storage->live_object_count; i++) { LiveObjectTrace* trace = storage->live_objects[i]; if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { @@ -278,7 +282,8 @@ static void event_storage_reset(EventStorage* storage) { int max = storage->live_object_count; - for (int i = 0; i < max; i++) { + int i; + for (i = 0; i < max; i++) { LiveObjectTrace* object = storage->live_objects[i]; free(object); } @@ -347,6 +352,12 @@ event_storage_add(&global_event_storage, thread, object, object_klass, size); } +static int enable_notifications() { + return check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), + "Set event notifications"); +} + static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { jint res; @@ -366,19 +377,13 @@ memset(&caps, 0, sizeof(caps)); // Get line numbers, sample heap, sample events, and filename for the test. caps.can_get_line_numbers = 1; - caps.can_generate_sampled_object_alloc_events = 1; caps.can_sample_heap = 1; caps.can_get_source_file_name = 1; if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){ return JNI_ERR; } - if (check_error( - (*jvmti)->SetEventNotificationMode(jvmti, - JVMTI_ENABLE, - JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, - NULL), - "Set event notifications")) { + if (enable_notifications()) { return JNI_ERR; } @@ -391,12 +396,7 @@ } JNIEXPORT void JNICALL -Java_MyPackage_HeapMonitor_disableSampling(JNIEnv* env, jclass cls) { - (*jvmti)->SetTlabHeapSampling(jvmti, 0); -} - -JNIEXPORT void JNICALL -Java_MyPackage_HeapMonitor_enableSampling(JNIEnv* env, jclass cls, jint value) { +Java_MyPackage_HeapMonitor_setSamplingRate(JNIEnv* env, jclass cls, jint value) { (*jvmti)->SetTlabHeapSampling(jvmti, value); } @@ -405,6 +405,22 @@ return event_storage_get_count(&global_event_storage) == 0; } +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) { + enable_notifications(); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { + check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), + "Set event notifications"); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { +} + JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls, jobjectArray frames) { jsize size = (*env)->GetArrayLength(env, frames); # HG changeset patch # User jcbeyler # Date 1521493260 25200 # Mon Mar 19 14:01:00 2018 -0700 # Node ID e6d76066fba961eef25ce30b3311f97c04fbdfed # Parent ab97b97defd1d5bea49b7612ac53b3bdcb8f8f70 [mq]: event5 diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -175,6 +175,7 @@ // Allocation IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index)) + JvmtiSampledObjectAllocEventCollector collector; Klass* k = pool->klass_at(index, CHECK); InstanceKlass* klass = InstanceKlass::cast(k); @@ -204,12 +205,14 @@ IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* thread, BasicType type, jint size)) + JvmtiSampledObjectAllocEventCollector collector; oop obj = oopFactory::new_typeArray(type, size, CHECK); thread->set_vm_result(obj); IRT_END IRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* thread, ConstantPool* pool, int index, jint size)) + JvmtiSampledObjectAllocEventCollector collector; Klass* klass = pool->klass_at(index, CHECK); objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK); thread->set_vm_result(obj); @@ -217,6 +220,7 @@ IRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* thread, jint* first_size_address)) + JvmtiSampledObjectAllocEventCollector collector; // We may want to pass in more arguments - could make this slightly faster LastFrameAccessor last_frame(thread); ConstantPool* constants = last_frame.method()->constants(); diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -10357,7 +10357,7 @@ Can sample the heap. If this capability is enabled then the heap sampling method - can be called and the + can be called and the events can be enabled. @@ -11540,15 +11540,16 @@ - - Set TLAB Heap Sampling - - Set up the TLAB allocation system to sample memory by forcing slow path execution at a given - average rate via the . The - will be the average sampling rate used throughout + + Set Heap Sampling Rate + + Set up the allocation system to sample memory at a given + average rate via the parameter, defined + by sampling rate in bytes. The + will be the average sampling rate in bytes used throughout the execution. - Setting to 0 disables the TLAB heap sampling. + Setting to 0 disables the heap sampling. Combined with a event, the Java agent can obtain object allocations with a given sample rate. new @@ -11556,13 +11557,13 @@ - + - The monitoring rate in bytes used for sampling. The sampler will use a statistical approach to - provide in average sampling every allocated bytes. - - Note: a low monitoring rate, such as sampling every 1024 bytes, will probably incur a high overhead. + The sampling rate in bytes used for sampling. The sampler will use a statistical approach to + provide in average sampling every allocated bytes. + + Note: a low sampling rate, such as sampling every 1024 bytes, will probably incur a high overhead. Due to the incurred overhead, the sampler should only be used when knowing it may impact performance. On the other hand, sampling however every 1024kB has a far less chance of a high overhead since it will sample 1024 times less than the 1024-byte sampling. @@ -11571,10 +11572,7 @@ - is less than zero. - - - Calling this method without TLAB. + is less than zero. @@ -11788,9 +11786,6 @@ A method in the new class version has different modifiers than its counterpart in the old class version. - - The Thread Local Allocation Buffer system is disabled and the method requires it enabled. - @@ -13566,12 +13561,12 @@ + id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11"> Sent when an object is sampled via the Heap Sampling Monitoring system . Therefore, this event is sent to the user if there is at least one call to - with a strictly positive integer value. + with a strictly positive integer value. The event is sent once the allocation has been done and provides the object, stack trace for the allocation, the thread allocating, the size of allocation, and class. diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -3632,10 +3632,10 @@ } /* end GetAvailableProcessors */ jvmtiError -JvmtiEnv::SetTlabHeapSampling(jint monitoring_rate) { - ThreadHeapSampler::set_tlab_heap_sampling(monitoring_rate); +JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) { + ThreadHeapSampler::set_tlab_heap_sampling(sampling_rate); return JVMTI_ERROR_NONE; -} /* end SetTlabHeapSampling */ +} /* end SetHeapSamplingRate */ // // System Properties functions diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -127,6 +127,8 @@ Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; +Monitor* ThreadHeapSampler_lock = NULL; + #if INCLUDE_TRACE Mutex* JfrStacktrace_lock = NULL; Monitor* JfrMsg_lock = NULL; @@ -283,6 +285,9 @@ def(CompileThread_lock , PaddedMonitor, nonleaf+5, false, Monitor::_safepoint_check_always); def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes); def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always); + + def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_never); + if (WhiteBoxAPI) { def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never); } diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -125,6 +125,7 @@ extern Monitor* Service_lock; // a lock used for service thread operation extern Monitor* PeriodicTask_lock; // protects the periodic task structure extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition +extern Monitor* ThreadHeapSampler_lock; // protects the static data for initialization. #if INCLUDE_TRACE extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp --- a/src/hotspot/share/runtime/threadHeapSampler.cpp +++ b/src/hotspot/share/runtime/threadHeapSampler.cpp @@ -27,13 +27,14 @@ // Cheap random number generator uint64_t ThreadHeapSampler::_rnd; -int ThreadHeapSampler::_monitoring_rate; +int ThreadHeapSampler::_sampling_rate; int ThreadHeapSampler::_enabled; // Statics for the fast log static const int FastLogNumBits = 10; static const int FastLogMask = (1 << FastLogNumBits) - 1; -static double _log_table[1<> 32; const uint32_t y = x_high >> (20 - FastLogNumBits) & FastLogMask; const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023; - return exponent + _log_table[y]; + return exponent + log_table[y]; } // Generates a geometric variable with the specified mean (512K by default). @@ -86,7 +87,7 @@ // negative answer. double log_val = (fast_log2(q) - 26); size_t rate = static_cast( - (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (_monitoring_rate)) + 1); + (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (_sampling_rate)) + 1); _bytes_until_sample = rate; // Try to correct sample size by removing extra space from last allocation. @@ -111,12 +112,27 @@ pick_next_sample(overflow_bytes); } -void ThreadHeapSampler::set_tlab_heap_sampling(int monitoring_rate) { - if (monitoring_rate == 0) { +void ThreadHeapSampler::init_log_table() { + MutexLocker mu(ThreadHeapSampler_lock); + + if (log_table_initialized) { + return; + } + + for (int i = 0; i < (1 << FastLogNumBits); i++) { + log_table[i] = (log(1.0 + static_cast(i+0.5) / (1 << FastLogNumBits)) + / log(2.0)); + } + + log_table_initialized = true; +} + +void ThreadHeapSampler::set_tlab_heap_sampling(int sampling_rate) { + if (sampling_rate == 0) { disable(); - _monitoring_rate = monitoring_rate; + _sampling_rate = sampling_rate; } else { - _monitoring_rate = monitoring_rate; + _sampling_rate = sampling_rate; enable(); } } diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp --- a/src/hotspot/share/runtime/threadHeapSampler.hpp +++ b/src/hotspot/share/runtime/threadHeapSampler.hpp @@ -36,10 +36,11 @@ void pick_next_sample(size_t diff = 0); static int _enabled; - static int _monitoring_rate; + static int _sampling_rate; static void enable() { OrderAccess::release_store(&_enabled, 1); } static void disable() { OrderAccess::release_store(&_enabled, 0); } + void init_log_table(); public: ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) { @@ -47,6 +48,7 @@ if (_rnd == 0) { _rnd = 1; } + init_log_table(); } void set_thread(Thread* t) { _thread = t; } @@ -57,7 +59,7 @@ void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0); static int enabled() { return OrderAccess::load_acquire(&_enabled); } - static void set_tlab_heap_sampling(int monitoring_rate); + static void set_tlab_heap_sampling(int sampling_rate); }; #endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Google and/or its affiliates. All rights reserved. + * Copyright (c) 2018, Google 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 diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java @@ -51,6 +51,8 @@ /** Set a specific sampling rate, 0 turns off sampling. */ public native static void setSamplingRate(int rate); + /** Set a specific garbage history buffer. */ + public native static void setGarbageHistory(int amount); public native static void enableSamplingEvents(); public native static void disableSamplingEvents(); @@ -64,11 +66,11 @@ List frames = new ArrayList(); if (depth > 1) { createStackDepth(depth - 1, frames); - frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 61)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 68)); } else { actuallyAllocate(); - frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 126)); - frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 64)); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 131)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 71)); } return frames; } @@ -82,17 +84,17 @@ int sum = 0; List frames = new ArrayList(); allocate(frames); - frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 79)); + frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 86)); return frames; } private static void createStackDepth(int depth, List frames) { if (depth > 1) { createStackDepth(depth - 1, frames); - frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 86)); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 93)); } else { allocate(frames); - frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 89)); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 96)); } } @@ -101,8 +103,8 @@ for (int j = 0; j < 1000; j++) { sum += actuallyAllocate(); } - frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 124)); - frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 97)); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 131)); + frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 104)); } public static List repeatAllocate(int max) { @@ -110,7 +112,7 @@ for (int i = 0; i < max; i++) { frames = allocate(); } - frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 106)); + frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 113)); return frames; } @@ -134,12 +136,43 @@ return sum; } + public static int allocateSize(int totalSize) { + int sum = 0; + + // Let us assume that a 1-element array is 24 bytes. + int iterations = totalSize / 24; + + if (arrays == null) { + arrays = new int[iterations][]; + } + + System.out.println("Allocating for " + iterations); + for (int i = 0; i < iterations; i++) { + int tmp[] = new int[1]; + + // Force it to be kept and, at the same time, wipe out any previous data. + arrays[i] = tmp; + sum += arrays[0][0]; + } + + return sum; + } + /** Remove the reference to the global array to free data at the next GC. */ public static void freeStorage() { arrays = null; } public native static boolean obtainedEvents(Frame[] frames); + public native static boolean garbageContains(Frame[] frames); public native static boolean eventStorageIsEmpty(); public native static void resetEventStorage(); + public native static int getEventStorageElementCount(); + public native static void forceGarbageCollection(); + + public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) { + double actual = getEventStorageElementCount(); + double diffPercentage = Math.abs(actual - expected) / expected; + return diffPercentage < acceptedErrorPercentage; + } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.List; + +/** + * @test + * @summary Verifies if turning off the event notification stops events. + * @build Frame HeapMonitor + * @compile HeapMonitorEventOnOffTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorEventOnOffTest + */ +public class HeapMonitorEventOnOffTest { + private static void checkNoEventsAreBeingSent() { + HeapMonitor.resetEventStorage(); + HeapMonitor.repeatAllocate(10); + + // Check that the data is available while heap sampling is enabled. + boolean status = HeapMonitor.eventStorageIsEmpty(); + if (!status) { + throw new RuntimeException("Failed to find the traces before the wipe out."); + } + } + + private static void checkEventsAreBeingSent() { + List frameList = HeapMonitor.repeatAllocate(10); + + frameList.add(new Frame("checkEventsAreBeingSent", "()V", "HeapMonitorEventOnOffTest.java", 48)); + Frame[] frames = frameList.toArray(new Frame[0]); + + // Check that the data is available while heap sampling is enabled. + boolean status = HeapMonitor.obtainedEvents(frames); + if (!status) { + throw new RuntimeException("Failed to find the traces before the wipe out."); + } + } + + public static void main(String[] args) { + HeapMonitor.enableSamplingRate(); + checkEventsAreBeingSent(); + + // Disabling the notification system should stop events. + HeapMonitor.disableSamplingEvents(); + checkNoEventsAreBeingSent(); + + // Enabling the notification system should start events again. + HeapMonitor.enableSamplingEvents(); + checkEventsAreBeingSent(); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor Statistics using CMS GC + * @build Frame HeapMonitor + * @compile HeapMonitorGCCMSTest.java + * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest + */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor Statistics using ParallelGc + * @build Frame HeapMonitor + * @compile HeapMonitorGCTest.java + * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseParallelGC MyPackage.HeapMonitorGCTest + */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor Statistics using SerialGC + * @build Frame HeapMonitor + * @compile HeapMonitorGCTest.java + * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseSerialGC MyPackage.HeapMonitorGCTest + */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.List; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the default GC with the Heap Monitor event system. + * @compile HeapMonitorGCTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorGCTest + */ + +/** + * This test is checking that various GCs work as intended: events are sent, forcing GC works, etc. + */ +public class HeapMonitorGCTest { + public static void main(String[] args) { + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should be null to begin with."); + } + + HeapMonitor.enableSamplingRate(); + + List frameList = HeapMonitor.allocate(); + frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorGCTest.java", 47)); + Frame[] frames = frameList.toArray(new Frame[0]); + + if (!HeapMonitor.obtainedEvents(frames)) { + throw new RuntimeException("No expected events were found."); + } + + HeapMonitor.forceGarbageCollection(); + + if (!HeapMonitor.garbageContains(frames)) { + throw new RuntimeException("Forcing GC did not work, not a single object was collected."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor using the interpreter. + * @build Frame HeapMonitor + * @compile HeapMonitorTest.java + * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorTest + */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor using the interpreter. + * @build Frame HeapMonitor + * @compile HeapMonitorStatObjectCorrectnessTest.java + * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest + */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java deleted file mode 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorObjectTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package MyPackage; - -import java.util.ArrayList; -import java.util.List; - -/** - * @test - * @summary Verifies the JVMTI Heap Monitor API - * @build Frame HeapMonitor - * @compile HeapMonitorObjectTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorObjectTest - */ - -public class HeapMonitorObjectTest { - - public static HeapMonitorObjectTest rememberIt; - - private static List allocateObjects() { - int sum = 0; - List frames = new ArrayList(); - allocateObjects(frames); - frames.add(new Frame("allocateObjects", "()Ljava/util/List;", "HeapMonitorObjectTest.java", 44)); - return frames; - } - - private static void allocateObjects(List frames) { - for (int j = 0; j < 1000; j++) { - actuallyAllocateObjects(); - } - frames.add(new Frame("actuallyAllocateObjects", "()V", "HeapMonitorObjectTest.java", 59)); - frames.add(new Frame("allocateObjects", "(Ljava/util/List;)V", "HeapMonitorObjectTest.java", 51)); - } - - private static void actuallyAllocateObjects() { - for (int j = 0; j < 512 * 256; j++) { - rememberIt = new HeapMonitorObjectTest(); - } - } - - public static void main(String[] args) { - if (!HeapMonitor.eventStorageIsEmpty()) { - throw new RuntimeException("Storage is not empty at test start..."); - } - - HeapMonitor.enableSampling(); - List frameList = allocateObjects(); - frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorObjectTest.java", 69)); - - Frame[] frames = frameList.toArray(new Frame[0]); - if (!HeapMonitor.obtainedEvents(frames)) { - throw new RuntimeException("Events not found with the right frames."); - } - - HeapMonitor.disableSampling(); - HeapMonitor.resetEventStorage(); - if (!HeapMonitor.eventStorageIsEmpty()) { - throw new RuntimeException("Storage is not empty after reset."); - } - - HeapMonitor.allocate(); - if (!HeapMonitor.eventStorageIsEmpty()) { - throw new RuntimeException("Storage is not empty after allocation while disabled."); - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorSamplingOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorSamplingOnOffTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorSamplingOnOffTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +import java.util.List; + +/** + * @test + * @summary Verifies if turning off the event notification stops events. + * @build Frame HeapMonitor + * @compile HeapMonitorSamplingOnOffTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorSamplingOnOffTest + */ +public class HeapMonitorSamplingOnOffTest { + private static void checkNoEventsAreBeingSent() { + HeapMonitor.resetEventStorage(); + HeapMonitor.repeatAllocate(10); + + // Check that the data is available while heap sampling is enabled. + boolean status = HeapMonitor.eventStorageIsEmpty(); + if (!status) { + throw new RuntimeException("Failed to find the traces before the wipe out."); + } + } + + private static void checkEventsAreBeingSent() { + List frameList = HeapMonitor.repeatAllocate(10); + + frameList.add(new Frame("checkEventsAreBeingSent", "()V", "HeapMonitorSamplingOnOffTest.java", 48)); + Frame[] frames = frameList.toArray(new Frame[0]); + + // Check that the data is available while heap sampling is enabled. + boolean status = HeapMonitor.obtainedEvents(frames); + if (!status) { + throw new RuntimeException("Failed to find the traces before the wipe out."); + } + } + + public static void main(String[] args) { + HeapMonitor.enableSamplingRate(); + checkEventsAreBeingSent(); + + // Disabling the sampling should stop events. + HeapMonitor.disableSamplingRate(); + checkNoEventsAreBeingSent(); + + // Enabling the sampling should start events again. + HeapMonitor.enableSamplingRate(); + checkEventsAreBeingSent(); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays. + * @compile HeapMonitorStatArrayCorrectnessTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatArrayCorrectnessTest + */ + +public class HeapMonitorStatArrayCorrectnessTest { + + // Do 100000 iterations and expect maxIteration / multiplier samples. + private static final int maxIteration = 100000; + private static int array[]; + + private static void allocate(int size) { + for (int j = 0; j < maxIteration; j++) { + array = new int[size]; + } + } + + public static void main(String[] args) { + int sizes[] = {1000, 10000, 100000}; + + for (int currentSize : sizes) { + System.out.println("Testing size " + currentSize); + + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Should not have any events stored yet."); + } + + // 111 is as good a number as any. + final int samplingMultiplier = 111; + HeapMonitor.setSamplingRate(samplingMultiplier * currentSize); + + allocate(currentSize); + + // For simplifications, we ignore the array memory usage for array internals (with the array + // sizes requested, it should be a negligible oversight). + // + // That means that with maxIterations, the loop in the method allocate requests: + // maxIterations * currentSize * 4 bytes (4 for integers) + // + // Via the enable sampling, the code requests a sample every samplingMultiplier * currentSize bytes. + // + // Therefore, the expected sample number is: + // (maxIterations * currentSize * 4) / (samplingMultiplier * currentSize); + double expected = maxIteration; + expected *= 4; + expected /= samplingMultiplier; + + // 10% error ensures a sanity test without becoming flaky. + if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) { + throw new RuntimeException("Statistics should show about " + expected + " samples."); + } + + HeapMonitor.resetEventStorage(); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor Statistics + * @compile HeapMonitorStatObjectCorrectnessTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatObjectCorrectnessTest + */ + +/** This test is checking the object allocation path works with heap sampling. */ +public class HeapMonitorStatObjectCorrectnessTest { + + // Do 200000 iterations and expect maxIteration / multiplier samples. + private static final int maxIteration = 200000; + private static BigObject obj; + + private native static boolean statsHaveExpectedNumberSamples(int expected, int percentError); + + private static void allocate() { + for (int j = 0; j < maxIteration; j++) { + obj = new BigObject(); + } + } + + public static void main(String[] args) { + final int sizeObject = 1400; + + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should be null to begin with."); + } + + // 111 is as good a number as any. + final int samplingMultiplier = 111; + HeapMonitor.setSamplingRate(samplingMultiplier * sizeObject); + + allocate(); + + // For simplifications, the code is allocating: + // (BigObject size) * maxIteration. + // + // We ignore the class memory usage apart from field memory usage for BigObject. BigObject + // allocates 250 long, so 2000 bytes, so whatever is used for the class is negligible. + // + // That means that with maxIterations, the loop in the method allocate requests: + // maxIterations * 2000 bytes. + // + // Via the enable sampling, the code requests a sample every samplingMultiplier * sizeObject bytes. + // + // Therefore, the expected sample number is: + // (maxIterations * sizeObject) / (samplingMultiplier * sizeObject); + // + // Which becomes: + // maxIterations / samplingMultiplier + double expected = maxIteration; + expected /= samplingMultiplier; + + // 10% error ensures a sanity test without becoming flaky. + if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) { + throw new RuntimeException("Statistics should show about " + expected + " samples."); + } + + HeapMonitor.resetEventStorage(); + } + + /** + * Big class on purpose to just be able to ignore the class memory space overhead. + * + * Class contains 175 long fields, so 175 * 8 = 1400 bytes. + */ + private static class BigObject { + private long a0_0, a0_1, a0_2, a0_3, a0_4, a0_5, a0_6, a0_7, a0_8, a0_9; + private long a1_0, a1_1, a1_2, a1_3, a1_4, a1_5, a1_6, a1_7, a1_8, a1_9; + private long a2_0, a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8, a2_9; + private long a3_0, a3_1, a3_2, a3_3, a3_4, a3_5, a3_6, a3_7, a3_8, a3_9; + private long a4_0, a4_1, a4_2, a4_3, a4_4, a4_5, a4_6, a4_7, a4_8, a4_9; + private long a5_0, a5_1, a5_2, a5_3, a5_4, a5_5, a5_6, a5_7, a5_8, a5_9; + private long a6_0, a6_1, a6_2, a6_3, a6_4, a6_5, a6_6, a6_7, a6_8, a6_9; + private long a7_0, a7_1, a7_2, a7_3, a7_4, a7_5, a7_6, a7_7, a7_8, a7_9; + private long a8_0, a8_1, a8_2, a8_3, a8_4, a8_5, a8_6, a8_7, a8_8, a8_9; + private long a9_0, a9_1, a9_2, a9_3, a9_4, a9_5, a9_6, a9_7, a9_8, a9_9; + private long a10_0, a10_1, a10_2, a10_3, a10_4, a10_5, a10_6, a10_7, a10_8, a10_9; + private long a11_0, a11_1, a11_2, a11_3, a11_4, a11_5, a11_6, a11_7, a11_8, a11_9; + private long a12_0, a12_1, a12_2, a12_3, a12_4, a12_5, a12_6, a12_7, a12_8, a12_9; + private long a13_0, a13_1, a13_2, a13_3, a13_4, a13_5, a13_6, a13_7, a13_8, a13_9; + private long a14_0, a14_1, a14_2, a14_3, a14_4, a14_5, a14_6, a14_7, a14_8, a14_9; + private long a15_0, a15_1, a15_2, a15_3, a15_4, a15_5, a15_6, a15_7, a15_8, a15_9; + private long a16_0, a16_1, a16_2, a16_3, a16_4, a16_5, a16_6, a16_7, a16_8, a16_9; + private long a17_0, a17_1, a17_2, a17_3, a17_4; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @summary Verifies the JVMTI Heap Monitor sampling rate average + * @build Frame HeapMonitor + * @compile HeapMonitorStatRateTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatRateTest + */ + +public class HeapMonitorStatRateTest { + + private native static double getAverageRate(); + + public static void main(String[] args) { + int[] tab = {1024, 16384, 524288}; + + for (int rateIdx = 0; rateIdx < tab.length; rateIdx++) { + int rate = tab[rateIdx]; + + HeapMonitor.resetEventStorage(); + HeapMonitor.setSamplingRate(rate); + + int allocationTotal = 512 * 1024 * 1024; + HeapMonitor.allocateSize(allocationTotal); + + HeapMonitor.setSamplingRate(0); + + double actualCount = HeapMonitor.getEventStorageElementCount(); + double expectedCount = allocationTotal / rate; + + double error = Math.abs(actualCount - expectedCount); + double errorPercentage = error / expectedCount * 100; + + if (errorPercentage > 10) { + throw new RuntimeException("Rate average over 10% for rate " + rate + " -> " + actualCount + + ", " + expectedCount); + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor Statistics + * @compile HeapMonitorStatSimpleTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatSimpleTest + */ + +public class HeapMonitorStatSimpleTest { + private native static int areSamplingStatisticsZero(); + + public static void main(String[] args) { + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should be null to begin with."); + } + + HeapMonitor.enableSamplingRate(); + HeapMonitor.allocate(); + + if (HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should not be null now."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java @@ -42,7 +42,7 @@ throw new RuntimeException("Storage is not empty at test start..."); } - HeapMonitor.enableSampling(); + HeapMonitor.enableSamplingRate(); List frameList = HeapMonitor.allocate(); frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 46)); @@ -51,7 +51,7 @@ throw new RuntimeException("Events not found with the right frames."); } - HeapMonitor.disableSampling(); + HeapMonitor.disableSamplingRate(); HeapMonitor.resetEventStorage(); if (!HeapMonitor.eventStorageIsEmpty()) { throw new RuntimeException("Storage is not empty after reset."); diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor Thread sanity + * @compile HeapMonitorThreadOnOffTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadOnOffTest + */ + +import java.util.ArrayList; +import java.util.List; + +public class HeapMonitorThreadOnOffTest { + public static void main(String[] args) { + final int numThreads = 24; + ArrayList list = new ArrayList<>(); + + // Add one thread that consistently turns on/off the sampler to ensure correctness with + // potential resets. + Switch switchPlayer = new Switch(); + Thread switchThread = new Thread(switchPlayer, "Switch Player"); + switchThread.start(); + + for (int i = 0 ; i < numThreads; i++) { + Thread thread = new Thread(new Allocator(i), "Allocator" + i); + thread.start(); + list.add(thread); + } + + for (Thread elem : list) { + try { + elem.join(); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } + + switchPlayer.stop(); + try { + switchThread.join(); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted while waiting for the switch player..."); + } + + // We don't check here for correctness of data. If we made it here, the test succeeded: + // Threads can allocate like crazy + // Other threads can turn on/off the system + } +} + +class Allocator implements Runnable { + private int depth; + private volatile int tmp[]; + + public Allocator(int depth) { + this.depth = depth; + } + + private int helper() { + int sum = 0; + // Let us assume that the array is 24 bytes of memory. + for (int i = 0; i < 127000 / 6; i++) { + int newTmp[] = new int[1]; + // Force it to be kept. + tmp = newTmp; + sum += tmp[0]; + } + return sum; + } + + private int recursiveWrapper(int depth) { + if (depth > 0) { + return recursiveWrapper(depth - 1); + } + return helper(); + } + + public void run() { + int sum = 0; + for (int j = 0; j < 500; j++) { + sum += recursiveWrapper(depth); + } + } +} + +class Switch implements Runnable { + private volatile boolean keepGoing; + + public Switch() { + keepGoing = true; + } + + public void stop() { + keepGoing = false; + } + + public void run() { + while (keepGoing) { + HeapMonitor.disableSamplingRate(); + HeapMonitor.resetEventStorage(); + HeapMonitor.enableSamplingRate(); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor Thread sanity + * @compile HeapMonitorThreadTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest + */ + +import java.util.ArrayList; +import java.util.List; + +public class HeapMonitorThreadTest { + private native static boolean checkSamples(int numThreads); + + public static void main(String[] args) { + final int numThreads = 24; + ArrayList list = new ArrayList<>(); + + // Remember a lot of garbage to have space for all thread samples. + HeapMonitor.enableSamplingRate(); + HeapMonitor.setGarbageHistory(10000); + + for (int i = 0 ; i < numThreads; i++) { + Thread thread = new Thread(new Allocator(i), "Allocator" + i); + thread.start(); + list.add(thread); + } + + for (Thread elem : list) { + try { + elem.join(); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } + + if (!checkSamples(numThreads)) { + throw new RuntimeException("Problem with checkSamples..."); + } + } +} + +class Allocator implements Runnable { + private int depth; + private volatile int tmp[]; + + public Allocator(int depth) { + this.depth = depth; + } + + private int helper() { + int sum = 0; + // Let us assume that the array is 24 bytes of memory. + for (int i = 0; i < 127000 / 6; i++) { + int newTmp[] = new int[1]; + // Force it to be kept. + tmp = newTmp; + sum += tmp[0]; + } + return sum; + } + + private int recursiveWrapper(int depth) { + if (depth > 0) { + return recursiveWrapper(depth - 1); + } + return helper(); + } + + public void run() { + int sum = 0; + for (int j = 0; j < 50; j++) { + sum += recursiveWrapper(depth); + } + System.out.println(sum); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -50,15 +51,25 @@ static jvmtiEnv *jvmti = NULL; -typedef struct _LiveObjectTrace{ +typedef struct _ObjectTrace{ + jweak object; + size_t size; jvmtiFrameInfo* frames; size_t frame_count; -} LiveObjectTrace; + jthread thread; +} ObjectTrace; typedef struct _EventStorage { + int compaction_required; int live_object_size; int live_object_count; - LiveObjectTrace** live_objects; + ObjectTrace** live_objects; + + int garbage_history_size; + int garbage_history_index; + ObjectTrace** garbage_collected_objects; + + pthread_mutex_t mutex; } EventStorage; typedef struct _ExpectedContentFrame { @@ -110,7 +121,7 @@ } static jboolean check_sample_content(JNIEnv *env, - LiveObjectTrace* trace, + ObjectTrace* trace, ExpectedContentFrame *expected, size_t expected_count, int print_out_comparisons) { @@ -222,29 +233,106 @@ static EventStorage global_event_storage; +static void event_storage_set_compaction_required(EventStorage* storage) { + pthread_mutex_lock(&storage->mutex); + storage->compaction_required = 1; + pthread_mutex_unlock(&storage->mutex); +} + +static int event_storage_get_compaction_required(EventStorage* storage) { + pthread_mutex_lock(&storage->mutex); + int result = storage->compaction_required; + pthread_mutex_unlock(&storage->mutex); + return result; +} + +static void event_storage_set_garbage_history(EventStorage* storage, int value) { + pthread_mutex_lock(&storage->mutex); + global_event_storage.garbage_history_size = value; + free(global_event_storage.garbage_collected_objects); + size_t size = + sizeof(*global_event_storage.garbage_collected_objects) * value; + global_event_storage.garbage_collected_objects = malloc(size); + memset(global_event_storage.garbage_collected_objects, 0, size); + pthread_mutex_unlock(&storage->mutex); +} + +// No mutex here, it is handled by the caller. +static void event_storage_add_garbage_collected_object(EventStorage* storage, + ObjectTrace* object) { + int idx = storage->garbage_history_index; + free(storage->garbage_collected_objects[idx]); + storage->garbage_collected_objects[idx] = object; + storage->garbage_history_index = (idx + 1) % storage->garbage_history_size; +} + static int event_storage_get_count(EventStorage* storage) { - return storage->live_object_count; + pthread_mutex_lock(&storage->mutex); + int result = storage->live_object_count; + pthread_mutex_unlock(&storage->mutex); + return result; +} + +static double event_storage_get_average_rate(EventStorage* storage) { + double accumulation = 0; + + pthread_mutex_lock(&storage->mutex); + int max_size = storage->live_object_count; + + for (int i = 0; i < max_size; i++) { + accumulation += storage->live_objects[i]->size; + } + pthread_mutex_unlock(&storage->mutex); + return accumulation / max_size; } static jboolean event_storage_contains(JNIEnv* env, EventStorage* storage, ExpectedContentFrame* frames, size_t size) { + pthread_mutex_lock(&storage->mutex); fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); int i; for (i = 0; i < storage->live_object_count; i++) { - LiveObjectTrace* trace = storage->live_objects[i]; + ObjectTrace* trace = storage->live_objects[i]; if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { + pthread_mutex_unlock(&storage->mutex); return TRUE; } } + pthread_mutex_unlock(&storage->mutex); return FALSE; } +static jboolean event_storage_garbage_contains(JNIEnv* env, + EventStorage* storage, + ExpectedContentFrame* frames, + size_t size) { + pthread_mutex_lock(&storage->mutex); + fprintf(stderr, "Checking garbage storage count %d\n", + storage->garbage_history_size); + int i; + for (i = 0; i < storage->garbage_history_size; i++) { + ObjectTrace* trace = storage->garbage_collected_objects[i]; + + if (trace == NULL) { + continue; + } + + if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { + pthread_mutex_unlock(&storage->mutex); + return TRUE; + } + } + pthread_mutex_unlock(&storage->mutex); + return FALSE; +} + +// No mutex here, handled by the caller. static void event_storage_augment_storage(EventStorage* storage) { int new_max = (storage->live_object_size * 2) + 1; - LiveObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects)); + ObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects)); int current_count = storage->live_object_count; memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects)); @@ -255,6 +343,7 @@ } static void event_storage_add(EventStorage* storage, + JNIEnv* jni, jthread thread, jobject object, jclass klass, @@ -262,8 +351,11 @@ jvmtiFrameInfo frames[64]; jint count; jvmtiError err; + err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count); if (err == JVMTI_ERROR_NONE && count >= 1) { + pthread_mutex_lock(&storage->mutex); + if (storage->live_object_count >= storage->live_object_size) { event_storage_augment_storage(storage); } @@ -272,23 +364,69 @@ jvmtiFrameInfo* allocated_frames = malloc(count * sizeof(*allocated_frames)); memcpy(allocated_frames, frames, count * sizeof(*allocated_frames)); - LiveObjectTrace* live_object = malloc(sizeof(*live_object)); + ObjectTrace* live_object = malloc(sizeof(*live_object)); live_object->frames = allocated_frames; live_object->frame_count = count; + live_object->size = size; + live_object->thread = thread; + live_object->object = (*jni)->NewWeakGlobalRef(jni, object); storage->live_objects[storage->live_object_count] = live_object; storage->live_object_count++; + + pthread_mutex_unlock(&storage->mutex); + } +} + +static void event_storage_compact(EventStorage* storage, JNIEnv* jni) { + pthread_mutex_lock(&storage->mutex); + storage->compaction_required = 0; + + int max = storage->live_object_count; + int i, dest; + ObjectTrace** live_objects = storage->live_objects; + + for (i = 0, dest = 0; i < max; i++) { + ObjectTrace* live_object = live_objects[i]; + jweak object = live_object->object; + + if (!(*jni)->IsSameObject(jni, object, NULL)) { + if (dest != i) { + live_objects[dest] = live_object; + dest++; + } + } else { + event_storage_add_garbage_collected_object(storage, live_object); + } + } + + storage->live_object_count = dest; + pthread_mutex_unlock(&storage->mutex); +} + +static void event_storage_free_objects(ObjectTrace** array, int max) { + int i; + for (i = 0; i < max; i++) { + free(array[i]), array[i] = NULL; } } static void event_storage_reset(EventStorage* storage) { - int max = storage->live_object_count; - int i; - for (i = 0; i < max; i++) { - LiveObjectTrace* object = storage->live_objects[i]; - free(object); - } - free(storage->live_objects); - memset(storage, 0, sizeof(*storage)); + pthread_mutex_lock(&storage->mutex); + + // Reset everything except the mutex and the garbage collection. + event_storage_free_objects(storage->live_objects, + storage->live_object_count); + storage->live_object_size = 0; + storage->live_object_count = 0; + free(storage->live_objects), storage->live_objects = NULL; + + event_storage_free_objects(storage->garbage_collected_objects, + storage->garbage_history_size); + + storage->compaction_required = 0; + storage->garbage_history_index = 0; + + pthread_mutex_unlock(&storage->mutex); } // Start of the JVMTI agent code. @@ -349,10 +487,26 @@ jobject object, jclass object_klass, jlong size) { - event_storage_add(&global_event_storage, thread, object, object_klass, size); + if (event_storage_get_compaction_required(&global_event_storage)) { + event_storage_compact(&global_event_storage, jni_env); + } + + event_storage_add(&global_event_storage, jni_env, thread, object, + object_klass, size); +} + +JNIEXPORT +void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) { + event_storage_set_compaction_required(&global_event_storage); } static int enable_notifications() { + if (check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), + "Set event notifications")) { + return 1; + } + return check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), "Set event notifications"); @@ -369,9 +523,13 @@ return JNI_ERR; } + pthread_mutex_init(&global_event_storage.mutex, 0); + event_storage_set_garbage_history(&global_event_storage, 200); + jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.SampledObjectAlloc = &SampledObjectAlloc; + callbacks.GarbageCollectionFinish = &GarbageCollectionFinish; jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); @@ -379,6 +537,7 @@ caps.can_get_line_numbers = 1; caps.can_sample_heap = 1; caps.can_get_source_file_name = 1; + caps.can_generate_garbage_collection_events = 1; if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){ return JNI_ERR; } @@ -397,7 +556,12 @@ JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_setSamplingRate(JNIEnv* env, jclass cls, jint value) { - (*jvmti)->SetTlabHeapSampling(jvmti, value); + (*jvmti)->SetHeapSamplingRate(jvmti, value); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_setGarbageHistory(JNIEnv* env, jclass cls, jint value) { + event_storage_set_garbage_history(&global_event_storage, value); } JNIEXPORT jboolean JNICALL @@ -405,6 +569,11 @@ return event_storage_get_count(&global_event_storage) == 0; } +JNIEXPORT jint JNICALL +Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) { + return event_storage_get_count(&global_event_storage); +} + JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) { enable_notifications(); @@ -415,10 +584,10 @@ check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), "Set event notifications"); -} -JNIEXPORT void JNICALL -Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { + check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), + "Garbage Collection Finish"); } JNIEXPORT jboolean JNICALL @@ -429,6 +598,20 @@ return event_storage_contains(env, &global_event_storage, native_frames, size); } +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls, jobjectArray frames) { + jsize size = (*env)->GetArrayLength(env, frames); + ExpectedContentFrame native_frames[size]; + fill_native_frames(env, frames, native_frames, size); + return event_storage_garbage_contains(env, &global_event_storage, native_frames, size); +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) { + check_error((*jvmti)->ForceGarbageCollection(jvmti), + "Forced Garbage Collection"); +} + JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) { return event_storage_reset(&global_event_storage); @@ -445,13 +628,93 @@ return FALSE; } - if (check_capability_error((*jvmti)->SetTlabHeapSampling(jvmti, 1<<19), - "Set Tlab Heap Sampling")) { + if (check_capability_error((*jvmti)->SetHeapSamplingRate(jvmti, 1<<19), + "Set Heap Sampling Rate")) { return FALSE; } return TRUE; } +JNIEXPORT jdouble JNICALL +Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { + return event_storage_get_average_rate(&global_event_storage); +} + +typedef struct sThreadsFound { + jthread* threads; + int num_threads; +} ThreadsFound; + +static void find_threads_in_array(ThreadsFound* thread_data, + ObjectTrace** array, + int array_size) { + int i; + jthread* threads = thread_data->threads; + int num_threads = thread_data->num_threads; + + for (i = 0; i < array_size; i++) { + ObjectTrace* object = array[i]; + + if (object == NULL) { + continue; + } + + // Check it is the right frame: only accept helper top framed traces. + if (object->frame_count == 0) { + continue; + } + + jvmtiFrameInfo* frames = object->frames; + jthread thread = object->thread; + + jmethodID methodid = frames[0].method; + char *name = NULL, *signature = NULL, *file_name = NULL; + (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); + + if (strcmp(name, "helper")) { + continue; + } + + // Really not efficient look-up but it's for a test... + int found = 0; + int j; + for (j = 0; j < num_threads; j++) { + if (thread == threads[j]) { + found = 1; + break; + } + } + + if (!found) { + threads[num_threads] = thread; + num_threads++; + } + } + thread_data->num_threads = num_threads; +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, + jint num_threads) { + pthread_mutex_lock(&global_event_storage.mutex); + jint trace_counter; + + ThreadsFound thread_data; + thread_data.num_threads = 0; + thread_data.threads = malloc(sizeof(jthread) * num_threads); + memset(thread_data.threads, 0, sizeof(jthread) * num_threads); + + find_threads_in_array(&thread_data, global_event_storage.live_objects, + global_event_storage.live_object_count); + find_threads_in_array(&thread_data, + global_event_storage.garbage_collected_objects, + global_event_storage.garbage_history_size); + + free(thread_data.threads); + pthread_mutex_unlock(&global_event_storage.mutex); + return thread_data.num_threads == num_threads; +} + #ifdef __cplusplus } #endif