# HG changeset patch # User jcbeyler # Date 1522474476 25200 # Fri Mar 30 22:34:36 2018 -0700 # Node ID c9b94af6c2c492c38580c46ea7af88f07d51c80a # Parent be608cad0b2aa8a5e73f8925f26ab10409b1ee9c [mq]: heap8 diff --git a/make/nb_native/nbproject/configurations.xml b/make/nb_native/nbproject/configurations.xml --- a/make/nb_native/nbproject/configurations.xml +++ b/make/nb_native/nbproject/configurations.xml @@ -6154,6 +6154,9 @@ libIsModifiableModuleTest.c + + libHeapMonitorTest.c + libMAAClassFileLoadHook.c @@ -40165,6 +40168,11 @@ tool="0" flavor2="0"> + + 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 @@ -1242,9 +1242,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 @@ -202,6 +202,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(); @@ -240,6 +241,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; @@ -321,6 +323,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 @@ -337,6 +340,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 @@ -354,6 +358,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 @@ -372,6 +377,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 @@ -64,6 +64,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.inline.hpp" @@ -3631,6 +3632,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, @@ -2487,7 +2508,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))); @@ -2503,7 +2523,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) { @@ -2514,6 +2534,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) { @@ -2539,7 +2590,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) { @@ -2672,6 +2723,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); } } @@ -2688,14 +2742,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. } } } @@ -2733,31 +2792,25 @@ } // 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) { } // 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); - JvmtiExport::post_vm_object_alloc(JavaThread::current(), 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); } @@ -2765,7 +2818,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) { @@ -2775,7 +2828,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; @@ -2784,11 +2837,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(); } } } @@ -2823,6 +2882,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 @@ -237,6 +237,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; @@ -2049,6 +2050,7 @@ tlab().make_parsable(true); // retire TLAB, if any } + BarrierSet::barrier_set()->on_thread_detach(this); Threads::remove(this); @@ -4335,7 +4337,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/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) \ # HG changeset patch # User jcbeyler # Date 1522689232 25200 # Mon Apr 02 10:13:52 2018 -0700 # Node ID fc186fc4cf645ff2ecce2c4d4c59ec29a30774aa # Parent c9b94af6c2c492c38580c46ea7af88f07d51c80a [mq]: event_rebased 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 @@ -51,8 +51,6 @@ return 0; } - // TODO: To be deprecated when FastTLABRefill is deprecated. - update_end_pointers(); return pointer_delta(reserved_end(), top()); } @@ -125,8 +123,6 @@ myThread()->incr_allocated_bytes(used_bytes()); } - // 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 @@ -135,12 +131,11 @@ set_pf_top(NULL); set_current_end(NULL); set_allocation_end(NULL); - set_last_slow_path_end(NULL); } } assert(!(retire || ZeroTLAB) || (start() == NULL && current_end() == NULL && top() == NULL && - _allocation_end == NULL && _last_slow_path_end == NULL), + _allocation_end == NULL), "TLAB must be reset"); } @@ -205,7 +200,6 @@ set_pf_top(top); set_current_end(end); set_allocation_end(end); - set_last_slow_path_end(end); invariants(); } @@ -336,7 +330,6 @@ 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;; @@ -350,20 +343,9 @@ } 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(); @@ -378,8 +360,6 @@ } 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(); } 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 @@ -43,8 +43,6 @@ // 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; @@ -54,7 +52,6 @@ HeapWord* _pf_top; // allocation prefetch watermark 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 @@ -79,7 +76,6 @@ void set_start(HeapWord* start) { _start = start; } 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; } @@ -92,8 +88,6 @@ size_t remaining(); - void update_end_pointers(); - // Make parsable and release it. void reset(); 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 @@ -229,6 +229,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); @@ -258,12 +259,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); @@ -271,6 +274,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 @@ -10353,17 +10353,12 @@ 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. + Can generate sampled allocation events. + If this capability is enabled then the heap sampling method + can be called and the + events can be enabled. @@ -11545,29 +11540,32 @@ - - 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 allocations 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. - Combined with a event, the Java agent can obtain object allocations with a given sample rate. - - new - - - - - +

+ Setting to 0 samples each allocation supported by the system. + 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. + The sampling rate in bytes used for sampling. The sampler will use a statistical approach to + provide in average sampling every allocated bytes. +

+ Passing 0 as a sampling rate provokes a sample per allocation by the system. +

+ 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. @@ -11576,10 +11574,7 @@ - is less than zero. - - - Calling this method without TLAB. + is less than zero. @@ -11793,9 +11788,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. - @@ -13552,13 +13544,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. @@ -13570,17 +13562,32 @@ - + - Sent when an object is sampled via the + Sent when an allocated 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 +

+ By default, the sampling rate used is geometric variable with a 512kb mean, meaning one object every 512k bytes, + in average, per thread will + provoke a callback. Each thread having its own internal allocation count, each thread will be sampling + at the same default rate of 512kb. +

+ If another sampling rate is required, the user can call + with a strictly positive integer value, representing + the new sampling rate to be used by the default sampler. +

+ This event is sent once the sampled 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/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -538,6 +538,14 @@ if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { record_class_file_load_hook_enabled(); } + + if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) { + if (enabled) { + ThreadHeapSampler::enable(); + } else { + ThreadHeapSampler::disable(); + } + } JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled); } else { // We have a specified event_thread. @@ -569,6 +577,14 @@ if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { record_class_file_load_hook_enabled(); } + + if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) { + if (enabled) { + ThreadHeapSampler::enable(); + } else { + ThreadHeapSampler::disable(); + } + } JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled); } @@ -3633,10 +3649,13 @@ } /* end GetAvailableProcessors */ jvmtiError -JvmtiEnv::SetTlabHeapSampling(jint monitoring_rate) { - ThreadHeapSampler::set_tlab_heap_sampling(monitoring_rate); +JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) { + if (sampling_rate < 0) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } + ThreadHeapSampler::set_tlab_heap_sampling(sampling_rate); return JVMTI_ERROR_NONE; -} /* end SetTlabHeapSampling */ +} /* end SetHeapSamplingRate */ // // System Properties functions 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 @@ -2900,7 +2900,6 @@ 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(); } 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 @@ -501,7 +501,6 @@ // collection of objects allocated while running java code inside // 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); 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,8 +141,7 @@ 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; + jc.can_generate_sampled_alloc_events = 1; return jc; } @@ -412,10 +411,8 @@ 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_generate_sampled_alloc_events) + log_trace(jvmti)("can_generate_sampled_alloc_events"); if (cap->can_suspend) log_trace(jvmti)("can_suspend"); if (cap->can_redefine_any_class ) 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 @@ -126,6 +126,8 @@ Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; +Monitor* ThreadHeapSampler_lock = NULL; + #if INCLUDE_TRACE Mutex* JfrStacktrace_lock = NULL; Monitor* JfrMsg_lock = NULL; @@ -286,6 +288,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 @@ -124,6 +124,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 new file mode 100644 --- /dev/null +++ b/src/hotspot/share/runtime/threadHeapSampler.cpp @@ -0,0 +1,143 @@ +/* + * 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; +// Default is 512kb. +int ThreadHeapSampler::_sampling_rate = 512 * 1024; +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_geometric_sample() { + _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) * (_sampling_rate)) + 1); + _bytes_until_sample = rate; +} + +void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) { + if (_sampling_rate == 1) { + _bytes_until_sample = 1; + return; + } + + pick_next_geometric_sample(); + + // 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::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) { + MutexLocker mu(ThreadHeapSampler_lock); + _sampling_rate = sampling_rate; +} 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,66 @@ +/* + * 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_geometric_sample(); + void pick_next_sample(size_t overflowed_bytes = 0); + static int _enabled; + static int _sampling_rate; + + void init_log_table(); + + public: + ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) { + _rnd = static_cast(reinterpret_cast(this)); + if (_rnd == 0) { + _rnd = 1; + } + init_log_table(); + } + + 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 enable() { OrderAccess::release_store(&_enabled, 1); } + static void disable() { OrderAccess::release_store(&_enabled, 0); } + 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 new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java @@ -0,0 +1,38 @@ +/* + * 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; + +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,167 @@ +/* + * 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; + } + } + + /** Set a specific sampling rate, 0 samples every allocation. */ + 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. + * + * @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", 57)); + } else { + actuallyAllocate(); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 60)); + } + 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", 75)); + 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", 82)); + } else { + allocate(frames); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 85)); + } + } + + 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", 120)); + frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 93)); + } + + 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", 102)); + 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; + } + + 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/HeapMonitorArrayAllSampledTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.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 + * @build Frame HeapMonitor + * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays. + * @compile HeapMonitorArrayAllSampledTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorArrayAllSampledTest + */ + +public class HeapMonitorArrayAllSampledTest { + + // Do 1000 iterations and expect maxIteration samples. + private static final int maxIteration = 1000; + 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, 1000000}; + + HeapMonitor.setSamplingRate(0); + HeapMonitor.enableSamplingEvents(); + + for (int currentSize : sizes) { + System.out.println("Testing size " + currentSize); + + HeapMonitor.resetEventStorage(); + allocate(currentSize); + + // 10% error ensures a sanity test without becoming flaky. + if (!HeapMonitor.statsHaveExpectedNumberSamples(maxIteration, 10)) { + throw new RuntimeException("Statistics should show about " + maxIteration + " samples."); + } + } + } +} 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.enableSamplingEvents(); + 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.enableSamplingEvents(); + + 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/HeapMonitorIllegalArgumentTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java @@ -0,0 +1,44 @@ +/* + * 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 SetHeapSamplingRate returns an illegal argument for negative ints. + * @build Frame HeapMonitor + * @compile HeapMonitorIllegalArgumentTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorIllegalArgumentTest + */ + +public class HeapMonitorIllegalArgumentTest { + private native static int testIllegalArgument(); + + public static void main(String[] args) { + int result = testIllegalArgument(); + + if (result == 0) { + throw new RuntimeException("Test illegal argument failed."); + } + } +} 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/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,44 @@ +/* + * 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. + * @build Frame HeapMonitor + * @compile HeapMonitorNoCapabilityTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorNoCapabilityTest + */ + +public class HeapMonitorNoCapabilityTest { + 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/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,85 @@ +/* + * 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}; + + HeapMonitor.enableSamplingEvents(); + + for (int currentSize : sizes) { + System.out.println("Testing size " + currentSize); + + HeapMonitor.resetEventStorage(); + 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."); + } + } + } +} 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,139 @@ +/* + * 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 sampling via object allocation. + * @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(); + } + } + + private static void testBigAllocationRate() { + final int sizeObject = 1400; + + // 111 is as good a number as any. + final int samplingMultiplier = 111; + HeapMonitor.setSamplingRate(samplingMultiplier * sizeObject); + + emptyStorage(); + 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."); + } + } + + private static void emptyStorage() { + HeapMonitor.resetEventStorage(); + + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should be null to begin with."); + } + } + + private static void testEveryAllocationSampled() { + // 0 means sample every allocation. + HeapMonitor.setSamplingRate(0); + + emptyStorage(); + allocate(); + + double expected = maxIteration; + + // 10% error ensures a sanity test without becoming flaky. + if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) { + throw new RuntimeException("Statistics should show about " + expected + " samples."); + } + } + + public static void main(String[] args) { + HeapMonitor.enableSamplingEvents(); + + testBigAllocationRate(); + testEveryAllocationSampled(); + } + + /** + * 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,66 @@ +/* + * 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); + + HeapMonitor.enableSamplingEvents(); + + int allocationTotal = 512 * 1024 * 1024; + HeapMonitor.allocateSize(allocationTotal); + + HeapMonitor.disableSamplingEvents(); + + 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,50 @@ +/* + * 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 events are only sent after enabling. + * @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) { + HeapMonitor.allocate(); + if (!HeapMonitor.eventStorageIsEmpty()) { + throw new RuntimeException("Statistics should be null to begin with."); + } + + HeapMonitor.enableSamplingEvents(); + 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 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.enableSamplingEvents(); + 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.disableSamplingEvents(); + 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/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.disableSamplingEvents(); + HeapMonitor.resetEventStorage(); + HeapMonitor.enableSamplingEvents(); + } + } +} 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,172 @@ +/* + * 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 information sanity + * @compile HeapMonitorThreadTest.java + * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class HeapMonitorThreadTest { + private native static boolean checkSamples(int numThreads); + + public static void main(String[] args) { + final int numThreads = 24; + ArrayList threadList = new ArrayList<>(); + + HeapMonitor.enableSamplingEvents(); + + // The test creates numThreads threads, each allocate a lot. Then they inform the main thread + // they are done allocating. The test looks if all threads are available in the collected event + // information and then proceeds to tell each thread to stop running before leaving the test. + for (int i = 0 ; i < numThreads; i++) { + BlockingQueue queue = new LinkedBlockingQueue<>(); + Allocator allocator = new Allocator(i, queue); + Thread thread = new Thread(allocator, "Allocator" + i); + thread.start(); + + ThreadInformation info = new ThreadInformation(thread, queue, allocator); + threadList.add(info); + } + + // Wait until all threads have put an object in the queue. + for (ThreadInformation info : threadList) { + info.waitForAllocations(); + } + + if (!checkSamples(numThreads)) { + throw new RuntimeException("Problem with checkSamples..."); + } + + // Now inform each thread we are done and wait for them to be done. + for (ThreadInformation info : threadList) { + info.stop(); + } + } +} + +class ThreadInformation { + private Thread thread; + private BlockingQueue finishedAllocationQueue; + private Allocator allocator; + + public ThreadInformation(Thread thread, BlockingQueue finishedAllocationQueue, + Allocator allocator) { + this.thread = thread; + this.finishedAllocationQueue = finishedAllocationQueue; + this.allocator = allocator; + } + + public void waitForAllocations() { + try { + finishedAllocationQueue.take(); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } + + public void stop() { + try { + allocator.stopRun(); + thread.join(); + + if (!allocator.endedNormally()) { + throw new RuntimeException("Thread did not end normally..."); + } + + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } +} + +class Allocator implements Runnable { + private int depth; + private List currentList; + private BlockingQueue finishedAllocationQueue; + private BlockingQueue jobCanStop; + private boolean failed; + + public Allocator(int depth, BlockingQueue finishedAllocationQueue) { + jobCanStop = new LinkedBlockingQueue<>(); + this.finishedAllocationQueue = finishedAllocationQueue; + this.depth = depth; + } + + public boolean endedNormally() { + return !failed; + } + + private void helper() { + List newList = new ArrayList<>(); + // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in + // memory at least 2MB without counting the link-list itself, which adds to this. + int iterations = (1 << 21) / 24; + for (int i = 0; i < iterations; i++) { + int newTmp[] = new int[1]; + // Force it to be kept. + newList.add(newTmp); + } + + // Replace old list with new list, which provokes two things: + // Old list will get GC'd at some point. + // New list forces that this thread has some allocations still sampled. + currentList = newList; + } + + private void recursiveWrapper(int depth) { + if (depth > 0) { + recursiveWrapper(depth - 1); + } + helper(); + } + + public void stopRun() throws InterruptedException { + jobCanStop.put(new Object()); + } + + public void run() { + for (int j = 0; j < 50; j++) { + recursiveWrapper(depth); + } + + try { + // Tell the main thread we are done. + finishedAllocationQueue.put(new Object()); + + // Wait until the main thread says we can stop. + jobCanStop.take(); + } catch (InterruptedException e) { + failed = true; + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c @@ -0,0 +1,748 @@ +/* + * 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 +#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 _ObjectTrace{ + jweak object; + size_t size; + jvmtiFrameInfo* frames; + size_t frame_count; + jthread thread; +} ObjectTrace; + +typedef struct _EventStorage { + int live_object_size; + int live_object_count; + ObjectTrace** live_objects; + + int garbage_history_size; + int garbage_history_index; + ObjectTrace** garbage_collected_objects; + + // Two separate mutexes to separate storage data race and the compaction field + // data race. + pthread_mutex_t storage_mutex; + + int compaction_required; + pthread_mutex_t compaction_mutex; +} 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; + 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) && + (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, + ObjectTrace* trace, + ExpectedContentFrame *expected, + size_t expected_count, + int print_out_comparisons) { + if (expected_count > trace->frame_count) { + return FALSE; + } + + jvmtiFrameInfo* frames = trace->frames; + + 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; + 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) { + 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, + "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 void event_storage_set_compaction_required(EventStorage* storage) { + pthread_mutex_lock(&storage->compaction_mutex); + storage->compaction_required = 1; + pthread_mutex_unlock(&storage->compaction_mutex); +} + +static int event_storage_get_compaction_required(EventStorage* storage) { + pthread_mutex_lock(&storage->compaction_mutex); + int result = storage->compaction_required; + pthread_mutex_unlock(&storage->compaction_mutex); + return result; +} + +static void event_storage_set_garbage_history(EventStorage* storage, int value) { + pthread_mutex_lock(&storage->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->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) { + pthread_mutex_lock(&storage->storage_mutex); + int result = storage->live_object_count; + pthread_mutex_unlock(&storage->storage_mutex); + return result; +} + +static double event_storage_get_average_rate(EventStorage* storage) { + double accumulation = 0; + + pthread_mutex_lock(&storage->storage_mutex); + int max_size = storage->live_object_count; + + int i; + for (i = 0; i < max_size; i++) { + accumulation += storage->live_objects[i]->size; + } + pthread_mutex_unlock(&storage->storage_mutex); + return accumulation / max_size; +} + +static jboolean event_storage_contains(JNIEnv* env, + EventStorage* storage, + ExpectedContentFrame* frames, + size_t size) { + pthread_mutex_lock(&storage->storage_mutex); + fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); + int i; + for (i = 0; i < storage->live_object_count; i++) { + ObjectTrace* trace = storage->live_objects[i]; + + if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { + pthread_mutex_unlock(&storage->storage_mutex); + return TRUE; + } + } + pthread_mutex_unlock(&storage->storage_mutex); + return FALSE; +} + +static jboolean event_storage_garbage_contains(JNIEnv* env, + EventStorage* storage, + ExpectedContentFrame* frames, + size_t size) { + pthread_mutex_lock(&storage->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->storage_mutex); + return TRUE; + } + } + pthread_mutex_unlock(&storage->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; + 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)); + free(storage->live_objects); + storage->live_objects = new_objects; + + storage->live_object_size = new_max; +} + +static void event_storage_add(EventStorage* storage, + JNIEnv* jni, + 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) { + jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames)); + memcpy(allocated_frames, frames, count * sizeof(*allocated_frames)); + + ObjectTrace* live_object = (ObjectTrace*) 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); + + // Only now lock and get things done quickly. + pthread_mutex_lock(&storage->storage_mutex); + + if (storage->live_object_count >= storage->live_object_size) { + event_storage_augment_storage(storage); + } + assert(storage->live_object_count < storage->live_object_size); + + storage->live_objects[storage->live_object_count] = live_object; + storage->live_object_count++; + + pthread_mutex_unlock(&storage->storage_mutex); + } +} + +static void event_storage_compact(EventStorage* storage, JNIEnv* jni) { + pthread_mutex_lock(&storage->compaction_mutex); + storage->compaction_required = 0; + pthread_mutex_unlock(&storage->compaction_mutex); + + pthread_mutex_lock(&storage->storage_mutex); + + 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->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) { + pthread_mutex_lock(&storage->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->storage_mutex); +} + +// 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) { + 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"); +} + +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; + } + + pthread_mutex_init(&global_event_storage.storage_mutex, 0); + pthread_mutex_init(&global_event_storage.compaction_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)); + // Get line numbers, sample heap, sample events, and filename for the test. + caps.can_get_line_numbers = 1; + caps.can_generate_sampled_alloc_events = 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; + } + + if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, + sizeof(jvmtiEventCallbacks)), + " Set Event Callbacks")) { + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_setSamplingRate(JNIEnv* env, jclass cls, jint value) { + (*jvmti)->SetHeapSamplingRate(jvmti, value); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) { + 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(); +} + +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"); + + check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), + "Garbage Collection Finish"); +} + +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 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); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, + jclass cls) { + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_generate_sampled_alloc_events = 1; + if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), + "Add capabilities\n")){ + return FALSE; + } + + if (check_capability_error((*jvmti)->SetHeapSamplingRate(jvmti, 1<<19), + "Set Heap Sampling Rate")) { + return FALSE; + } + return TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env, + jclass cls) { + if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 0), + "Sampling rate 0 failed\n")){ + return FALSE; + } + + if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 1024), + "Sampling rate 1024 failed\n")){ + return FALSE; + } + + if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1), + "Sampling rate -1 passed\n")){ + return FALSE; + } + + if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1024), + "Sampling rate -1024 passed\n")){ + 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.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); + + free(thread_data.threads); + pthread_mutex_unlock(&global_event_storage.storage_mutex); + fprintf(stderr, "Obtained %d - %d\n", + thread_data.num_threads, + num_threads); + return thread_data.num_threads == num_threads; +} + +#ifdef __cplusplus +} +#endif # HG changeset patch # User jcbeyler # Date 1522885485 25200 # Wed Apr 04 16:44:45 2018 -0700 # Node ID 1fba53555a7b286ee1c8e34ce229298e346488c8 # Parent fc186fc4cf645ff2ecce2c4d4c59ec29a30774aa [mq]: heap9 diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -329,6 +329,7 @@ JRT_ENTRY(void, Runtime1::new_instance(JavaThread* thread, Klass* klass)) NOT_PRODUCT(_new_instance_slowcase_cnt++;) + JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "not a class"); Handle holder(THREAD, klass->klass_holder()); // keep the klass alive @@ -344,6 +345,7 @@ JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* thread, Klass* klass, jint length)) NOT_PRODUCT(_new_type_array_slowcase_cnt++;) + JvmtiSampledObjectAllocEventCollector collector; // Note: no handle for klass needed since they are not used // anymore after new_typeArray() and no GC can happen before. // (This may have to change if this code changes!) @@ -362,6 +364,7 @@ JRT_ENTRY(void, Runtime1::new_object_array(JavaThread* thread, Klass* array_klass, jint length)) NOT_PRODUCT(_new_object_array_slowcase_cnt++;) + JvmtiSampledObjectAllocEventCollector collector; // Note: no handle for klass needed since they are not used // anymore after new_objArray() and no GC can happen before. @@ -381,6 +384,7 @@ JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims)) NOT_PRODUCT(_new_multi_array_slowcase_cnt++;) + JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "not a class"); assert(rank >= 1, "rank must be nonzero"); 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 @@ -358,6 +358,12 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, Thread* thread, size_t size) { HeapWord* obj = NULL; + // In assertion mode, check that there was a sampling collector present + // in the stack. This enforces checking that no path is without a sampling + // collector. + assert(thread->heap_sampler().sampling_collector_present(), + "Sampling collector not present."); + 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. 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 @@ -217,6 +217,7 @@ } oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -227,6 +228,7 @@ } oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -240,6 +242,7 @@ int size, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -253,6 +256,7 @@ int size, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); 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 @@ -850,6 +850,7 @@ IRT_END void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode) { + JvmtiSampledObjectAllocEventCollector collector; Thread* THREAD = thread; LastFrameAccessor last_frame(thread); // extract receiver from the outgoing argument list if necessary diff --git a/src/hotspot/share/memory/oopFactory.cpp b/src/hotspot/share/memory/oopFactory.cpp --- a/src/hotspot/share/memory/oopFactory.cpp +++ b/src/hotspot/share/memory/oopFactory.cpp @@ -41,6 +41,7 @@ typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) { int length = utf8_str == NULL ? 0 : UTF8::unicode_length(utf8_str); + JvmtiSampledObjectAllocEventCollector collector; typeArrayOop result = new_charArray(length, CHECK_NULL); if (length > 0) { UTF8::convert_to_unicode(utf8_str, result->char_at_addr(0), length); @@ -49,10 +50,12 @@ } typeArrayOop oopFactory::new_tenured_charArray(int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; return TypeArrayKlass::cast(Universe::charArrayKlassObj())->allocate(length, THREAD); } typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); typeArrayOop result = type_asArrayKlass->allocate(length, THREAD); @@ -64,6 +67,7 @@ // int or long depending on pointer size. Only stack trace elements in Throwable use // this. They cast Symbol* into this type. typeArrayOop oopFactory::new_symbolArray(int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; BasicType type = LP64_ONLY(T_LONG) NOT_LP64(T_INT); Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); @@ -72,6 +76,7 @@ } typeArrayOop oopFactory::new_typeArray_nozero(BasicType type, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); typeArrayOop result = type_asArrayKlass->allocate_common(length, false, THREAD); @@ -80,6 +85,7 @@ objArrayOop oopFactory::new_objArray(Klass* klass, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "must be instance class"); if (klass->is_array_klass()) { return ArrayKlass::cast(klass)->allocate_arrayArray(1, length, THREAD); @@ -89,11 +95,13 @@ } objArrayHandle oopFactory::new_objArray_handle(Klass* klass, int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; objArrayOop obj = new_objArray(klass, length, CHECK_(objArrayHandle())); return objArrayHandle(THREAD, obj); } typeArrayHandle oopFactory::new_byteArray_handle(int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; typeArrayOop obj = new_byteArray(length, CHECK_(typeArrayHandle())); return typeArrayHandle(THREAD, obj); } diff --git a/src/hotspot/share/memory/oopFactory.hpp b/src/hotspot/share/memory/oopFactory.hpp --- a/src/hotspot/share/memory/oopFactory.hpp +++ b/src/hotspot/share/memory/oopFactory.hpp @@ -32,6 +32,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.hpp" #include "oops/typeArrayKlass.hpp" +#include "prims/jvmtiExport.hpp" #include "utilities/growableArray.hpp" // oopFactory is a class used for creating new objects. @@ -41,14 +42,38 @@ class oopFactory: AllStatic { public: // Basic type leaf array allocation - static typeArrayOop new_boolArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD); } - static typeArrayOop new_charArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD); } - static typeArrayOop new_singleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD); } - static typeArrayOop new_doubleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD); } - static typeArrayOop new_byteArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD); } - static typeArrayOop new_shortArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD); } - static typeArrayOop new_intArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD); } - static typeArrayOop new_longArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_boolArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD); + } + static typeArrayOop new_charArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD); + } + static typeArrayOop new_singleArray(int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD); + } + static typeArrayOop new_doubleArray(int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD); + } + static typeArrayOop new_byteArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD); + } + static typeArrayOop new_shortArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD); + } + static typeArrayOop new_intArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD); + } + static typeArrayOop new_longArray (int length, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; + return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD); + } // create java.lang.Object[] static objArrayOop new_objectArray(int length, TRAPS) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1002,6 +1002,7 @@ } instanceOop InstanceKlass::allocate_instance(TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; bool has_finalizer_flag = has_finalizer(); // Query before possible GC int size = size_helper(); // Query before forming handle. diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp --- a/src/hotspot/share/oops/objArrayKlass.cpp +++ b/src/hotspot/share/oops/objArrayKlass.cpp @@ -58,6 +58,7 @@ Klass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_data, int n, Klass* element_klass, TRAPS) { + JvmtiSampledObjectAllocEventCollector collector; // Eagerly allocate the direct array supertype. Klass* super_klass = NULL; if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) { diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -631,6 +631,7 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) JVMWrapper("JVM_Clone"); + JvmtiSampledObjectAllocEventCollector collector; Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); Klass* klass = obj->klass(); JvmtiVMObjectAllocEventCollector oam; @@ -3314,6 +3315,7 @@ JVM_ENTRY(jobject, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length)) JVMWrapper("JVM_NewArray"); + JvmtiSampledObjectAllocEventCollector collector; JvmtiVMObjectAllocEventCollector oam; oop element_mirror = JNIHandles::resolve(eltClass); oop result = Reflection::reflect_new_array(element_mirror, length, CHECK_NULL); @@ -3323,6 +3325,7 @@ JVM_ENTRY(jobject, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim)) JVMWrapper("JVM_NewMultiArray"); + JvmtiSampledObjectAllocEventCollector collector; JvmtiVMObjectAllocEventCollector oam; arrayOop dim_array = check_array(env, dim, true, CHECK_NULL); oop element_mirror = JNIHandles::resolve(eltClass); @@ -3487,6 +3490,9 @@ if (JvmtiExport::should_post_vm_object_alloc()) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } + if (JvmtiExport::should_post_sampled_object_alloc()) { + JvmtiExport::post_sampled_object_alloc(JavaThread::current(), result); + } return res; JVM_END 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 @@ -13563,7 +13563,7 @@ + id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" filtered="thread" since="11"> Sent when an allocated object is sampled via the Heap Sampling Monitoring system . 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 @@ -96,7 +96,8 @@ static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT | FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT; static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS | - BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT; + BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT | + SAMPLED_OBJECT_ALLOC_BIT; static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT; static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT | 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 @@ -2284,7 +2284,7 @@ // 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 ) { + if (state != NULL) { // state is non NULL when VMObjectAllocEventCollector is enabled. JvmtiVMObjectAllocEventCollector *collector; collector = state->get_vm_object_alloc_event_collector(); @@ -2308,10 +2308,11 @@ // 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 ) { + 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); } @@ -2508,7 +2509,7 @@ } } -void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { +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))); if (object == NULL) { @@ -2534,7 +2535,7 @@ } } -void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { +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))); @@ -2543,19 +2544,25 @@ return; } + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == 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)) { + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->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); + JvmtiEnv *env = ets->get_env(); + JvmtiThreadEventTransition jet(thread); jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; if (callback != NULL) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2820,7 +2827,7 @@ // GC support. void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) { if (_allocated != NULL) { - for(int i=_allocated->length() - 1; i >= 0; i--) { + for(int i = _allocated->length() - 1; i >= 0; i--) { if (_allocated->at(i) != NULL) { f->do_oop(_allocated->adr_at(i)); } @@ -2903,11 +2910,21 @@ _post_callback = JvmtiExport::post_sampled_object_alloc; setup_jvmti_thread_state(); } + // Set the sampling collector as present in assertion mode only. + assert(JavaThread::current()->heap_sampler().add_sampling_collector(), + "Should never return false."); } JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() { - generate_call_for_allocated(); + if (JvmtiExport::should_post_sampled_object_alloc()) { + generate_call_for_allocated(); + } + // Note: we set ourselves in the thread state only if we should post but + // always unset in case the user has disabled the posting but we were set. unset_jvmti_thread_state(); + // Set the sampling collector as present in assertion mode only. + assert(JavaThread::current()->heap_sampler().remove_sampling_collector(), + "Should never return false."); } JvmtiGCMarker::JvmtiGCMarker() { 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 @@ -130,6 +130,7 @@ memset(&jc, 0, sizeof(jc)); jc.can_suspend = 1; + jc.can_generate_sampled_alloc_events = 1; return jc; } @@ -141,7 +142,6 @@ jc.can_generate_field_modification_events = 1; jc.can_generate_field_access_events = 1; jc.can_generate_breakpoint_events = 1; - jc.can_generate_sampled_alloc_events = 1; return jc; } 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 @@ -289,7 +289,7 @@ 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); + def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_sometimes); if (WhiteBoxAPI) { def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never); 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 @@ -141,3 +141,24 @@ MutexLocker mu(ThreadHeapSampler_lock); _sampling_rate = sampling_rate; } + +bool ThreadHeapSampler::sampling_collector_present() const { + return _collectors_present > 0; +} + +bool ThreadHeapSampler::remove_sampling_collector() { + _collectors_present--; + return true; +} + +bool ThreadHeapSampler::add_sampling_collector() { + _collectors_present++; + return true; +} + +void ThreadHeapSampler::enable() { + // Done here to be done when things have settled. This adds a mutex lock but + // presumably, users won't be enabling and disabling all the time. + init_log_table(); + OrderAccess::release_store(&_enabled, 1); +} 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 @@ -39,7 +39,11 @@ static int _enabled; static int _sampling_rate; - void init_log_table(); + // Used for assertion mode to determine if there is a path to a TLAB slow path + // without a collector present. + int _collectors_present; + + static void init_log_table(); public: ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) { @@ -47,7 +51,8 @@ if (_rnd == 0) { _rnd = 1; } - init_log_table(); + + _collectors_present = 0; } void set_thread(Thread* t) { _thread = t; } @@ -58,9 +63,13 @@ 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 enable() { OrderAccess::release_store(&_enabled, 1); } + static void enable(); static void disable() { OrderAccess::release_store(&_enabled, 0); } static void set_tlab_heap_sampling(int sampling_rate); + + bool sampling_collector_present() const; + bool remove_sampling_collector(); + bool add_sampling_collector(); }; #endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP 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 @@ -29,10 +29,11 @@ /** API for handling the underlying heap sampling monitoring system. */ public class HeapMonitor { private static int[][] arrays; + private static int allocationIterations = 1000; static { try { - System.loadLibrary("HeapMonitor"); + System.loadLibrary("HeapMonitorTest"); } catch (UnsatisfiedLinkError ule) { System.err.println("Could not load HeapMonitor library"); System.err.println("java.library.path: " + System.getProperty("java.library.path")); @@ -42,8 +43,8 @@ /** Set a specific sampling rate, 0 samples every allocation. */ public native static void setSamplingRate(int rate); - public native static void enableSamplingEvents(); + public native static void enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread); public native static void disableSamplingEvents(); /** @@ -55,11 +56,11 @@ List frames = new ArrayList(); if (depth > 1) { createStackDepth(depth - 1, frames); - frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 57)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 58)); } else { actuallyAllocate(); - frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120)); - frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 60)); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 121)); + frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 61)); } return frames; } @@ -73,27 +74,27 @@ int sum = 0; List frames = new ArrayList(); allocate(frames); - frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 75)); + frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 76)); 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", 82)); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 83)); } else { allocate(frames); - frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 85)); + frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 86)); } } private static void allocate(List frames) { int sum = 0; - for (int j = 0; j < 1000; j++) { + for (int j = 0; j < allocationIterations; j++) { sum += actuallyAllocate(); } - frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120)); - frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 93)); + frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 121)); + frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 94)); } public static List repeatAllocate(int max) { @@ -101,7 +102,7 @@ for (int i = 0; i < max; i++) { frames = allocate(); } - frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 102)); + frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 103)); return frames; } @@ -164,4 +165,8 @@ double diffPercentage = Math.abs(actual - expected) / expected; return diffPercentage < acceptedErrorPercentage; } + + public static void setAllocationIterations(int iterations) { + allocationIterations = iterations; + } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java @@ -28,7 +28,7 @@ * @build Frame HeapMonitor * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays. * @compile HeapMonitorArrayAllSampledTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorArrayAllSampledTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorArrayAllSampledTest */ public class HeapMonitorArrayAllSampledTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java @@ -30,7 +30,7 @@ * @summary Verifies if turning off the event notification stops events. * @build Frame HeapMonitor * @compile HeapMonitorEventOnOffTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorEventOnOffTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventOnOffTest */ public class HeapMonitorEventOnOffTest { private static void checkNoEventsAreBeingSent() { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java @@ -0,0 +1,58 @@ +/* + * 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 ThreadInformation + * @summary Verifies the JVMTI Heap Monitor for only two threads + * @compile HeapMonitorEventsForTwoThreadsTest.java + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest + */ + +import java.util.List; + +public class HeapMonitorEventsForTwoThreadsTest { + private native static boolean checkSamples(String first, String second); + + public static void main(String[] args) { + final int numThreads = 24; + List threadList = ThreadInformation.createThreadList(numThreads); + + Thread firstThread = threadList.get(0).getThread(); + Thread secondThread = threadList.get(1).getThread(); + // HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread); + + ThreadInformation.startThreads(threadList); + ThreadInformation.waitForThreads(threadList); + + // Should only see events for the two threads. + if (!checkSamples(firstThread.getName(), secondThread.getName())) { + throw new RuntimeException("Problem with checkSamples..."); + } + + // Now inform each thread we are done and wait for them to be done. + ThreadInformation.stopThreads(threadList); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java @@ -28,5 +28,5 @@ * @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 + * @run main/othervm/native -agentlib:HeapMonitorTest -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 --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java @@ -28,5 +28,5 @@ * @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 + * @run main/othervm/native -agentlib:HeapMonitorTest -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 --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java @@ -28,5 +28,5 @@ * @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 + * @run main/othervm/native -agentlib:HeapMonitorTest -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 --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java @@ -30,7 +30,7 @@ * @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 + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorGCTest */ /** diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java @@ -28,7 +28,7 @@ * @summary Verifies the JVMTI SetHeapSamplingRate returns an illegal argument for negative ints. * @build Frame HeapMonitor * @compile HeapMonitorIllegalArgumentTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorIllegalArgumentTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorIllegalArgumentTest */ public class HeapMonitorIllegalArgumentTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java @@ -28,5 +28,5 @@ * @summary Verifies the JVMTI Heap Monitor using the interpreter. * @build Frame HeapMonitor * @compile HeapMonitorTest.java - * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorTest + * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorTest 10 */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java @@ -28,5 +28,5 @@ * @summary Verifies the JVMTI Heap Monitor using the interpreter. * @build Frame HeapMonitor * @compile HeapMonitorStatObjectCorrectnessTest.java - * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest + * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java @@ -28,7 +28,7 @@ * @summary Verifies the JVMTI Heap Monitor does not work without the required capability. * @build Frame HeapMonitor * @compile HeapMonitorNoCapabilityTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorNoCapabilityTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorNoCapabilityTest */ public class HeapMonitorNoCapabilityTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java @@ -28,7 +28,7 @@ * @build Frame HeapMonitor * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays. * @compile HeapMonitorStatArrayCorrectnessTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatArrayCorrectnessTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatArrayCorrectnessTest */ public class HeapMonitorStatArrayCorrectnessTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java @@ -28,7 +28,7 @@ * @build Frame HeapMonitor * @summary Verifies the JVMTI Heap Monitor sampling via object allocation. * @compile HeapMonitorStatObjectCorrectnessTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatObjectCorrectnessTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatObjectCorrectnessTest */ /** This test is checking the object allocation path works with heap sampling. */ diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java @@ -28,7 +28,7 @@ * @summary Verifies the JVMTI Heap Monitor sampling rate average * @build Frame HeapMonitor * @compile HeapMonitorStatRateTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatRateTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatRateTest */ public class HeapMonitorStatRateTest { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java @@ -28,7 +28,7 @@ * @build Frame HeapMonitor * @summary Verifies the JVMTI Heap Monitor events are only sent after enabling. * @compile HeapMonitorStatSimpleTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatSimpleTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatSimpleTest */ public class HeapMonitorStatSimpleTest { 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 @@ -30,7 +30,7 @@ * @summary Verifies the JVMTI Heap Monitor API * @build Frame HeapMonitor * @compile HeapMonitorTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorTest */ public class HeapMonitorTest { @@ -38,13 +38,18 @@ private static native boolean framesAreNotLive(Frame[] frames); public static void main(String[] args) { + if (args.length > 0) { + // For interpreter mode, have a means to reduce the default iteration count. + HeapMonitor.setAllocationIterations(Integer.parseInt(args[0])); + } + if (!HeapMonitor.eventStorageIsEmpty()) { throw new RuntimeException("Storage is not empty at test start..."); } HeapMonitor.enableSamplingEvents(); List frameList = HeapMonitor.allocate(); - frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 46)); + frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 51)); Frame[] frames = frameList.toArray(new Frame[0]); if (!HeapMonitor.obtainedEvents(frames)) { diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java @@ -28,7 +28,7 @@ * @build Frame HeapMonitor * @summary Verifies the JVMTI Heap Monitor Thread sanity * @compile HeapMonitorThreadOnOffTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadOnOffTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadOnOffTest */ import java.util.ArrayList; @@ -101,7 +101,7 @@ public void run() { int sum = 0; - for (int j = 0; j < 500; j++) { + for (int j = 0; j < 100; j++) { sum += recursiveWrapper(depth); } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java @@ -25,148 +25,31 @@ /** * @test - * @build Frame HeapMonitor + * @build Frame HeapMonitor ThreadInformation * @summary Verifies the JVMTI Heap Monitor Thread information sanity * @compile HeapMonitorThreadTest.java - * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadTest */ -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; public class HeapMonitorThreadTest { private native static boolean checkSamples(int numThreads); public static void main(String[] args) { final int numThreads = 24; - ArrayList threadList = new ArrayList<>(); + List threadList = ThreadInformation.createThreadList(numThreads); HeapMonitor.enableSamplingEvents(); - // The test creates numThreads threads, each allocate a lot. Then they inform the main thread - // they are done allocating. The test looks if all threads are available in the collected event - // information and then proceeds to tell each thread to stop running before leaving the test. - for (int i = 0 ; i < numThreads; i++) { - BlockingQueue queue = new LinkedBlockingQueue<>(); - Allocator allocator = new Allocator(i, queue); - Thread thread = new Thread(allocator, "Allocator" + i); - thread.start(); - - ThreadInformation info = new ThreadInformation(thread, queue, allocator); - threadList.add(info); - } - - // Wait until all threads have put an object in the queue. - for (ThreadInformation info : threadList) { - info.waitForAllocations(); - } + ThreadInformation.startThreads(threadList); + ThreadInformation.waitForThreads(threadList); if (!checkSamples(numThreads)) { throw new RuntimeException("Problem with checkSamples..."); } // Now inform each thread we are done and wait for them to be done. - for (ThreadInformation info : threadList) { - info.stop(); - } - } -} - -class ThreadInformation { - private Thread thread; - private BlockingQueue finishedAllocationQueue; - private Allocator allocator; - - public ThreadInformation(Thread thread, BlockingQueue finishedAllocationQueue, - Allocator allocator) { - this.thread = thread; - this.finishedAllocationQueue = finishedAllocationQueue; - this.allocator = allocator; - } - - public void waitForAllocations() { - try { - finishedAllocationQueue.take(); - } catch(InterruptedException e) { - throw new RuntimeException("Thread got interrupted..."); - } - } - - public void stop() { - try { - allocator.stopRun(); - thread.join(); - - if (!allocator.endedNormally()) { - throw new RuntimeException("Thread did not end normally..."); - } - - } catch(InterruptedException e) { - throw new RuntimeException("Thread got interrupted..."); - } + ThreadInformation.stopThreads(threadList); } } - -class Allocator implements Runnable { - private int depth; - private List currentList; - private BlockingQueue finishedAllocationQueue; - private BlockingQueue jobCanStop; - private boolean failed; - - public Allocator(int depth, BlockingQueue finishedAllocationQueue) { - jobCanStop = new LinkedBlockingQueue<>(); - this.finishedAllocationQueue = finishedAllocationQueue; - this.depth = depth; - } - - public boolean endedNormally() { - return !failed; - } - - private void helper() { - List newList = new ArrayList<>(); - // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in - // memory at least 2MB without counting the link-list itself, which adds to this. - int iterations = (1 << 21) / 24; - for (int i = 0; i < iterations; i++) { - int newTmp[] = new int[1]; - // Force it to be kept. - newList.add(newTmp); - } - - // Replace old list with new list, which provokes two things: - // Old list will get GC'd at some point. - // New list forces that this thread has some allocations still sampled. - currentList = newList; - } - - private void recursiveWrapper(int depth) { - if (depth > 0) { - recursiveWrapper(depth - 1); - } - helper(); - } - - public void stopRun() throws InterruptedException { - jobCanStop.put(new Object()); - } - - public void run() { - for (int j = 0; j < 50; j++) { - recursiveWrapper(depth); - } - - try { - // Tell the main thread we are done. - finishedAllocationQueue.put(new Object()); - - // Wait until the main thread says we can stop. - jobCanStop.take(); - } catch (InterruptedException e) { - failed = true; - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java @@ -0,0 +1,183 @@ +/* + * 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; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** API for handling heap allocating threads. */ +class ThreadInformation { + private Thread thread; + private BlockingQueue waitingQueue; + private Allocator allocator; + + public ThreadInformation(Thread thread, BlockingQueue waitingQueue, + Allocator allocator) { + this.thread = thread; + this.waitingQueue = waitingQueue; + this.allocator = allocator; + } + + public void waitForToken() { + allocator.waitForToken(); + } + + public void stop() { + try { + allocator.stopRun(); + thread.join(); + + if (!allocator.endedNormally()) { + throw new RuntimeException("Thread did not end normally..."); + } + + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } + + private void start() { + allocator.start(); + } + + public static void startThreads(List threadList) { + for (ThreadInformation info : threadList) { + info.start(); + } + } + + public static void stopThreads(List threadList) { + for (ThreadInformation info : threadList) { + info.stop(); + } + } + + public Thread getThread() { + return thread; + } + + public static void waitForThreads(List threadList) { + // Wait until all threads have put an object in the queue. + for (ThreadInformation info : threadList) { + info.waitForToken(); + } + } + + public static List createThreadList(int numThreads) { + List threadList = new ArrayList<>(); + for (int i = 0 ; i < numThreads; i++) { + BlockingQueue queue = new LinkedBlockingQueue<>(); + Allocator allocator = new Allocator(i, queue); + Thread thread = new Thread(allocator, "Allocator" + i); + + ThreadInformation info = new ThreadInformation(thread, queue, allocator); + threadList.add(info); + thread.start(); + } + return threadList; + } +} + +class Allocator implements Runnable { + private int depth; + private List currentList; + private BlockingQueue waitingQueue; + private BlockingQueue jobCanStop; + private boolean failed; + + public Allocator(int depth, BlockingQueue waitingQueue) { + jobCanStop = new LinkedBlockingQueue<>(); + this.waitingQueue = waitingQueue; + this.depth = depth; + } + + public boolean endedNormally() { + return !failed; + } + + private void helper() { + List newList = new ArrayList<>(); + // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in + // memory at least 2MB without counting the link-list itself, which adds to this. + int iterations = (1 << 21) / 24; + for (int i = 0; i < iterations; i++) { + int newTmp[] = new int[1]; + // Force it to be kept. + newList.add(newTmp); + } + + // Replace old list with new list, which provokes two things: + // Old list will get GC'd at some point. + // New list forces that this thread has some allocations still sampled. + currentList = newList; + } + + private void recursiveWrapper(int depth) { + if (depth > 0) { + recursiveWrapper(depth - 1); + } + helper(); + } + + public void stopRun() throws InterruptedException { + jobCanStop.put(new Object()); + } + + public void run() { + // Wait till we are told to really start. + waitForToken(); + + for (int j = 0; j < 50; j++) { + recursiveWrapper(depth); + } + + try { + // Tell the main thread we are done. + waitingQueue.put(new Object()); + + // Wait until the main thread says we can stop. + jobCanStop.take(); + } catch (InterruptedException e) { + failed = true; + } + } + + public void waitForToken() { + try { + waitingQueue.take(); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } + + public void start() { + try { + waitingQueue.put(new Object()); + } catch(InterruptedException e) { + throw new RuntimeException("Thread got interrupted..."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c @@ -50,6 +50,7 @@ #define MAX_TRACES 400 static jvmtiEnv *jvmti = NULL; +static jvmtiEnv *second_jvmti = NULL; typedef struct _ObjectTrace{ jweak object; @@ -522,6 +523,28 @@ "Set event notifications"); } +static int enable_notifications_for_two_threads(jthread first, jthread second) { + if (check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), + "Set event notifications")) { + return 1; + } + + if (check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first), + "Set event notifications")) { + return 1; + } + + if (check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second), + "Set event notifications")) { + return 1; + } + + return 0; +} + static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { jint res; @@ -529,7 +552,15 @@ 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"); + fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + + // Get second jvmti environment. + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &second_jvmti), + JVMTI_VERSION_9); + if (res != JNI_OK || second_jvmti == NULL) { + fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n"); return JNI_ERR; } @@ -544,16 +575,16 @@ jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); - // Get line numbers, sample heap, sample events, and filename for the test. + // Get line numbers, sample events, filename, and gc events for the tests. caps.can_get_line_numbers = 1; caps.can_generate_sampled_alloc_events = 1; caps.can_get_source_file_name = 1; caps.can_generate_garbage_collection_events = 1; - if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){ + if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")) { return JNI_ERR; } - if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, + if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(jvmtiEventCallbacks)), " Set Event Callbacks")) { return JNI_ERR; @@ -582,6 +613,14 @@ } JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env, + jclass cls, + jthread first, + jthread second) { + enable_notifications_for_two_threads(first, second); +} + +JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), @@ -743,6 +782,85 @@ return thread_data.num_threads == num_threads; } + +static void release_data(JNIEnv* env, jstring first, const char* first_str, + jstring second, const char* second_str, + jthread* threads) { + (*env)->ReleaseStringUTFChars(env, first, first_str); + (*env)->ReleaseStringUTFChars(env, second, second_str); + free(threads); +} + +static int check_threads(JNIEnv* env, jstring first, jstring second, + jthread* threads) { + const char* first_str = (*env)->GetStringUTFChars(env, first, 0); + const char* second_str = (*env)->GetStringUTFChars(env, second, 0); + + jthread first_thread = threads[0]; + jthread second_thread = threads[1]; + + jvmtiThreadInfo first_info; + jvmtiThreadInfo second_info; + (*jvmti)->GetThreadInfo(jvmti, first_thread, &first_info); + (*jvmti)->GetThreadInfo(jvmti, second_thread, &second_info); + + if (strcmp(first_str, first_info.name) && + strcmp(first_str, second_info.name)) { + release_data(env, first, first_str, second, second_str, threads); + return FALSE; + } + + if (strcmp(second_str, first_info.name) && + strcmp(second_str, second_info.name)) { + release_data(env, first, first_str, second, second_str, threads); + return FALSE; + } + + release_data(env, first, first_str, second, second_str, threads); + return TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorEventsForTwoThreadsTest_checkSamples(JNIEnv* env, + jclass cls, + jstring first, + jstring second) { + pthread_mutex_lock(&global_event_storage.storage_mutex); + jint trace_counter; + + const int expected_num_threads = 2; + ThreadsFound thread_data; + thread_data.num_threads = 0; + thread_data.threads = malloc(sizeof(jthread) * expected_num_threads); + memset(thread_data.threads, 0, sizeof(jthread) * expected_num_threads); + + find_threads_in_array(&thread_data, global_event_storage.live_objects, + global_event_storage.live_object_count); + + pthread_mutex_unlock(&global_event_storage.storage_mutex); + fprintf(stderr, "Obtained %d - %d\n", thread_data.num_threads, + expected_num_threads); + + if (thread_data.num_threads != expected_num_threads) { + free(thread_data.threads); + return FALSE; + } + + return check_threads(env, first, second, thread_data.threads); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorOnlyOneAgent_enablingSamplingInSecondaryAgent( + JNIEnv* env, jclass cls) { + // Because sampling allocations is in solo mode only, trying to grab the + // capability with the second agent should fail. + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_generate_sampled_alloc_events = 1; + return (*second_jvmti)->AddCapabilities(second_jvmti, &caps) + == JVMTI_ERROR_NONE; +} + #ifdef __cplusplus } #endif # HG changeset patch # User jcbeyler # Date 1522951679 25200 # Thu Apr 05 11:07:59 2018 -0700 # Node ID b942aed1891de99d78ea4e3ed891e1269e1814e7 # Parent 1fba53555a7b286ee1c8e34ce229298e346488c8 [mq]: heap11 diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -329,7 +329,6 @@ JRT_ENTRY(void, Runtime1::new_instance(JavaThread* thread, Klass* klass)) NOT_PRODUCT(_new_instance_slowcase_cnt++;) - JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "not a class"); Handle holder(THREAD, klass->klass_holder()); // keep the klass alive @@ -345,7 +344,6 @@ JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* thread, Klass* klass, jint length)) NOT_PRODUCT(_new_type_array_slowcase_cnt++;) - JvmtiSampledObjectAllocEventCollector collector; // Note: no handle for klass needed since they are not used // anymore after new_typeArray() and no GC can happen before. // (This may have to change if this code changes!) @@ -364,7 +362,6 @@ JRT_ENTRY(void, Runtime1::new_object_array(JavaThread* thread, Klass* array_klass, jint length)) NOT_PRODUCT(_new_object_array_slowcase_cnt++;) - JvmtiSampledObjectAllocEventCollector collector; // Note: no handle for klass needed since they are not used // anymore after new_objArray() and no GC can happen before. @@ -384,7 +381,6 @@ JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims)) NOT_PRODUCT(_new_multi_array_slowcase_cnt++;) - JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "not a class"); assert(rank >= 1, "rank must be nonzero"); 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 @@ -217,7 +217,6 @@ } oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -228,7 +227,6 @@ } oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -242,7 +240,6 @@ int size, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); @@ -256,7 +253,6 @@ int size, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); 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 @@ -850,7 +850,6 @@ IRT_END void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode) { - JvmtiSampledObjectAllocEventCollector collector; Thread* THREAD = thread; LastFrameAccessor last_frame(thread); // extract receiver from the outgoing argument list if necessary diff --git a/src/hotspot/share/memory/oopFactory.cpp b/src/hotspot/share/memory/oopFactory.cpp --- a/src/hotspot/share/memory/oopFactory.cpp +++ b/src/hotspot/share/memory/oopFactory.cpp @@ -41,7 +41,6 @@ typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) { int length = utf8_str == NULL ? 0 : UTF8::unicode_length(utf8_str); - JvmtiSampledObjectAllocEventCollector collector; typeArrayOop result = new_charArray(length, CHECK_NULL); if (length > 0) { UTF8::convert_to_unicode(utf8_str, result->char_at_addr(0), length); @@ -50,12 +49,10 @@ } typeArrayOop oopFactory::new_tenured_charArray(int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; return TypeArrayKlass::cast(Universe::charArrayKlassObj())->allocate(length, THREAD); } typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); typeArrayOop result = type_asArrayKlass->allocate(length, THREAD); @@ -67,7 +64,6 @@ // int or long depending on pointer size. Only stack trace elements in Throwable use // this. They cast Symbol* into this type. typeArrayOop oopFactory::new_symbolArray(int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; BasicType type = LP64_ONLY(T_LONG) NOT_LP64(T_INT); Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); @@ -76,7 +72,6 @@ } typeArrayOop oopFactory::new_typeArray_nozero(BasicType type, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; Klass* type_asKlassOop = Universe::typeArrayKlassObj(type); TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop); typeArrayOop result = type_asArrayKlass->allocate_common(length, false, THREAD); @@ -85,7 +80,6 @@ objArrayOop oopFactory::new_objArray(Klass* klass, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; assert(klass->is_klass(), "must be instance class"); if (klass->is_array_klass()) { return ArrayKlass::cast(klass)->allocate_arrayArray(1, length, THREAD); @@ -95,13 +89,11 @@ } objArrayHandle oopFactory::new_objArray_handle(Klass* klass, int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; objArrayOop obj = new_objArray(klass, length, CHECK_(objArrayHandle())); return objArrayHandle(THREAD, obj); } typeArrayHandle oopFactory::new_byteArray_handle(int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; typeArrayOop obj = new_byteArray(length, CHECK_(typeArrayHandle())); return typeArrayHandle(THREAD, obj); } diff --git a/src/hotspot/share/memory/oopFactory.hpp b/src/hotspot/share/memory/oopFactory.hpp --- a/src/hotspot/share/memory/oopFactory.hpp +++ b/src/hotspot/share/memory/oopFactory.hpp @@ -42,38 +42,14 @@ class oopFactory: AllStatic { public: // Basic type leaf array allocation - static typeArrayOop new_boolArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD); - } - static typeArrayOop new_charArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD); - } - static typeArrayOop new_singleArray(int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD); - } - static typeArrayOop new_doubleArray(int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD); - } - static typeArrayOop new_byteArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD); - } - static typeArrayOop new_shortArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD); - } - static typeArrayOop new_intArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD); - } - static typeArrayOop new_longArray (int length, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; - return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD); - } + static typeArrayOop new_boolArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_charArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_singleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD); } + static typeArrayOop new_doubleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD); } + static typeArrayOop new_byteArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_shortArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_intArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD); } + static typeArrayOop new_longArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD); } // create java.lang.Object[] static objArrayOop new_objectArray(int length, TRAPS) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1002,7 +1002,6 @@ } instanceOop InstanceKlass::allocate_instance(TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; bool has_finalizer_flag = has_finalizer(); // Query before possible GC int size = size_helper(); // Query before forming handle. diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp --- a/src/hotspot/share/oops/objArrayKlass.cpp +++ b/src/hotspot/share/oops/objArrayKlass.cpp @@ -58,7 +58,6 @@ Klass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_data, int n, Klass* element_klass, TRAPS) { - JvmtiSampledObjectAllocEventCollector collector; // Eagerly allocate the direct array supertype. Klass* super_klass = NULL; if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) { 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 @@ -279,6 +279,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; @@ -323,12 +324,12 @@ // 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 assert(check_compiled_frame(thread), "incorrect caller"); assert(elem_type->is_klass(), "not a class"); + JvmtiSampledObjectAllocEventCollector collector; jint dims[2]; dims[0] = len1; dims[1] = len2; @@ -340,7 +341,6 @@ // 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 @@ -358,12 +358,12 @@ // 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 assert(check_compiled_frame(thread), "incorrect caller"); assert(elem_type->is_klass(), "not a class"); + JvmtiSampledObjectAllocEventCollector collector; jint dims[4]; dims[0] = len1; dims[1] = len2; @@ -377,12 +377,12 @@ // 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 assert(check_compiled_frame(thread), "incorrect caller"); assert(elem_type->is_klass(), "not a class"); + JvmtiSampledObjectAllocEventCollector collector; jint dims[5]; dims[0] = len1; dims[1] = len2; @@ -399,6 +399,7 @@ assert(check_compiled_frame(thread), "incorrect caller"); assert(elem_type->is_klass(), "not a class"); assert(oop(dims)->is_typeArray(), "not an array"); + JvmtiSampledObjectAllocEventCollector collector; ResourceMark rm; jint len = dims->length(); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -631,7 +631,6 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) JVMWrapper("JVM_Clone"); - JvmtiSampledObjectAllocEventCollector collector; Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); Klass* klass = obj->klass(); JvmtiVMObjectAllocEventCollector oam; @@ -3315,7 +3314,6 @@ JVM_ENTRY(jobject, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length)) JVMWrapper("JVM_NewArray"); - JvmtiSampledObjectAllocEventCollector collector; JvmtiVMObjectAllocEventCollector oam; oop element_mirror = JNIHandles::resolve(eltClass); oop result = Reflection::reflect_new_array(element_mirror, length, CHECK_NULL); @@ -3325,7 +3323,6 @@ JVM_ENTRY(jobject, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim)) JVMWrapper("JVM_NewMultiArray"); - JvmtiSampledObjectAllocEventCollector collector; JvmtiVMObjectAllocEventCollector oam; arrayOop dim_array = check_array(env, dim, true, CHECK_NULL); oop element_mirror = JNIHandles::resolve(eltClass); @@ -3490,9 +3487,6 @@ if (JvmtiExport::should_post_vm_object_alloc()) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } - if (JvmtiExport::should_post_sampled_object_alloc()) { - JvmtiExport::post_sampled_object_alloc(JavaThread::current(), result); - } return res; JVM_END 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 @@ -13563,7 +13563,7 @@ + id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11"> Sent when an allocated object is sampled via the Heap Sampling Monitoring system . 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 @@ -549,7 +549,6 @@ JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled); } else { // We have a specified event_thread. - JavaThread* java_thread = NULL; ThreadsListHandle tlh; jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL); @@ -577,14 +576,6 @@ if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { record_class_file_load_hook_enabled(); } - - if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) { - if (enabled) { - ThreadHeapSampler::enable(); - } else { - ThreadHeapSampler::disable(); - } - } JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled); } @@ -3653,7 +3644,7 @@ if (sampling_rate < 0) { return JVMTI_ERROR_ILLEGAL_ARGUMENT; } - ThreadHeapSampler::set_tlab_heap_sampling(sampling_rate); + ThreadHeapSampler::set_sampling_rate(sampling_rate); return JVMTI_ERROR_NONE; } /* end SetHeapSamplingRate */ 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 @@ -96,8 +96,7 @@ static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT | FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT; static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS | - BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT | - SAMPLED_OBJECT_ALLOC_BIT; + BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT; static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT; static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT | 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 @@ -2539,30 +2539,22 @@ EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, ("[%s] Trg sampled object alloc triggered", JvmtiTrace::safe_get_thread_name(thread))); - if (object == NULL) { return; } - JvmtiThreadState *state = thread->jvmti_thread_state(); - if (state == NULL) { - return; - } - HandleMark hm(thread); Handle h(thread, object); - - JvmtiEnvThreadStateIterator it(state); - for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { - if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) { + 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()); - JvmtiEnv *env = ets->get_env(); - JvmtiThreadEventTransition jet(thread); + JvmtiJavaThreadEventTransition jet(thread); jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; if (callback != NULL) { (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(), @@ -2812,7 +2804,7 @@ oop obj = _allocated->at(i); _post_callback(JavaThread::current(), obj); } - delete _allocated; + delete _allocated, _allocated = NULL; } } 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 @@ -126,7 +126,7 @@ Monitor* PeriodicTask_lock = NULL; Monitor* RedefineClasses_lock = NULL; -Monitor* ThreadHeapSampler_lock = NULL; +Mutex* ThreadHeapSampler_lock = NULL; #if INCLUDE_TRACE Mutex* JfrStacktrace_lock = NULL; @@ -289,7 +289,7 @@ 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_sometimes); + def(ThreadHeapSampler_lock , PaddedMutex, 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 @@ -124,7 +124,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. +extern Mutex* 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 @@ -88,12 +88,12 @@ // 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) * (_sampling_rate)) + 1); + (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_rate())) + 1); _bytes_until_sample = rate; } void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) { - if (_sampling_rate == 1) { + if (get_sampling_rate() == 1) { _bytes_until_sample = 1; return; } @@ -123,7 +123,7 @@ } void ThreadHeapSampler::init_log_table() { - MutexLocker mu(ThreadHeapSampler_lock); + MutexLockerEx mu(ThreadHeapSampler_lock); if (log_table_initialized) { return; @@ -137,11 +137,16 @@ log_table_initialized = true; } -void ThreadHeapSampler::set_tlab_heap_sampling(int sampling_rate) { - MutexLocker mu(ThreadHeapSampler_lock); - _sampling_rate = sampling_rate; +void ThreadHeapSampler::enable() { + // Done here to be done when things have settled. This adds a mutex lock but + // presumably, users won't be enabling and disabling all the time. + init_log_table(); + OrderAccess::release_store(&_enabled, 1); } + +// Methods used in assertion mode to check if a collector is present or not at +// the moment of TLAB sampling, ie a slow allocation path. bool ThreadHeapSampler::sampling_collector_present() const { return _collectors_present > 0; } @@ -155,10 +160,3 @@ _collectors_present++; return true; } - -void ThreadHeapSampler::enable() { - // Done here to be done when things have settled. This adds a mutex lock but - // presumably, users won't be enabling and disabling all the time. - init_log_table(); - OrderAccess::release_store(&_enabled, 1); -} 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 @@ -26,6 +26,7 @@ #define RUNTIME_THREADHEAPSAMPLER_HPP #include "memory/allocation.hpp" +#include "runtime/orderAccess.inline.hpp" class ThreadHeapSampler { private: @@ -65,7 +66,13 @@ static int enabled() { return OrderAccess::load_acquire(&_enabled); } static void enable(); static void disable() { OrderAccess::release_store(&_enabled, 0); } - static void set_tlab_heap_sampling(int sampling_rate); + + static void set_sampling_rate(int sampling_rate) { + OrderAccess::release_store(&_sampling_rate, sampling_rate); + } + static int get_sampling_rate() { + return OrderAccess::load_acquire(&_sampling_rate); + } bool sampling_collector_present() const; bool remove_sampling_collector(); 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 @@ -44,7 +44,7 @@ /** Set a specific sampling rate, 0 samples every allocation. */ public native static void setSamplingRate(int rate); public native static void enableSamplingEvents(); - public native static void enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread); + public native static boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread); public native static void disableSamplingEvents(); /** diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java @@ -26,7 +26,7 @@ /** * @test * @build Frame HeapMonitor ThreadInformation - * @summary Verifies the JVMTI Heap Monitor for only two threads + * @summary Ensures the JVMTI Heap Monitor is not thread enable (test to change when it becomes so) * @compile HeapMonitorEventsForTwoThreadsTest.java * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest */ @@ -34,25 +34,14 @@ import java.util.List; public class HeapMonitorEventsForTwoThreadsTest { - private native static boolean checkSamples(String first, String second); - public static void main(String[] args) { final int numThreads = 24; List threadList = ThreadInformation.createThreadList(numThreads); Thread firstThread = threadList.get(0).getThread(); Thread secondThread = threadList.get(1).getThread(); - // HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread); - - ThreadInformation.startThreads(threadList); - ThreadInformation.waitForThreads(threadList); - - // Should only see events for the two threads. - if (!checkSamples(firstThread.getName(), secondThread.getName())) { - throw new RuntimeException("Problem with checkSamples..."); + if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) { + throw new RuntimeException("Sampling event is thread enabled, that is unexpected."); } - - // Now inform each thread we are done and wait for them to be done. - ThreadInformation.stopThreads(threadList); } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java @@ -0,0 +1,46 @@ +/* + * 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 ThreadInformation + * @summary Verifies the JVMTI Heap Monitor is only availabe for a single agent. + * @compile HeapMonitorOnlyOneAgent.java + * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorOnlyOneAgent + */ + +import java.util.List; + +public class HeapMonitorOnlyOneAgent { + private native static boolean enablingSamplingInSecondaryAgent(); + + public static void main(String[] args) { + HeapMonitor.enableSamplingEvents(); + + if (enablingSamplingInSecondaryAgent() == true) { + throw new RuntimeException("Enabling sampling in second agent did not fail..."); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c @@ -527,22 +527,23 @@ if (check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), "Set event notifications")) { - return 1; + return 0; } if (check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first), "Set event notifications")) { - return 1; + return 0; } + // Second thread should fail. if (check_error((*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second), "Set event notifications")) { - return 1; + return 0; } - return 0; + return 1; } static @@ -612,12 +613,12 @@ enable_notifications(); } -JNIEXPORT void JNICALL +JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env, jclass cls, jthread first, jthread second) { - enable_notifications_for_two_threads(first, second); + return enable_notifications_for_two_threads(first, second); } JNIEXPORT void JNICALL