# HG changeset patch
# User jmanson
# Date 1528155811 25200
# Mon Jun 04 16:43:31 2018 -0700
# Node ID f59e09d33b1fa64123dac3a4bf7f69db91434f0f
# Parent 2bea53d7a27bfa37b15907c9b9ef24d139542b28
JEP 331
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
@@ -6153,6 +6153,9 @@
libIsModifiableModuleTest.c
+
+ libHeapMonitorTest.c
+ libMAAClassFileLoadHook.c
@@ -40154,6 +40157,11 @@
tool="0"
flavor2="0">
+
+ 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.
+ obj = thread->tlab().allocate_sampled_object(size);
+
+ if (obj != NULL) {
+ return obj;
+ }
+ }
// Retain tlab and allocate object in shared space if
// the amount free in the tlab is too large to discard.
@@ -388,7 +408,7 @@
// between minimal and new_tlab_size is accepted.
size_t actual_tlab_size = 0;
size_t min_tlab_size = ThreadLocalAllocBuffer::compute_min_size(size);
- HeapWord* obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size);
+ obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size);
if (obj == NULL) {
assert(actual_tlab_size == 0, "Allocation failed, but actual size was updated. min: " SIZE_FORMAT ", desired: " SIZE_FORMAT ", actual: " SIZE_FORMAT,
min_tlab_size, new_tlab_size, actual_tlab_size);
@@ -412,6 +432,14 @@
Copy::fill_to_words(obj + hdr_size, actual_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, actual_tlab_size);
return obj;
}
diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -183,6 +183,18 @@
virtual void trace_heap(GCWhen::Type when, const GCTracer* tracer);
+ // Internal allocation methods.
+ inline static HeapWord* common_allocate_memory(Klass* klass, int size,
+ void (*post_setup)(Klass*, HeapWord*, int),
+ int size_for_post, bool init_memory,
+ TRAPS);
+
+ // Internal allocation method for common obj/class/array allocations.
+ inline static HeapWord* allocate_memory(Klass* klass, int size,
+ void (*post_setup)(Klass*, HeapWord*, int),
+ int size_for_post, bool init_memory,
+ TRAPS);
+
// Verification functions
virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size)
PRODUCT_RETURN;
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
@@ -34,6 +34,7 @@
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/sharedRuntime.hpp"
+#include "runtime/handles.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "services/lowMemoryDetector.hpp"
#include "utilities/align.hpp"
@@ -154,10 +155,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);
+ size_t 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;
}
@@ -212,12 +217,58 @@
Copy::fill_to_aligned_words(obj + hs, size - hs);
}
+HeapWord* CollectedHeap::common_allocate_memory(Klass* klass, int size,
+ void (*post_setup)(Klass*, HeapWord*, int),
+ int size_for_post, bool init_memory,
+ TRAPS) {
+ HeapWord* obj;
+ if (init_memory) {
+ obj = common_mem_allocate_init(klass, size, CHECK_NULL);
+ } else {
+ obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
+ }
+ post_setup(klass, obj, size_for_post);
+ return obj;
+}
+
+HeapWord* CollectedHeap::allocate_memory(Klass* klass, int size,
+ void (*post_setup)(Klass*, HeapWord*, int),
+ int size_for_post, bool init_memory,
+ TRAPS) {
+ HeapWord* obj;
+
+ assert(JavaThread::current()->heap_sampler().add_sampling_collector(),
+ "Should never return false.");
+
+ if (JvmtiExport::should_post_sampled_object_alloc()) {
+ HandleMark hm(THREAD);
+ Handle obj_h;
+ {
+ JvmtiSampledObjectAllocEventCollector collector;
+ obj = common_allocate_memory(klass, size, post_setup, size_for_post,
+ init_memory, CHECK_NULL);
+ // If we want to be sampling, protect the allocated object with a Handle
+ // before doing the callback. The callback is done in the destructor of
+ // the JvmtiSampledObjectAllocEventCollector.
+ obj_h = Handle(THREAD, (oop) obj);
+ }
+ obj = (HeapWord*) obj_h();
+ } else {
+ obj = common_allocate_memory(klass, size, post_setup, size_for_post,
+ init_memory, CHECK_NULL);
+ }
+
+ assert(JavaThread::current()->heap_sampler().remove_sampling_collector(),
+ "Should never return false.");
+ return obj;
+}
+
oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) {
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");
- HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
- post_allocation_setup_obj(klass, obj, size);
+ HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_obj,
+ size, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@@ -226,8 +277,8 @@
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");
- HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
- post_allocation_setup_class(klass, obj, size); // set oop_size
+ HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_class,
+ size, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@@ -239,8 +290,8 @@
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");
- HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
- post_allocation_setup_array(klass, obj, length);
+ HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array,
+ length, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@@ -252,9 +303,9 @@
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");
- HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
- ((oop)obj)->set_klass_gap(0);
- post_allocation_setup_array(klass, obj, length);
+
+ HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array,
+ length, false, CHECK_NULL);
#ifndef PRODUCT
const size_t hs = oopDesc::header_size()+1;
Universe::heap()->check_for_non_bad_heap_word_value(obj+hs, size-hs);
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
@@ -45,6 +45,14 @@
make_parsable(true); // also retire the TLAB
}
+size_t ThreadLocalAllocBuffer::remaining() {
+ if (end() == NULL) {
+ return 0;
+ }
+
+ return pointer_delta(hard_end(), top());
+}
+
void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() {
global_stats()->initialize();
@@ -121,10 +129,12 @@
set_top(NULL);
set_pf_top(NULL);
set_end(NULL);
+ set_allocation_end(NULL);
}
}
assert(!(retire || ZeroTLAB) ||
- (start() == NULL && end() == NULL && top() == NULL),
+ (start() == NULL && end() == NULL && top() == NULL &&
+ _allocation_end == NULL),
"TLAB must be reset");
}
@@ -172,8 +182,13 @@
_allocated_size += new_size;
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());
}
@@ -185,6 +200,7 @@
set_top(top);
set_pf_top(top);
set_end(end);
+ set_allocation_end(end);
invariants();
}
@@ -306,12 +322,45 @@
guarantee(p == top(), "end of last object must match end of space");
}
+void ThreadLocalAllocBuffer::set_sample_end() {
+ size_t heap_words_remaining = pointer_delta(_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_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() {
+ _end = _allocation_end;
+}
+
+HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) {
+ set_back_allocation_end();
+ HeapWord* result = allocate(size);
+
+ if (result) {
+ myThread()->heap_sampler().check_for_sampling(result, size * HeapWordSize, _bytes_since_last_sample_point);
+ set_sample_end();
+ }
+
+ return result;
+}
+
+HeapWord* ThreadLocalAllocBuffer::hard_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,12 @@
// 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 end and allocation_end
+// fields.
+// allocation_end contains the real end of the tlab allocation,
+// whereas end can be set to an arbitrary spot in the tlab to
+// trip the return and sample the allocation.
class ThreadLocalAllocBuffer: public CHeapObj {
friend class VMStructs;
friend class JVMCIVMStructs;
@@ -44,10 +50,13 @@
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* _end; // allocation end (can be the sampling end point or _allocation_end)
+ HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve)
+
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
@@ -67,6 +76,7 @@
void set_start(HeapWord* start) { _start = start; }
void set_end(HeapWord* end) { _end = end; }
+ void set_allocation_end(HeapWord* ptr) { _allocation_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; }
@@ -77,7 +87,7 @@
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();
bool is_last_allocation(HeapWord* obj, size_t size) { return pointer_delta(top(), obj) == size; }
@@ -118,8 +128,8 @@
HeapWord* start() const { return _start; }
HeapWord* end() const { return _end; }
- HeapWord* hard_end() const { return _end + alignment_reserve(); }
HeapWord* top() const { return _top; }
+ HeapWord* hard_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()); }
@@ -127,9 +137,11 @@
size_t free() const { return pointer_delta(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);
// Undo last allocation.
inline bool undo_allocate(HeapWord* obj, size_t size);
@@ -171,6 +183,9 @@
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; }
template void addresses_do(T f) {
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,14 @@
See .
+
+
+ Can generate sampled allocation events.
+ If this capability is enabled then the heap sampling method
+ can be
+ called and events can be generated.
+
+
@@ -11531,6 +11539,47 @@
+
+
+ Set Heap Sampling Rate
+
+ Generate a event when objects are allocated.
+ Each thread keeps a counter of bytes allocated. The event will only be generated
+ when that counter exceeds an average of
+ since the last sample.
+
+ Setting to 0 will cause an event to be
+ generated by each allocation supported by the system.
+
+ new
+
+
+
+
+
+
+
+ The sampling rate in bytes. The sampler uses a statistical approach to
+ generate an event, on average, once for every bytes of
+ memory allocated by a given thread.
+
+ Passing 0 as a sampling rate generates a sample for every allocation.
+
+ Note: The overhead of this feature is directly correlated with the sampling rate.
+ A high sampling rate, such as 1024 bytes, will incur a high overhead.
+ A lower rate, such as 1024KB, will have a much lower overhead. Sampling should only
+ be used with an understanding that it may impact performance.
+
+
+
+
+
+ is less than zero.
+
+
+
+
+
@@ -13495,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.
@@ -13513,8 +13562,75 @@
+
+
+ Sent when an allocated object is sampled.
+ By default, the sampling rate is a geometric variable with a 512KB mean.
+ Each thread tracks how many bytes it has allocated since it sent the last event.
+ When the number of bytes exceeds the sampling rate, it will send another event.
+ This implies that, on average, one object will be sampled every time a thread has
+ allocated 512KB bytes since the last sample.
+
+ Note that this is a geometric variable: it will not sample every 512KB precisely.
+ The goal of this is to ensure high quality sampling even if allocation is
+ happening in a fixed pattern (i.e., the same set of objects are being allocated
+ every 512KB).
+
+ If another sampling rate is required, the user can call
+ with a strictly positive integer value, representing
+ the new sampling rate.
+
+ This event is sent once the sampled allocation has been performed. It provides the object, stack trace
+ of the allocation, the thread allocating, the size of allocation, and the object's class.
+
+ A typical use case of this system is to determine where heap allocations originate.
+ In conjunction with weak references and the function
+ , a user can track which objects were allocated from which
+ stack trace, and which are still live during the execution of the program.
+
+ 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 +13650,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"
@@ -537,10 +538,17 @@
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.
-
JavaThread* java_thread = NULL;
ThreadsListHandle tlh;
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
@@ -3631,6 +3639,15 @@
return JVMTI_ERROR_NONE;
} /* end GetAvailableProcessors */
+jvmtiError
+JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) {
+ if (sampling_rate < 0) {
+ return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+ }
+ ThreadHeapSampler::set_sampling_rate(sampling_rate);
+ return JVMTI_ERROR_NONE;
+} /* end SetHeapSamplingRate */
+
//
// 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));
@@ -620,6 +621,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
@@ -1028,12 +1028,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;
};
@@ -1198,6 +1198,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;
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2280,7 +2281,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();
@@ -2295,6 +2296,27 @@
}
}
+// Collect all the sampled allocated objects.
+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,
@@ -2484,8 +2506,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) {
@@ -2500,7 +2521,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) {
@@ -2511,6 +2532,34 @@
}
}
+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) {
@@ -2536,7 +2585,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) {
@@ -2669,12 +2718,28 @@
} 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()) {
+ JvmtiSampledObjectAllocEventCollector *prev = state->get_sampled_object_alloc_event_collector();
+
+ if (prev) {
+ // JvmtiSampledObjectAllocEventCollector wants only one active collector
+ // enabled. This allows to have a collector detect a user code requiring
+ // a sample in the callback.
+ return;
+ }
+ state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*) this);
}
+
+ _unset_jvmti_thread_state = true;
}
// Unset current event collection in this thread and reset it with previous
// collector.
void JvmtiEventCollector::unset_jvmti_thread_state() {
+ if (!_unset_jvmti_thread_state) {
+ return;
+ }
+
JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state();
if (state != NULL) {
// restore the previous event collector (if any)
@@ -2685,14 +2750,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.
}
}
}
@@ -2730,31 +2800,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() {
- if (_allocated != NULL) {
+void JvmtiObjectAllocEventCollector::generate_call_for_allocated() {
+ if (_allocated) {
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;
+ delete _allocated, _allocated = NULL;
}
- 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);
}
@@ -2762,9 +2826,9 @@
}
// GC support.
-void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) {
- if (_allocated != NULL) {
- for(int i=_allocated->length() - 1; i >= 0; i--) {
+void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) {
+ if (_allocated) {
+ for(int i = _allocated->length() - 1; i >= 0; i--) {
if (_allocated->at(i) != NULL) {
f->do_oop(_allocated->adr_at(i));
}
@@ -2772,7 +2836,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;
@@ -2781,11 +2845,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();
}
}
}
@@ -2820,6 +2890,63 @@
}
};
+// Setup current thread to record vm allocated objects.
+JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() {
+ if (JvmtiExport::should_post_vm_object_alloc()) {
+ _enable = true;
+ setup_jvmti_thread_state();
+ _post_callback = JvmtiExport::post_vm_object_alloc;
+ }
+}
+
+JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() {
+ if (_enable) {
+ generate_call_for_allocated();
+ }
+ unset_jvmti_thread_state();
+}
+
+bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() {
+ Thread* thread = Thread::current();
+ // Really only sample allocations if this is a JavaThread and not the compiler
+ // thread.
+ if (!thread->is_Java_thread() || thread->is_Compiler_thread()) {
+ return false;
+ }
+
+ if (Compile_lock->owner() == thread ||
+ MultiArray_lock->owner() == thread) {
+ return false;
+ }
+ return true;
+}
+
+// Setup current thread to record sampled allocated objects.
+JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() {
+ if (JvmtiExport::should_post_sampled_object_alloc()) {
+ if (!object_alloc_is_safe_to_sample()) {
+ return;
+ }
+
+ _enable = true;
+ setup_jvmti_thread_state();
+ _post_callback = JvmtiExport::post_sampled_object_alloc;
+ }
+}
+
+JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() {
+ if (!_enable) {
+ return;
+ }
+
+ generate_call_for_allocated();
+ unset_jvmti_thread_state();
+
+ // Unset the sampling collector as present in assertion mode only.
+ assert(Thread::current()->is_Java_thread(),
+ "Should always be in a Java thread");
+}
+
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);
@@ -363,6 +364,18 @@
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,
@@ -422,12 +435,16 @@
class JvmtiEventCollector : public StackObj {
private:
JvmtiEventCollector* _prev; // Save previous one to support nested event collector.
+ bool _unset_jvmti_thread_state;
public:
- void setup_jvmti_thread_state(); // Set this collector in current thread.
+ JvmtiEventCollector() : _prev(NULL), _unset_jvmti_thread_state(false) {}
+
+ void setup_jvmti_thread_state(); // Set this collector in current thread, returns if success.
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; }
};
@@ -462,42 +479,67 @@
};
-// 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:
- 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
+class JvmtiObjectAllocEventCollector : public JvmtiEventCollector {
+ protected:
+ GrowableArray* _allocated; // field to record collected allocated object oop.
+ bool _enable; // This flag is enabled in constructor if set up in the thread state
+ // 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.
//GC support
void oops_do(OopClosure* f);
friend class JvmtiExport;
- // Record vm allocated object oop.
+
+ // Record allocated object oop.
inline void record_allocation(oop obj);
//GC support
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; }
+ static bool object_alloc_is_safe_to_sample();
+};
// 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
@@ -130,6 +130,7 @@
memset(&jc, 0, sizeof(jc));
jc.can_suspend = 1;
+ jc.can_generate_sampled_object_alloc_events = 1;
return jc;
}
@@ -410,6 +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_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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, 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
@@ -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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018 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
@@ -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/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -129,6 +129,8 @@
Monitor* PeriodicTask_lock = NULL;
Monitor* RedefineClasses_lock = NULL;
+Mutex* ThreadHeapSampler_lock = NULL;
+
#if INCLUDE_JFR
Mutex* JfrStacktrace_lock = NULL;
Monitor* JfrMsg_lock = NULL;
@@ -291,6 +293,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 , 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
@@ -128,6 +128,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 Mutex* ThreadHeapSampler_lock; // protects the static data for initialization.
#if INCLUDE_JFR
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
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
@@ -42,6 +42,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 "utilities/align.hpp"
@@ -338,6 +339,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.
JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr
@@ -517,6 +519,8 @@
void incr_allocated_bytes(jlong size) { _allocated_bytes += size; }
inline jlong cooked_allocated_bytes();
+ ThreadHeapSampler& heap_sampler() { return _heap_sampler; }
+
JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;)
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
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,188 @@
+/*
+ * 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 "precompiled.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/orderAccess.inline.hpp"
+#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;
+ assert(sizeof(d) == sizeof(x),
+ "double and uint64_t do not have the same size");
+ x = *reinterpret_cast(&d);
+ const uint32_t x_high = x >> 32;
+ assert(FastLogNumBits <= 20, "FastLogNumBits should be less than 20.");
+ 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);
+ double result =
+ (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_rate())) + 1;
+ assert(result > 0 && result < SIZE_MAX, "Result is not in an acceptable range.");
+ size_t rate = static_cast(result);
+ _bytes_until_sample = rate;
+}
+
+void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) {
+ if (get_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() {
+ MutexLockerEx mu(ThreadHeapSampler_lock, Mutex::_no_safepoint_check_flag);
+
+ 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::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);
+}
+
+int ThreadHeapSampler::enabled() {
+ return OrderAccess::load_acquire(&_enabled);
+}
+
+void ThreadHeapSampler::disable() {
+ OrderAccess::release_store(&_enabled, 0);
+}
+
+int ThreadHeapSampler::get_sampling_rate() {
+ return OrderAccess::load_acquire(&_sampling_rate);
+}
+
+void ThreadHeapSampler::set_sampling_rate(int sampling_rate) {
+ OrderAccess::release_store(&_sampling_rate, sampling_rate);
+}
+
+// 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;
+}
+
+bool ThreadHeapSampler::remove_sampling_collector() {
+ assert(_collectors_present > 0, "Problem with collector counter.");
+ _collectors_present--;
+ return true;
+}
+
+bool ThreadHeapSampler::add_sampling_collector() {
+ _collectors_present++;
+ return true;
+}
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,74 @@
+/*
+ * 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;
+ // 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;
+
+ // Used for assertion mode to determine if there is a path to a TLAB slow path
+ // without a collector present.
+ size_t _collectors_present;
+
+ static void init_log_table();
+
+ public:
+ ThreadHeapSampler() : _bytes_until_sample(0) {
+ _rnd = static_cast(reinterpret_cast(this));
+ if (_rnd == 0) {
+ _rnd = 1;
+ }
+
+ _collectors_present = 0;
+ }
+
+ 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();
+ static void enable();
+ static void disable();
+
+ static void set_sampling_rate(int sampling_rate);
+ static int get_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/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,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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** 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("HeapMonitorTest");
+ } 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 boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
+ public native static void disableSamplingEvents();
+
+ /**
+ * 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", 58));
+ return frames;
+ }
+
+ private static void allocate(List frames) {
+ int sum = 0;
+ for (int j = 0; j < allocationIterations; j++) {
+ sum += actuallyAllocate();
+ }
+ frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 93));
+ frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 66));
+ }
+
+ 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", 75));
+ 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 static int[][][] sampleEverything() {
+ enableSamplingEvents();
+ setSamplingRate(0);
+
+ // Loop around an allocation loop and wait until the tlabs have settled.
+ final int maxTries = 10;
+ int[][][] result = new int[maxTries][][];
+ for (int i = 0; i < maxTries; i++) {
+ final int maxInternalTries = 400;
+ result[i] = new int[maxInternalTries][];
+
+ resetEventStorage();
+ for (int j = 0; j < maxInternalTries; j++) {
+ final int size = 1000;
+ result[i][j] = new int[size];
+ }
+
+ int sampledEvents = sampledEvents();
+ if (sampledEvents == maxInternalTries) {
+ return result;
+ }
+ }
+
+ throw new RuntimeException("Could not set the sampler");
+ }
+
+ public native static int sampledEvents();
+ 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 native static boolean enableVMEvents();
+
+ public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
+ double actual = getEventStorageElementCount();
+ 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
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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:HeapMonitorTest 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.
+ // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+ // statistical geometric variable around the sampling rate. This means that the test could be
+ // unlucky and not achieve the mean average fast enough for the test case.
+ 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:HeapMonitorTest MyPackage.HeapMonitorEventOnOffTest
+ */
+public class HeapMonitorEventOnOffTest {
+ private static void checkNoEventsAreBeingSent() {
+ HeapMonitor.resetEventStorage();
+ HeapMonitor.repeatAllocate(10);
+
+ // Check that the data is not available while heap sampling is disabled.
+ boolean status = HeapMonitor.eventStorageIsEmpty();
+ if (!status) {
+ throw new RuntimeException("Storage is not empty after allocating with disabled events.");
+ }
+ }
+
+ 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 after allocating with enabled events.");
+ }
+ }
+
+ 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/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,49 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @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
+ */
+
+import java.util.List;
+
+public class HeapMonitorEventsForTwoThreadsTest {
+ public native static boolean checkSamples();
+
+ 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();
+ if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) {
+ throw new RuntimeException("Sampling event is thread enabled, that is unexpected.");
+ }
+ }
+}
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,33 @@
+/*
+ * 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
+ * @requires vm.gc == "ConcMarkSweep" | vm.gc == "null"
+ * @compile HeapMonitorGCCMSTest.java
+ * @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
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ * @requires vm.gc == "Parallel" | vm.gc == "null"
+ * @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
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ * @requires vm.gc == "Serial" | vm.gc == "null"
+ * @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
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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
+ * @requires vm.gc == "G1" | vm.gc == "null"
+ * @run main/othervm/native -agentlib:HeapMonitorTest 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", 48));
+ 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:HeapMonitorTest 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: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
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:HeapMonitorTest -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.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;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor API when allocating a multi-array.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorMultiArrayTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorMultiArrayTest
+ */
+public class HeapMonitorMultiArrayTest {
+
+ private static int[][] tab;
+ public static void main(String[] args) throws Exception {
+ // Set ourselves to sample everything.
+ HeapMonitor.sampleEverything();
+
+ // Do a few allocations now and see if the callback happens and the program finishes:
+ HeapMonitor.resetEventStorage();
+ int iterations = 1000;
+ int allocationsPerIteration = 6;
+ for (int i = 0; i < iterations; i++) {
+ tab = new int[5][5];
+ }
+
+ int sampledEvents = HeapMonitor.sampledEvents();
+ int expectedNumber = iterations * allocationsPerIteration;
+
+ if (sampledEvents != expectedNumber) {
+ throw new RuntimeException("Number of samples (" + sampledEvents + ") not the expected ("
+ + expectedNumber + ")");
+ }
+ }
+}
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:HeapMonitorTest 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/HeapMonitorRecursiveTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 does not do infinite recursion.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorRecursiveTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorRecursiveTest
+ */
+public class HeapMonitorRecursiveTest {
+ private native static void setCallbackToCallAllocateSomeMore();
+ private native static boolean didCallback();
+ private static int[] tab;
+
+ public static void main(String[] args) throws Exception {
+ // Set ourselves to sample everything.
+ HeapMonitor.sampleEverything();
+
+ // Set a callback that does allocate more data. If a callback's allocations get sampled, this
+ // would do an infinite recursion.
+ setCallbackToCallAllocateSomeMore();
+
+ // Do a few allocations now and see if the callback happens and the program finishes:
+ for (int i = 0; i < 1000; i++) {
+ tab = new int[1024];
+ }
+
+ if (!didCallback()) {
+ throw new RuntimeException("Did not get a callback...");
+ }
+ }
+}
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,88 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
+ * @compile HeapMonitorStatArrayCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest 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.
+ // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+ // statistical geometric variable around the sampling rate. This means that the test could be
+ // unlucky and not achieve the mean average fast enough for the test case.
+ 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,145 @@
+/*
+ * 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:HeapMonitorTest 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.
+ // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+ // statistical geometric variable around the sampling rate. This means that the test could be
+ // unlucky and not achieve the mean average fast enough for the test case.
+ 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.
+ // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+ // statistical geometric variable around the sampling rate. This means that the test could be
+ // unlucky and not achieve the mean average fast enough for the test case.
+ 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,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
+ * @summary Verifies the JVMTI Heap Monitor sampling rate average.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorStatRateTest.java
+ * @requires vm.compMode != "Xcomp"
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatRateTest
+ */
+
+public class HeapMonitorStatRateTest {
+
+ private native static double getAverageRate();
+
+ private static boolean testRateOnce(int rate, boolean throwIfFailure) {
+ HeapMonitor.resetEventStorage();
+ HeapMonitor.setSamplingRate(rate);
+
+ HeapMonitor.enableSamplingEvents();
+
+ int allocationTotal = 10 * 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;
+
+ boolean failure = (errorPercentage > 10.0);
+
+ if (failure && throwIfFailure) {
+ throw new RuntimeException("Rate average over 10% for rate " + rate + " -> " + actualCount
+ + ", " + expectedCount);
+ }
+
+ return failure;
+ }
+
+
+ private static void testRate(int rate) {
+ // Test the rate twice, it can happen that the test is "unlucky" and the rate just goes above
+ // the 10% mark. So try again to squash flakiness.
+ // Flakiness is due to the fact that this test is dependent on the sampling rate, which is a
+ // statistical geometric variable around the sampling rate. This means that the test could be
+ // unlucky and not achieve the mean average fast enough for the test case.
+ if (!testRateOnce(rate, false)) {
+ testRateOnce(rate, true);
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] tab = {1024, 8192};
+
+ for (int rateIdx = 0; rateIdx < tab.length; rateIdx++) {
+ testRate(tab[rateIdx]);
+ }
+ }
+}
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:HeapMonitorTest 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,70 @@
+/*
+ * 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:HeapMonitorTest MyPackage.HeapMonitorTest
+ */
+
+public class HeapMonitorTest {
+
+ 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", 51));
+
+ 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:HeapMonitorTest 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 < 100; 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,59 @@
+/*
+ * 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 Thread information sanity.
+ * @compile HeapMonitorThreadTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadTest
+ */
+
+import java.util.List;
+
+public class HeapMonitorThreadTest {
+ private native static boolean checkSamples(int numThreads);
+
+ public static void main(String[] args) {
+ final int numThreads = 24;
+ List threadList = ThreadInformation.createThreadList(numThreads);
+
+ // Sample at a rate of 8k.
+ HeapMonitor.setSamplingRate(1 << 13);
+ HeapMonitor.enableSamplingEvents();
+
+ System.err.println("Starting threads");
+ ThreadInformation.startThreads(threadList);
+ ThreadInformation.waitForThreads(threadList);
+ System.err.println("Waited for threads");
+
+ if (!checkSamples(numThreads)) {
+ 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/HeapMonitorTwoAgentsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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 does not work with two agents.
+ * @compile HeapMonitorTwoAgentsTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorTwoAgentsTest
+ */
+
+import java.util.List;
+
+public class HeapMonitorTwoAgentsTest {
+ private native static boolean enablingSamplingInSecondaryAgent();
+ private native static boolean obtainedEventsForBothAgents(Frame[] frames);
+
+ public static void main(String[] args) {
+ HeapMonitor.enableSamplingEvents();
+
+ if (enablingSamplingInSecondaryAgent()) {
+ throw new RuntimeException("Enabling sampling in second agent succeeded...");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies that when the VM event is sent, sampled events are also collected.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorVMEventsTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorVMEventsTest
+ */
+
+public class HeapMonitorVMEventsTest implements Cloneable {
+ private static native int vmEvents();
+ private static Object[] array;
+
+ private static void cloneObjects(int iterations) {
+ HeapMonitorVMEventsTest object = new HeapMonitorVMEventsTest();
+ for (int i = 0; i < iterations; i++) {
+ try {
+ array[i] = object.clone();
+ } catch (Exception e) {
+ // Nop.
+ }
+ }
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ private static void checkDifference(int first, int second) {
+ double diff = Math.abs(first - second) * 100;
+ diff /= first;
+
+ // Accept a 10% error rate: with objects being allocated: this allows a bit of room in
+ // case other items are getting allocated during the test.
+ if (diff > 10) {
+ throw new RuntimeException("Error rate is over the accepted rate: " + diff
+ + ": " + first + " , " + second);
+ }
+ }
+
+ private static void compareSampledAndVM() {
+ final int iterations = 1024;
+ HeapMonitor.resetEventStorage();
+ cloneObjects(iterations);
+
+ int onlySampleCount = HeapMonitor.sampledEvents();
+
+ HeapMonitor.enableVMEvents();
+ HeapMonitor.resetEventStorage();
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Storage is not empty after reset.");
+ }
+
+ cloneObjects(iterations);
+
+ int sampleCount = HeapMonitor.sampledEvents();
+ int vmCount = vmEvents();
+
+ System.err.println("Obtained: " + onlySampleCount + " - " + sampleCount + " - " + vmCount);
+ checkDifference(onlySampleCount, sampleCount);
+ checkDifference(onlySampleCount, vmCount);
+ }
+
+ public static void main(String[] args) {
+ final int iterations = 1 << 10;
+ array = new Object[iterations];
+
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Storage is not empty at test start...");
+ }
+
+ HeapMonitor.sampleEverything();
+ compareSampledAndVM();
+ }
+}
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,196 @@
+/*
+ * 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 Allocator allocator;
+
+ public ThreadInformation(Thread thread, Allocator allocator) {
+ this.thread = thread;
+ this.allocator = allocator;
+ }
+
+ public void waitForJobDone() {
+ allocator.waitForJobDone();
+ }
+
+ 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) {
+ System.err.println("Waiting for threads to be done");
+ // Wait until all threads have put an object in the queue.
+ for (ThreadInformation info : threadList) {
+ info.waitForJobDone();
+ }
+ }
+
+ public static List createThreadList(int numThreads) {
+ List threadList = new ArrayList<>();
+ for (int i = 0 ; i < numThreads; i++) {
+ Allocator allocator = new Allocator(i);
+ Thread thread = new Thread(allocator, "Allocator" + i);
+
+ ThreadInformation info = new ThreadInformation(thread, allocator);
+ threadList.add(info);
+ thread.start();
+ }
+ return threadList;
+ }
+}
+
+class Allocator implements Runnable {
+ private int depth;
+ private List currentList;
+ private BlockingQueue