# HG changeset patch
# User jcbeyler
# Date 1522474476 25200
# Fri Mar 30 22:34:36 2018 -0700
# Node ID c9b94af6c2c492c38580c46ea7af88f07d51c80a
# Parent be608cad0b2aa8a5e73f8925f26ab10409b1ee9c
[mq]: heap8
diff --git a/make/nb_native/nbproject/configurations.xml b/make/nb_native/nbproject/configurations.xml
--- a/make/nb_native/nbproject/configurations.xml
+++ b/make/nb_native/nbproject/configurations.xml
@@ -6154,6 +6154,9 @@
libIsModifiableModuleTest.c
+
+ libHeapMonitorTest.c
+ libMAAClassFileLoadHook.c
@@ -40165,6 +40168,11 @@
tool="0"
flavor2="0">
+
+ tlab().allocate_sampled_object(size);
+
+ if (obj != NULL) {
+ return obj;
+ }
+ }
// Retain tlab and allocate object in shared space if
// the amount free in the tlab is too large to discard.
@@ -375,7 +386,7 @@
}
// Allocate a new TLAB...
- HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
+ obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
if (obj == NULL) {
return NULL;
}
@@ -395,6 +406,14 @@
Copy::fill_to_words(obj + hdr_size, new_tlab_size - hdr_size, badHeapWordVal);
#endif // ASSERT
}
+
+ // Send the thread information about this allocation in case a sample is
+ // requested.
+ if (ThreadHeapSampler::enabled()) {
+ size_t tlab_bytes_since_last_sample = thread->tlab().bytes_since_last_sample_point();
+ thread->heap_sampler().check_for_sampling(obj, size, tlab_bytes_since_last_sample);
+ }
+
thread->tlab().fill(obj, obj + size, new_tlab_size);
return obj;
}
diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
@@ -154,10 +154,14 @@
check_for_non_bad_heap_word_value(result, size));
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
- THREAD->incr_allocated_bytes(size * HeapWordSize);
+ int size_in_bytes = size * HeapWordSize;
+ THREAD->incr_allocated_bytes(size_in_bytes);
- AllocTracer::send_allocation_outside_tlab(klass, result, size * HeapWordSize, THREAD);
+ AllocTracer::send_allocation_outside_tlab(klass, result, size_in_bytes, THREAD);
+ if (ThreadHeapSampler::enabled()) {
+ THREAD->heap_sampler().check_for_sampling(result, size_in_bytes);
+ }
return result;
}
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
@@ -46,6 +46,16 @@
make_parsable(true); // also retire the TLAB
}
+size_t ThreadLocalAllocBuffer::remaining() {
+ if (current_end() == NULL) {
+ return 0;
+ }
+
+ // TODO: To be deprecated when FastTLABRefill is deprecated.
+ update_end_pointers();
+ return pointer_delta(reserved_end(), top());
+}
+
void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() {
global_stats()->initialize();
@@ -108,24 +118,29 @@
// Waste accounting should be done in caller as appropriate; see,
// for example, clear_before_allocation().
void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) {
- if (end() != NULL) {
+ if (current_end() != NULL) {
invariants();
if (retire) {
myThread()->incr_allocated_bytes(used_bytes());
}
- CollectedHeap::fill_with_object(top(), hard_end(), retire && zap);
+ // TODO: To be deprecated when FastTLABRefill is deprecated.
+ update_end_pointers();
+ CollectedHeap::fill_with_object(top(), reserved_end(), retire && zap);
if (retire || ZeroTLAB) { // "Reset" the TLAB
set_start(NULL);
set_top(NULL);
set_pf_top(NULL);
- set_end(NULL);
+ set_current_end(NULL);
+ set_allocation_end(NULL);
+ set_last_slow_path_end(NULL);
}
}
assert(!(retire || ZeroTLAB) ||
- (start() == NULL && end() == NULL && top() == NULL),
+ (start() == NULL && current_end() == NULL && top() == NULL &&
+ _allocation_end == NULL && _last_slow_path_end == NULL),
"TLAB must be reset");
}
@@ -171,8 +186,13 @@
_number_of_refills++;
print_stats("fill");
assert(top <= start + new_size - alignment_reserve(), "size too small");
+
initialize(start, top, start + new_size - alignment_reserve());
+ if (ThreadHeapSampler::enabled()) {
+ set_sample_end();
+ }
+
// Reset amount of internal fragmentation
set_refill_waste_limit(initial_refill_waste_limit());
}
@@ -183,7 +203,9 @@
set_start(start);
set_top(top);
set_pf_top(top);
- set_end(end);
+ set_current_end(end);
+ set_allocation_end(end);
+ set_last_slow_path_end(end);
invariants();
}
@@ -306,12 +328,60 @@
guarantee(p == top(), "end of last object must match end of space");
}
+void ThreadLocalAllocBuffer::set_sample_end() {
+ size_t heap_words_remaining = pointer_delta(_current_end, _top);
+ size_t bytes_until_sample = myThread()->heap_sampler().bytes_until_sample();
+ size_t words_until_sample = bytes_until_sample / HeapWordSize;;
+
+ if (heap_words_remaining > words_until_sample) {
+ HeapWord* new_end = _top + words_until_sample;
+ set_current_end(new_end);
+ set_last_slow_path_end(new_end);
+ _bytes_since_last_sample_point = bytes_until_sample;
+ } else {
+ _bytes_since_last_sample_point = heap_words_remaining * HeapWordSize;;
+ }
+}
+
Thread* ThreadLocalAllocBuffer::myThread() {
return (Thread*)(((char *)this) +
in_bytes(start_offset()) -
in_bytes(Thread::tlab_start_offset()));
}
+void ThreadLocalAllocBuffer::set_back_allocation_end() {
+ update_end_pointers();
+ _current_end = _allocation_end;
+}
+
+void ThreadLocalAllocBuffer::update_end_pointers() {
+ // Did a fast TLAB refill occur? (This will be deprecated when fast TLAB
+ // refill disappears).
+ if (_last_slow_path_end != _current_end) {
+ // Fix up the last slow path end to be now the end of this TLAB.
+ _last_slow_path_end = _current_end;
+ _allocation_end = _current_end;
+ }
+}
+
+HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) {
+ Thread* thread = myThread();
+ thread->tlab().set_back_allocation_end();
+ HeapWord* result = thread->tlab().allocate(size);
+
+ if (result) {
+ thread->heap_sampler().check_for_sampling(result, size * HeapWordSize, _bytes_since_last_sample_point);
+ thread->tlab().set_sample_end();
+ }
+
+ return result;
+}
+
+HeapWord* ThreadLocalAllocBuffer::reserved_end() {
+ assert (_last_slow_path_end == _current_end,
+ "Have to call update_end_pointers before reserved_end.");
+ return _allocation_end + alignment_reserve();
+}
GlobalTLABStats::GlobalTLABStats() :
_allocating_threads_avg(TLABAllocationWeight) {
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
@@ -37,6 +37,14 @@
// It is thread-private at any time, but maybe multiplexed over
// time across multiple threads. The park()/unpark() pair is
// used to make it available for such multiplexing.
+//
+// Heap sampling is performed via the current_end/allocation_end
+// fields.
+// allocation_end contains the real end of the tlab allocation,
+// whereas current_end can be set to an arbitrary spot in the tlab to
+// trip the return and sample the allocation.
+// last_slow_path_end is used to track if a fast tlab refill occured
+// between slowpath calls.
class ThreadLocalAllocBuffer: public CHeapObj {
friend class VMStructs;
friend class JVMCIVMStructs;
@@ -44,10 +52,14 @@
HeapWord* _start; // address of TLAB
HeapWord* _top; // address after last allocation
HeapWord* _pf_top; // allocation prefetch watermark
- HeapWord* _end; // allocation end (excluding alignment_reserve)
+ HeapWord* _current_end; // allocation end (can be the sampling end point or _allocation_end)
+ HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve)
+ HeapWord* _last_slow_path_end; // last address for slow_path_end (as opposed to _allocation_end)
+
size_t _desired_size; // desired size (including alignment_reserve)
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
size_t _allocated_before_last_gc; // total bytes allocated up until the last gc
+ size_t _bytes_since_last_sample_point; // bytes since last sample point.
static size_t _max_size; // maximum size of any TLAB
static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB
@@ -65,7 +77,9 @@
void initialize_statistics();
void set_start(HeapWord* start) { _start = start; }
- void set_end(HeapWord* end) { _end = end; }
+ void set_current_end(HeapWord* current_end) { _current_end = current_end; }
+ void set_allocation_end(HeapWord* ptr) { _allocation_end = ptr; }
+ void set_last_slow_path_end(HeapWord* ptr) { _last_slow_path_end = ptr; }
void set_top(HeapWord* top) { _top = top; }
void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; }
void set_desired_size(size_t desired_size) { _desired_size = desired_size; }
@@ -76,7 +90,9 @@
static int target_refills() { return _target_refills; }
size_t initial_desired_size();
- size_t remaining() const { return end() == NULL ? 0 : pointer_delta(hard_end(), top()); }
+ size_t remaining();
+
+ void update_end_pointers();
// Make parsable and release it.
void reset();
@@ -84,7 +100,7 @@
// Resize based on amount of allocation, etc.
void resize();
- void invariants() const { assert(top() >= start() && top() <= end(), "invalid tlab"); }
+ void invariants() const { assert(top() >= start() && top() <= current_end(), "invalid tlab"); }
void initialize(HeapWord* start, HeapWord* top, HeapWord* end);
@@ -114,19 +130,21 @@
static void set_max_size(size_t max_size) { _max_size = max_size; }
HeapWord* start() const { return _start; }
- HeapWord* end() const { return _end; }
- HeapWord* hard_end() const { return _end + alignment_reserve(); }
+ HeapWord* current_end() const { return _current_end; }
HeapWord* top() const { return _top; }
+ HeapWord* reserved_end();
HeapWord* pf_top() const { return _pf_top; }
size_t desired_size() const { return _desired_size; }
size_t used() const { return pointer_delta(top(), start()); }
size_t used_bytes() const { return pointer_delta(top(), start(), 1); }
- size_t free() const { return pointer_delta(end(), top()); }
+ size_t free() const { return pointer_delta(current_end(), top()); }
// Don't discard tlab if remaining space is larger than this.
size_t refill_waste_limit() const { return _refill_waste_limit; }
+ size_t bytes_since_last_sample_point() const { return _bytes_since_last_sample_point; }
// Allocate size HeapWords. The memory is NOT initialized to zero.
inline HeapWord* allocate(size_t size);
+ HeapWord* allocate_sampled_object(size_t size);
// Reserve space at the end of TLAB
static size_t end_reserve() {
@@ -162,11 +180,14 @@
void fill(HeapWord* start, HeapWord* top, size_t new_size);
void initialize();
+ void set_back_allocation_end();
+ void set_sample_end();
+
static size_t refill_waste_limit_increment() { return TLABWasteIncrement; }
// Code generation support
static ByteSize start_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _start); }
- static ByteSize end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _end ); }
+ static ByteSize current_end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _current_end ); }
static ByteSize top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _top ); }
static ByteSize pf_top_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _pf_top ); }
static ByteSize size_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _desired_size ); }
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
@@ -34,7 +34,7 @@
inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) {
invariants();
HeapWord* obj = top();
- if (pointer_delta(end(), obj) >= size) {
+ if (pointer_delta(current_end(), obj) >= size) {
// successful thread-local allocation
#ifdef ASSERT
// Skip mangling the space corresponding to the object header to
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -323,7 +323,7 @@
\
nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \
- nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \
+ nonstatic_field(ThreadLocalAllocBuffer, _current_end, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \
nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -1242,9 +1242,9 @@
if (UseTLAB) { // Private allocation: load from TLS
Node* thread = transform_later(new ThreadLocalNode());
int tlab_top_offset = in_bytes(JavaThread::tlab_top_offset());
- int tlab_end_offset = in_bytes(JavaThread::tlab_end_offset());
+ int tlab_current_end_offset = in_bytes(JavaThread::tlab_current_end_offset());
eden_top_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_top_offset);
- eden_end_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_end_offset);
+ eden_end_adr = basic_plus_adr(top()/*not oop*/, thread, tlab_current_end_offset);
} else { // Shared allocation: load from globals
CollectedHeap* ch = Universe::heap();
address top_adr = (address)ch->top_addr();
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -202,6 +202,7 @@
SharedRuntime::_new_instance_ctr++; // new instance requires GC
#endif
assert(check_compiled_frame(thread), "incorrect caller");
+ JvmtiSampledObjectAllocEventCollector collector;
// These checks are cheap to make and support reflective allocation.
int lh = klass->layout_helper();
@@ -240,6 +241,7 @@
SharedRuntime::_new_array_ctr++; // new array requires GC
#endif
assert(check_compiled_frame(thread), "incorrect caller");
+ JvmtiSampledObjectAllocEventCollector collector;
// Scavenge and allocate an instance.
oop result;
@@ -321,6 +323,7 @@
// multianewarray for 2 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread *thread))
+ JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi2_ctr++; // multianewarray for 1 dimension
#endif
@@ -337,6 +340,7 @@
// multianewarray for 3 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread *thread))
+ JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi3_ctr++; // multianewarray for 1 dimension
#endif
@@ -354,6 +358,7 @@
// multianewarray for 4 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread *thread))
+ JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi4_ctr++; // multianewarray for 1 dimension
#endif
@@ -372,6 +377,7 @@
// multianewarray for 5 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray5_C(Klass* elem_type, int len1, int len2, int len3, int len4, int len5, JavaThread *thread))
+ JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi5_ctr++; // multianewarray for 1 dimension
#endif
diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml
--- a/src/hotspot/share/prims/jvmti.xml
+++ b/src/hotspot/share/prims/jvmti.xml
@@ -10353,6 +10353,19 @@
See .
+
+
+ Can sample the heap.
+ If this capability is enabled then the heap sampling methods can be called.
+
+
+
+
+ Can generate sampled object allocation events.
+ If this capability is enabled then an event for a sampled object allocation is sent
+ when the heap sampler is enabled as well.
+
+
@@ -11531,6 +11544,47 @@
+
+
+ Set TLAB Heap Sampling
+
+ Set up the TLAB allocation system to sample memory by forcing slow path execution at a given
+ average rate via the . The
+ will be the average sampling rate used throughout
+ the execution.
+
+ Setting to 0 disables the TLAB heap sampling.
+ Combined with a event, the Java agent can obtain object allocations with a given sample rate.
+
+ new
+
+
+
+
+
+
+
+ The monitoring rate in bytes used for sampling. The sampler will use a statistical approach to
+ provide in average sampling every allocated bytes.
+
+ Note: a low monitoring rate, such as sampling every 1024 bytes, will probably incur a high overhead.
+ Due to the incurred overhead, the sampler should only be used when knowing it may impact performance.
+ On the other hand, sampling however every 1024kB has a far less chance of a high overhead since it will sample
+ 1024 times less than the 1024-byte sampling.
+
+
+
+
+
+ is less than zero.
+
+
+ Calling this method without TLAB.
+
+
+
+
+
@@ -11739,6 +11793,9 @@
A method in the new class version has different modifiers
than its counterpart in the old class version.
+
+ The Thread Local Allocation Buffer system is disabled and the method requires it enabled.
+
@@ -13513,8 +13570,56 @@
+
+
+ Sent when an object is sampled via the
+ Heap Sampling Monitoring system .
+ The event is sent once the allocation has been done and provides the object, stack trace
+ for the allocation, the thread allocating, the size of allocation, and class.
+
+ new
+
+
+
+
+
+
+ JNIEnv
+
+
+ The JNI environment of the event (current) thread.
+
+
+
+
+
+ Thread allocating the object.
+
+
+
+
+
+ JNI local reference to the object that was allocated.
+
+
+
+
+
+ JNI local reference to the class of the object
+
+
+
+
+
+ Size of the object (in bytes). See .
+
+
+
+
+
+ id="ObjectFree" const="JVMTI_EVENT_OBJECT_FREE" num="83">
An Object Free event is sent when the garbage collector frees an object.
Events are only sent for tagged objects--see
@@ -13534,7 +13639,7 @@
The freed object's tag
-
+
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -64,6 +64,7 @@
#include "runtime/reflectionUtils.hpp"
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadHeapSampler.hpp"
#include "runtime/threadSMR.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
@@ -3631,6 +3632,12 @@
return JVMTI_ERROR_NONE;
} /* end GetAvailableProcessors */
+jvmtiError
+JvmtiEnv::SetTlabHeapSampling(jint monitoring_rate) {
+ ThreadHeapSampler::set_tlab_heap_sampling(monitoring_rate);
+ return JVMTI_ERROR_NONE;
+} /* end SetTlabHeapSampling */
+
//
// System Properties functions
//
diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp
--- a/src/hotspot/share/prims/jvmtiEventController.cpp
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp
@@ -84,6 +84,7 @@
static const jlong OBJECT_FREE_BIT = (((jlong)1) << (JVMTI_EVENT_OBJECT_FREE - TOTAL_MIN_EVENT_TYPE_VAL));
static const jlong RESOURCE_EXHAUSTED_BIT = (((jlong)1) << (JVMTI_EVENT_RESOURCE_EXHAUSTED - TOTAL_MIN_EVENT_TYPE_VAL));
static const jlong VM_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_VM_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL));
+static const jlong SAMPLED_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_SAMPLED_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL));
// bits for extension events
static const jlong CLASS_UNLOAD_BIT = (((jlong)1) << (EXT_EVENT_CLASS_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL));
@@ -615,6 +616,7 @@
JvmtiExport::set_should_post_compiled_method_load((any_env_thread_enabled & COMPILED_METHOD_LOAD_BIT) != 0);
JvmtiExport::set_should_post_compiled_method_unload((any_env_thread_enabled & COMPILED_METHOD_UNLOAD_BIT) != 0);
JvmtiExport::set_should_post_vm_object_alloc((any_env_thread_enabled & VM_OBJECT_ALLOC_BIT) != 0);
+ JvmtiExport::set_should_post_sampled_object_alloc((any_env_thread_enabled & SAMPLED_OBJECT_ALLOC_BIT) != 0);
// need this if we want thread events or we need them to init data
JvmtiExport::set_should_post_thread_life((any_env_thread_enabled & NEED_THREAD_LIFE_EVENTS) != 0);
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -1031,12 +1031,12 @@
return k;
}
-class JvmtiVMObjectAllocEventMark : public JvmtiClassEventMark {
+class JvmtiObjectAllocEventMark : public JvmtiClassEventMark {
private:
jobject _jobj;
jlong _size;
public:
- JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) {
+ JvmtiObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) {
_jobj = (jobject)to_jobject(obj);
_size = obj->size() * wordSize;
};
@@ -1201,6 +1201,7 @@
bool JvmtiExport::_should_post_object_free = false;
bool JvmtiExport::_should_post_resource_exhausted = false;
bool JvmtiExport::_should_post_vm_object_alloc = false;
+bool JvmtiExport::_should_post_sampled_object_alloc = false;
bool JvmtiExport::_should_post_on_exceptions = false;
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2298,6 +2299,26 @@
}
}
+// Collect all the vm internally allocated objects which are visible to java world
+void JvmtiExport::record_sampled_internal_object_allocation(oop obj) {
+ Thread* thread = Thread::current_or_null();
+ if (thread != NULL && thread->is_Java_thread()) {
+ // Can not take safepoint here.
+ NoSafepointVerifier no_sfpt;
+ // Can not take safepoint here so can not use state_for to get
+ // jvmti thread state.
+ JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state();
+ if (state != NULL ) {
+ // state is non NULL when SampledObjectAllocEventCollector is enabled.
+ JvmtiSampledObjectAllocEventCollector *collector;
+ collector = state->get_sampled_object_alloc_event_collector();
+ if (collector != NULL && collector->is_enabled()) {
+ collector->record_allocation(obj);
+ }
+ }
+ }
+}
+
void JvmtiExport::post_garbage_collection_finish() {
Thread *thread = Thread::current(); // this event is posted from VM-Thread.
EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
@@ -2487,7 +2508,6 @@
}
}
-
void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
@@ -2503,7 +2523,7 @@
JvmtiTrace::safe_get_thread_name(thread),
object==NULL? "NULL" : object->klass()->external_name()));
- JvmtiVMObjectAllocEventMark jem(thread, h());
+ JvmtiObjectAllocEventMark jem(thread, h());
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc;
if (callback != NULL) {
@@ -2514,6 +2534,37 @@
}
}
+void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
+ EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
+ ("[%s] Trg sampled object alloc triggered",
+ JvmtiTrace::safe_get_thread_name(thread)));
+
+ if (object == NULL) {
+ return;
+ }
+
+ HandleMark hm(thread);
+ Handle h(thread, object);
+
+ JvmtiEnvIterator it;
+ for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
+ if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
+ EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
+ ("[%s] Evt sampled object alloc sent %s",
+ JvmtiTrace::safe_get_thread_name(thread),
+ object == NULL ? "NULL" : object->klass()->external_name()));
+
+ JvmtiObjectAllocEventMark jem(thread, h());
+ JvmtiJavaThreadEventTransition jet(thread);
+ jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
+ if (callback != NULL) {
+ (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
+ jem.jni_jobject(), jem.jni_class(), jem.size());
+ }
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////
void JvmtiExport::cleanup_thread(JavaThread* thread) {
@@ -2539,7 +2590,7 @@
void JvmtiExport::oops_do(OopClosure* f) {
JvmtiCurrentBreakpoints::oops_do(f);
- JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(f);
+ JvmtiObjectAllocEventCollector::oops_do_for_all_threads(f);
}
void JvmtiExport::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
@@ -2672,6 +2723,9 @@
} else if (is_dynamic_code_event()) {
_prev = state->get_dynamic_code_event_collector();
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this);
+ } else if (is_sampled_object_alloc_event()) {
+ _prev = state->get_sampled_object_alloc_event_collector();
+ state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)this);
}
}
@@ -2688,14 +2742,19 @@
// this thread's jvmti state was created during the scope of
// the event collector.
}
- } else {
- if (is_dynamic_code_event()) {
- if (state->get_dynamic_code_event_collector() == this) {
- state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev);
- } else {
- // this thread's jvmti state was created during the scope of
- // the event collector.
- }
+ } else if (is_dynamic_code_event()) {
+ if (state->get_dynamic_code_event_collector() == this) {
+ state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev);
+ } else {
+ // this thread's jvmti state was created during the scope of
+ // the event collector.
+ }
+ } else if (is_sampled_object_alloc_event()) {
+ if (state->get_sampled_object_alloc_event_collector() == this) {
+ state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)_prev);
+ } else {
+ // this thread's jvmti state was created during the scope of
+ // the event collector.
}
}
}
@@ -2733,31 +2792,25 @@
}
// Setup current thread to record vm allocated objects.
-JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() : _allocated(NULL) {
- if (JvmtiExport::should_post_vm_object_alloc()) {
- _enable = true;
- setup_jvmti_thread_state();
- } else {
- _enable = false;
- }
+JvmtiObjectAllocEventCollector::JvmtiObjectAllocEventCollector() :
+ _allocated(NULL), _enable(false), _post_callback(NULL) {
}
// Post vm_object_alloc event for vm allocated objects visible to java
// world.
-JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() {
+void JvmtiObjectAllocEventCollector::generate_call_for_allocated() {
if (_allocated != NULL) {
set_enabled(false);
for (int i = 0; i < _allocated->length(); i++) {
oop obj = _allocated->at(i);
- JvmtiExport::post_vm_object_alloc(JavaThread::current(), obj);
+ _post_callback(JavaThread::current(), obj);
}
delete _allocated;
}
- unset_jvmti_thread_state();
}
-void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) {
- assert(is_enabled(), "VM object alloc event collector is not enabled");
+void JvmtiObjectAllocEventCollector::record_allocation(oop obj) {
+ assert(is_enabled(), "Object alloc event collector is not enabled");
if (_allocated == NULL) {
_allocated = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(1, true);
}
@@ -2765,7 +2818,7 @@
}
// GC support.
-void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) {
+void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) {
if (_allocated != NULL) {
for(int i=_allocated->length() - 1; i >= 0; i--) {
if (_allocated->at(i) != NULL) {
@@ -2775,7 +2828,7 @@
}
}
-void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
+void JvmtiObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
// no-op if jvmti not enabled
if (!JvmtiEnv::environments_might_exist()) {
return;
@@ -2784,11 +2837,17 @@
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) {
JvmtiThreadState *state = jthr->jvmti_thread_state();
if (state != NULL) {
- JvmtiVMObjectAllocEventCollector *collector;
+ JvmtiObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector();
while (collector != NULL) {
collector->oops_do(f);
- collector = (JvmtiVMObjectAllocEventCollector *)collector->get_prev();
+ collector = (JvmtiObjectAllocEventCollector*) collector->get_prev();
+ }
+
+ collector = state->get_sampled_object_alloc_event_collector();
+ while (collector != NULL) {
+ collector->oops_do(f);
+ collector = (JvmtiObjectAllocEventCollector*) collector->get_prev();
}
}
}
@@ -2823,6 +2882,35 @@
}
};
+// Setup current thread to record vm allocated objects.
+JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() {
+ if (JvmtiExport::should_post_vm_object_alloc()) {
+ _enable = true;
+ _post_callback = JvmtiExport::post_vm_object_alloc;
+ setup_jvmti_thread_state();
+ }
+}
+
+JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() {
+ generate_call_for_allocated();
+ unset_jvmti_thread_state();
+}
+
+// Setup current thread to record sampled allocated objects.
+JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() {
+ if (JvmtiExport::should_post_sampled_object_alloc()) {
+ _enable = true;
+ _callback_for_all_oops = true;
+ _post_callback = JvmtiExport::post_sampled_object_alloc;
+ setup_jvmti_thread_state();
+ }
+}
+
+JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() {
+ generate_call_for_allocated();
+ unset_jvmti_thread_state();
+}
+
JvmtiGCMarker::JvmtiGCMarker() {
// if there aren't any JVMTI environments then nothing to do
if (!JvmtiEnv::environments_might_exist()) {
diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp
--- a/src/hotspot/share/prims/jvmtiExport.hpp
+++ b/src/hotspot/share/prims/jvmtiExport.hpp
@@ -123,6 +123,7 @@
// breakpoint info
JVMTI_SUPPORT_FLAG(should_clean_up_heap_objects)
JVMTI_SUPPORT_FLAG(should_post_vm_object_alloc)
+ JVMTI_SUPPORT_FLAG(should_post_sampled_object_alloc)
// If flag cannot be implemented, give an error if on=true
static void report_unsupported(bool on);
@@ -376,6 +377,17 @@
record_vm_internal_object_allocation(object);
}
}
+
+ static void record_sampled_internal_object_allocation(oop object) NOT_JVMTI_RETURN;
+ // Post objects collected by sampled_object_alloc_event_collector.
+ static void post_sampled_object_alloc(JavaThread *thread, oop object) NOT_JVMTI_RETURN;
+ // Collects vm internal objects for later event posting.
+ inline static void sampled_object_alloc_event_collector(oop object) {
+ if (should_post_sampled_object_alloc()) {
+ record_sampled_internal_object_allocation(object);
+ }
+ }
+
inline static void post_array_size_exhausted() {
if (should_post_resource_exhausted()) {
post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR,
@@ -437,10 +449,13 @@
JvmtiEventCollector* _prev; // Save previous one to support nested event collector.
public:
+ JvmtiEventCollector() : _prev(NULL) {}
+
void setup_jvmti_thread_state(); // Set this collector in current thread.
void unset_jvmti_thread_state(); // Reset previous collector in current thread.
virtual bool is_dynamic_code_event() { return false; }
virtual bool is_vm_object_alloc_event(){ return false; }
+ virtual bool is_sampled_object_alloc_event(){ return false; }
JvmtiEventCollector *get_prev() { return _prev; }
};
@@ -475,21 +490,18 @@
};
-// Used to record vm internally allocated object oops and post
-// vm object alloc event for objects visible to java world.
-// Constructor enables JvmtiThreadState flag and all vm allocated
-// objects are recorded in a growable array. When destructor is
-// called the vm object alloc event is posted for each objects
-// visible to java world.
-// See jvm.cpp file for its usage.
+// Used as a base class for object allocation collection and then posting
+// the allocations to any event notification callbacks.
//
-class JvmtiVMObjectAllocEventCollector : public JvmtiEventCollector {
- private:
+class JvmtiObjectAllocEventCollector : public JvmtiEventCollector {
+ protected:
GrowableArray* _allocated; // field to record vm internally allocated object oop.
bool _enable; // This flag is enabled in constructor and disabled
// in destructor before posting event. To avoid
// collection of objects allocated while running java code inside
- // agent post_vm_object_alloc() event handler.
+ // agent post_X_object_alloc() event handler.
+ void (*_post_callback)(JavaThread*, oop); // what callback to use when destroying the collector.
+ bool _callback_for_all_oops;
//GC support
void oops_do(OopClosure* f);
@@ -502,15 +514,42 @@
static void oops_do_for_all_threads(OopClosure* f);
public:
- JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
- ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
- bool is_vm_object_alloc_event() { return true; }
+ JvmtiObjectAllocEventCollector() NOT_JVMTI_RETURN;
+
+ void generate_call_for_allocated();
bool is_enabled() { return _enable; }
void set_enabled(bool on) { _enable = on; }
};
+// Used to record vm internally allocated object oops and post
+// vm object alloc event for objects visible to java world.
+// Constructor enables JvmtiThreadState flag and all vm allocated
+// objects are recorded in a growable array. When destructor is
+// called the vm object alloc event is posted for each object
+// visible to java world.
+// See jvm.cpp file for its usage.
+//
+class JvmtiVMObjectAllocEventCollector : public JvmtiObjectAllocEventCollector {
+ public:
+ JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
+ ~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
+ virtual bool is_vm_object_alloc_event() { return true; }
+};
+// Used to record sampled allocated object oops and post
+// sampled object alloc event.
+// Constructor enables JvmtiThreadState flag and all sampled allocated
+// objects are recorded in a growable array. When destructor is
+// called the sampled object alloc event is posted for each sampled object.
+// See jvm.cpp file for its usage.
+//
+class JvmtiSampledObjectAllocEventCollector : public JvmtiObjectAllocEventCollector {
+ public:
+ JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN;
+ ~JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN;
+ bool is_sampled_object_alloc_event() { return true; }
+};
// Marker class to disable the posting of VMObjectAlloc events
// within its scope.
diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
+++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
@@ -141,6 +141,8 @@
jc.can_generate_field_modification_events = 1;
jc.can_generate_field_access_events = 1;
jc.can_generate_breakpoint_events = 1;
+ jc.can_sample_heap = 1;
+ jc.can_generate_sampled_object_alloc_events = 1;
return jc;
}
@@ -410,6 +412,10 @@
log_trace(jvmti)("can_generate_frame_pop_events");
if (cap->can_generate_breakpoint_events)
log_trace(jvmti)("can_generate_breakpoint_events");
+ if (cap->can_sample_heap)
+ log_trace(jvmti)("can_sample_heap");
+ if (cap->can_generate_sampled_object_alloc_events)
+ log_trace(jvmti)("can_generate_sampled_object_alloc_events");
if (cap->can_suspend)
log_trace(jvmti)("can_suspend");
if (cap->can_redefine_any_class )
diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp
--- a/src/hotspot/share/prims/jvmtiThreadState.cpp
+++ b/src/hotspot/share/prims/jvmtiThreadState.cpp
@@ -60,6 +60,7 @@
_head_env_thread_state = NULL;
_dynamic_code_event_collector = NULL;
_vm_object_alloc_event_collector = NULL;
+ _sampled_object_alloc_event_collector = NULL;
_the_class_for_redefinition_verification = NULL;
_scratch_class_for_redefinition_verification = NULL;
_cur_stack_depth = UNKNOWN_STACK_DEPTH;
diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp
--- a/src/hotspot/share/prims/jvmtiThreadState.hpp
+++ b/src/hotspot/share/prims/jvmtiThreadState.hpp
@@ -113,6 +113,8 @@
JvmtiDynamicCodeEventCollector* _dynamic_code_event_collector;
// holds the current vm object alloc event collector, NULL if no event collector in use
JvmtiVMObjectAllocEventCollector* _vm_object_alloc_event_collector;
+ // holds the current sampled object alloc event collector, NULL if no event collector in use
+ JvmtiSampledObjectAllocEventCollector* _sampled_object_alloc_event_collector;
// Should only be created by factory methods
JvmtiThreadState(JavaThread *thread);
@@ -314,12 +316,18 @@
JvmtiVMObjectAllocEventCollector* get_vm_object_alloc_event_collector() {
return _vm_object_alloc_event_collector;
}
+ JvmtiSampledObjectAllocEventCollector* get_sampled_object_alloc_event_collector() {
+ return _sampled_object_alloc_event_collector;
+ }
void set_dynamic_code_event_collector(JvmtiDynamicCodeEventCollector* collector) {
_dynamic_code_event_collector = collector;
}
void set_vm_object_alloc_event_collector(JvmtiVMObjectAllocEventCollector* collector) {
_vm_object_alloc_event_collector = collector;
}
+ void set_sampled_object_alloc_event_collector(JvmtiSampledObjectAllocEventCollector* collector) {
+ _sampled_object_alloc_event_collector = collector;
+ }
//
diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp
--- a/src/hotspot/share/runtime/thread.cpp
+++ b/src/hotspot/share/runtime/thread.cpp
@@ -237,6 +237,7 @@
set_active_handles(NULL);
set_free_handle_block(NULL);
set_last_handle_mark(NULL);
+ _heap_sampler.set_thread(this);
// This initial value ==> never claimed.
_oops_do_parity = 0;
@@ -2049,6 +2050,7 @@
tlab().make_parsable(true); // retire TLAB, if any
}
+
BarrierSet::barrier_set()->on_thread_detach(this);
Threads::remove(this);
@@ -4335,7 +4337,6 @@
assert_locked_or_safepoint(Threads_lock);
BarrierSet::barrier_set()->on_thread_attach(p);
-
p->set_next(_thread_list);
_thread_list = p;
diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp
--- a/src/hotspot/share/runtime/thread.hpp
+++ b/src/hotspot/share/runtime/thread.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
#include "runtime/park.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/stubRoutines.hpp"
+#include "runtime/threadHeapSampler.hpp"
#include "runtime/threadLocalStorage.hpp"
#include "runtime/unhandledOops.hpp"
#include "trace/traceBackend.hpp"
@@ -322,6 +323,7 @@
ThreadLocalAllocBuffer _tlab; // Thread-local eden
jlong _allocated_bytes; // Cumulative number of bytes allocated on
// the Java heap
+ ThreadHeapSampler _heap_sampler; // For use when sampling the memory.
mutable TRACE_DATA _trace_data; // Thread-local data for tracing
@@ -501,6 +503,8 @@
void incr_allocated_bytes(jlong size) { _allocated_bytes += size; }
inline jlong cooked_allocated_bytes();
+ ThreadHeapSampler& heap_sampler() { return _heap_sampler; }
+
TRACE_DEFINE_THREAD_TRACE_DATA_OFFSET;
TRACE_DATA* trace_data() const { return &_trace_data; }
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
@@ -672,7 +676,7 @@
static ByteSize tlab_##name##_offset() { return byte_offset_of(Thread, _tlab) + ThreadLocalAllocBuffer::name##_offset(); }
TLAB_FIELD_OFFSET(start)
- TLAB_FIELD_OFFSET(end)
+ TLAB_FIELD_OFFSET(current_end)
TLAB_FIELD_OFFSET(top)
TLAB_FIELD_OFFSET(pf_top)
TLAB_FIELD_OFFSET(size) // desired_size
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -529,7 +529,7 @@
\
nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \
- nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \
+ nonstatic_field(ThreadLocalAllocBuffer, _current_end, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \
nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \
nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \
# HG changeset patch
# User jcbeyler
# Date 1522689232 25200
# Mon Apr 02 10:13:52 2018 -0700
# Node ID fc186fc4cf645ff2ecce2c4d4c59ec29a30774aa
# Parent c9b94af6c2c492c38580c46ea7af88f07d51c80a
[mq]: event_rebased
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
@@ -51,8 +51,6 @@
return 0;
}
- // TODO: To be deprecated when FastTLABRefill is deprecated.
- update_end_pointers();
return pointer_delta(reserved_end(), top());
}
@@ -125,8 +123,6 @@
myThread()->incr_allocated_bytes(used_bytes());
}
- // TODO: To be deprecated when FastTLABRefill is deprecated.
- update_end_pointers();
CollectedHeap::fill_with_object(top(), reserved_end(), retire && zap);
if (retire || ZeroTLAB) { // "Reset" the TLAB
@@ -135,12 +131,11 @@
set_pf_top(NULL);
set_current_end(NULL);
set_allocation_end(NULL);
- set_last_slow_path_end(NULL);
}
}
assert(!(retire || ZeroTLAB) ||
(start() == NULL && current_end() == NULL && top() == NULL &&
- _allocation_end == NULL && _last_slow_path_end == NULL),
+ _allocation_end == NULL),
"TLAB must be reset");
}
@@ -205,7 +200,6 @@
set_pf_top(top);
set_current_end(end);
set_allocation_end(end);
- set_last_slow_path_end(end);
invariants();
}
@@ -336,7 +330,6 @@
if (heap_words_remaining > words_until_sample) {
HeapWord* new_end = _top + words_until_sample;
set_current_end(new_end);
- set_last_slow_path_end(new_end);
_bytes_since_last_sample_point = bytes_until_sample;
} else {
_bytes_since_last_sample_point = heap_words_remaining * HeapWordSize;;
@@ -350,20 +343,9 @@
}
void ThreadLocalAllocBuffer::set_back_allocation_end() {
- update_end_pointers();
_current_end = _allocation_end;
}
-void ThreadLocalAllocBuffer::update_end_pointers() {
- // Did a fast TLAB refill occur? (This will be deprecated when fast TLAB
- // refill disappears).
- if (_last_slow_path_end != _current_end) {
- // Fix up the last slow path end to be now the end of this TLAB.
- _last_slow_path_end = _current_end;
- _allocation_end = _current_end;
- }
-}
-
HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) {
Thread* thread = myThread();
thread->tlab().set_back_allocation_end();
@@ -378,8 +360,6 @@
}
HeapWord* ThreadLocalAllocBuffer::reserved_end() {
- assert (_last_slow_path_end == _current_end,
- "Have to call update_end_pointers before reserved_end.");
return _allocation_end + alignment_reserve();
}
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
@@ -43,8 +43,6 @@
// allocation_end contains the real end of the tlab allocation,
// whereas current_end can be set to an arbitrary spot in the tlab to
// trip the return and sample the allocation.
-// last_slow_path_end is used to track if a fast tlab refill occured
-// between slowpath calls.
class ThreadLocalAllocBuffer: public CHeapObj {
friend class VMStructs;
friend class JVMCIVMStructs;
@@ -54,7 +52,6 @@
HeapWord* _pf_top; // allocation prefetch watermark
HeapWord* _current_end; // allocation end (can be the sampling end point or _allocation_end)
HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve)
- HeapWord* _last_slow_path_end; // last address for slow_path_end (as opposed to _allocation_end)
size_t _desired_size; // desired size (including alignment_reserve)
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
@@ -79,7 +76,6 @@
void set_start(HeapWord* start) { _start = start; }
void set_current_end(HeapWord* current_end) { _current_end = current_end; }
void set_allocation_end(HeapWord* ptr) { _allocation_end = ptr; }
- void set_last_slow_path_end(HeapWord* ptr) { _last_slow_path_end = ptr; }
void set_top(HeapWord* top) { _top = top; }
void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; }
void set_desired_size(size_t desired_size) { _desired_size = desired_size; }
@@ -92,8 +88,6 @@
size_t remaining();
- void update_end_pointers();
-
// Make parsable and release it.
void reset();
diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
@@ -229,6 +229,7 @@
// Allocation
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
+ JvmtiSampledObjectAllocEventCollector collector;
Klass* k = pool->klass_at(index, CHECK);
InstanceKlass* klass = InstanceKlass::cast(k);
@@ -258,12 +259,14 @@
IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* thread, BasicType type, jint size))
+ JvmtiSampledObjectAllocEventCollector collector;
oop obj = oopFactory::new_typeArray(type, size, CHECK);
thread->set_vm_result(obj);
IRT_END
IRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* thread, ConstantPool* pool, int index, jint size))
+ JvmtiSampledObjectAllocEventCollector collector;
Klass* klass = pool->klass_at(index, CHECK);
objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK);
thread->set_vm_result(obj);
@@ -271,6 +274,7 @@
IRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* thread, jint* first_size_address))
+ JvmtiSampledObjectAllocEventCollector collector;
// We may want to pass in more arguments - could make this slightly faster
LastFrameAccessor last_frame(thread);
ConstantPool* constants = last_frame.method()->constants();
diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml
--- a/src/hotspot/share/prims/jvmti.xml
+++ b/src/hotspot/share/prims/jvmti.xml
@@ -10353,17 +10353,12 @@
See .
-
+
- Can sample the heap.
- If this capability is enabled then the heap sampling methods can be called.
-
-
-
-
- Can generate sampled object allocation events.
- If this capability is enabled then an event for a sampled object allocation is sent
- when the heap sampler is enabled as well.
+ Can generate sampled allocation events.
+ If this capability is enabled then the heap sampling method
+ can be called and the
+ events can be enabled.
@@ -11545,29 +11540,32 @@
-
- Set TLAB Heap Sampling
-
- Set up the TLAB allocation system to sample memory by forcing slow path execution at a given
- average rate via the . The
- will be the average sampling rate used throughout
+
+ Set Heap Sampling Rate
+
+ Set up the allocation system to sample memory allocations at a given
+ average rate via the parameter, defined
+ by sampling rate in bytes. The
+ will be the average sampling rate in bytes used throughout
the execution.
-
- Setting to 0 disables the TLAB heap sampling.
- Combined with a event, the Java agent can obtain object allocations with a given sample rate.
-
- new
-
-
-
-
-
+
+ Setting to 0 samples each allocation supported by the system.
+ Combined with a event, the Java agent can obtain object allocations with a given sample rate.
+
+ new
+
+
+
+
+
- The monitoring rate in bytes used for sampling. The sampler will use a statistical approach to
- provide in average sampling every allocated bytes.
-
- Note: a low monitoring rate, such as sampling every 1024 bytes, will probably incur a high overhead.
+ The sampling rate in bytes used for sampling. The sampler will use a statistical approach to
+ provide in average sampling every allocated bytes.
+
+ Passing 0 as a sampling rate provokes a sample per allocation by the system.
+
+ Note: a low sampling rate, such as sampling every 1024 bytes, will probably incur a high overhead.
Due to the incurred overhead, the sampler should only be used when knowing it may impact performance.
On the other hand, sampling however every 1024kB has a far less chance of a high overhead since it will sample
1024 times less than the 1024-byte sampling.
@@ -11576,10 +11574,7 @@
- is less than zero.
-
-
- Calling this method without TLAB.
+ is less than zero.
@@ -11793,9 +11788,6 @@
A method in the new class version has different modifiers
than its counterpart in the old class version.
-
- The Thread Local Allocation Buffer system is disabled and the method requires it enabled.
-
@@ -13552,13 +13544,13 @@
- JNI local reference to the object that was allocated
+ JNI local reference to the object that was allocated.
- JNI local reference to the class of the object
+ JNI local reference to the class of the object.
@@ -13570,17 +13562,32 @@
-
+
- Sent when an object is sampled via the
+ Sent when an allocated object is sampled via the
Heap Sampling Monitoring system .
- The event is sent once the allocation has been done and provides the object, stack trace
+
+ By default, the sampling rate used is geometric variable with a 512kb mean, meaning one object every 512k bytes,
+ in average, per thread will
+ provoke a callback. Each thread having its own internal allocation count, each thread will be sampling
+ at the same default rate of 512kb.
+
+ If another sampling rate is required, the user can call
+ with a strictly positive integer value, representing
+ the new sampling rate to be used by the default sampler.
+
+ This event is sent once the sampled allocation has been done and provides the object, stack trace
for the allocation, the thread allocating, the size of allocation, and class.
+
+ Typical use cases of this system is to determine where most heap allocation are originating from.
+ Using this in conjunction of weak references and the function
+ , a user can track which objects were allocated from which
+ stacktrace and which are still live during the execution of the program.
new
-
+
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -538,6 +538,14 @@
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
record_class_file_load_hook_enabled();
}
+
+ if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) {
+ if (enabled) {
+ ThreadHeapSampler::enable();
+ } else {
+ ThreadHeapSampler::disable();
+ }
+ }
JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
} else {
// We have a specified event_thread.
@@ -569,6 +577,14 @@
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
record_class_file_load_hook_enabled();
}
+
+ if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) {
+ if (enabled) {
+ ThreadHeapSampler::enable();
+ } else {
+ ThreadHeapSampler::disable();
+ }
+ }
JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
}
@@ -3633,10 +3649,13 @@
} /* end GetAvailableProcessors */
jvmtiError
-JvmtiEnv::SetTlabHeapSampling(jint monitoring_rate) {
- ThreadHeapSampler::set_tlab_heap_sampling(monitoring_rate);
+JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) {
+ if (sampling_rate < 0) {
+ return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+ }
+ ThreadHeapSampler::set_tlab_heap_sampling(sampling_rate);
return JVMTI_ERROR_NONE;
-} /* end SetTlabHeapSampling */
+} /* end SetHeapSamplingRate */
//
// System Properties functions
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -2900,7 +2900,6 @@
JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() {
if (JvmtiExport::should_post_sampled_object_alloc()) {
_enable = true;
- _callback_for_all_oops = true;
_post_callback = JvmtiExport::post_sampled_object_alloc;
setup_jvmti_thread_state();
}
diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp
--- a/src/hotspot/share/prims/jvmtiExport.hpp
+++ b/src/hotspot/share/prims/jvmtiExport.hpp
@@ -501,7 +501,6 @@
// collection of objects allocated while running java code inside
// agent post_X_object_alloc() event handler.
void (*_post_callback)(JavaThread*, oop); // what callback to use when destroying the collector.
- bool _callback_for_all_oops;
//GC support
void oops_do(OopClosure* f);
diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
+++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
@@ -141,8 +141,7 @@
jc.can_generate_field_modification_events = 1;
jc.can_generate_field_access_events = 1;
jc.can_generate_breakpoint_events = 1;
- jc.can_sample_heap = 1;
- jc.can_generate_sampled_object_alloc_events = 1;
+ jc.can_generate_sampled_alloc_events = 1;
return jc;
}
@@ -412,10 +411,8 @@
log_trace(jvmti)("can_generate_frame_pop_events");
if (cap->can_generate_breakpoint_events)
log_trace(jvmti)("can_generate_breakpoint_events");
- if (cap->can_sample_heap)
- log_trace(jvmti)("can_sample_heap");
- if (cap->can_generate_sampled_object_alloc_events)
- log_trace(jvmti)("can_generate_sampled_object_alloc_events");
+ if (cap->can_generate_sampled_alloc_events)
+ log_trace(jvmti)("can_generate_sampled_alloc_events");
if (cap->can_suspend)
log_trace(jvmti)("can_suspend");
if (cap->can_redefine_any_class )
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -126,6 +126,8 @@
Monitor* PeriodicTask_lock = NULL;
Monitor* RedefineClasses_lock = NULL;
+Monitor* ThreadHeapSampler_lock = NULL;
+
#if INCLUDE_TRACE
Mutex* JfrStacktrace_lock = NULL;
Monitor* JfrMsg_lock = NULL;
@@ -286,6 +288,9 @@
def(CompileThread_lock , PaddedMonitor, nonleaf+5, false, Monitor::_safepoint_check_always);
def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes);
def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always);
+
+ def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_never);
+
if (WhiteBoxAPI) {
def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never);
}
diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -124,6 +124,7 @@
extern Monitor* Service_lock; // a lock used for service thread operation
extern Monitor* PeriodicTask_lock; // protects the periodic task structure
extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition
+extern Monitor* ThreadHeapSampler_lock; // protects the static data for initialization.
#if INCLUDE_TRACE
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp
new file mode 100644
--- /dev/null
+++ b/src/hotspot/share/runtime/threadHeapSampler.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/threadHeapSampler.hpp"
+
+// Cheap random number generator
+uint64_t ThreadHeapSampler::_rnd;
+// Default is 512kb.
+int ThreadHeapSampler::_sampling_rate = 512 * 1024;
+int ThreadHeapSampler::_enabled;
+
+// Statics for the fast log
+static const int FastLogNumBits = 10;
+static const int FastLogMask = (1 << FastLogNumBits) - 1;
+static double log_table[1<0, "bad value passed to assert");
+ uint64_t x = 0;
+ memcpy(&x, &d, sizeof(uint64_t));
+ const uint32_t x_high = x >> 32;
+ const uint32_t y = x_high >> (20 - FastLogNumBits) & FastLogMask;
+ const int32_t exponent = ((x_high >> 20) & 0x7FF) - 1023;
+ return exponent + log_table[y];
+}
+
+// Generates a geometric variable with the specified mean (512K by default).
+// This is done by generating a random number between 0 and 1 and applying
+// the inverse cumulative distribution function for an exponential.
+// Specifically: Let m be the inverse of the sample rate, then
+// the probability distribution function is m*exp(-mx) so the CDF is
+// p = 1 - exp(-mx), so
+// q = 1 - p = exp(-mx)
+// log_e(q) = -mx
+// -log_e(q)/m = x
+// log_2(q) * (-log_e(2) * 1/m) = x
+// In the code, q is actually in the range 1 to 2**26, hence the -26 below
+void ThreadHeapSampler::pick_next_geometric_sample() {
+ _rnd = next_random(_rnd);
+ // Take the top 26 bits as the random number
+ // (This plus a 1<<58 sampling bound gives a max possible step of
+ // 5194297183973780480 bytes. In this case,
+ // for sample_parameter = 1<<19, max possible step is
+ // 9448372 bytes (24 bits).
+ const uint64_t PrngModPower = 48; // Number of bits in prng
+ // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
+ // under piii debug for some binaries.
+ double q = static_cast(_rnd >> (PrngModPower - 26)) + 1.0;
+ // Put the computed p-value through the CDF of a geometric.
+ // For faster performance (save ~1/20th exec time), replace
+ // min(0.0, FastLog2(q) - 26) by (Fastlog2(q) - 26.000705)
+ // The value 26.000705 is used rather than 26 to compensate
+ // for inaccuracies in FastLog2 which otherwise result in a
+ // negative answer.
+ double log_val = (fast_log2(q) - 26);
+ size_t rate = static_cast(
+ (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (_sampling_rate)) + 1);
+ _bytes_until_sample = rate;
+}
+
+void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) {
+ if (_sampling_rate == 1) {
+ _bytes_until_sample = 1;
+ return;
+ }
+
+ pick_next_geometric_sample();
+
+ // Try to correct sample size by removing extra space from last allocation.
+ if (overflowed_bytes > 0 && _bytes_until_sample > overflowed_bytes) {
+ _bytes_until_sample -= overflowed_bytes;
+ }
+}
+
+void ThreadHeapSampler::check_for_sampling(HeapWord* ptr, size_t allocation_size, size_t bytes_since_allocation) {
+ oopDesc* oop = reinterpret_cast(ptr);
+ size_t total_allocated_bytes = bytes_since_allocation + allocation_size;
+
+ // If not yet time for a sample, skip it.
+ if (total_allocated_bytes < _bytes_until_sample) {
+ _bytes_until_sample -= total_allocated_bytes;
+ return;
+ }
+
+ JvmtiExport::sampled_object_alloc_event_collector(oop);
+
+ size_t overflow_bytes = total_allocated_bytes - _bytes_until_sample;
+ pick_next_sample(overflow_bytes);
+}
+
+void ThreadHeapSampler::init_log_table() {
+ MutexLocker mu(ThreadHeapSampler_lock);
+
+ if (log_table_initialized) {
+ return;
+ }
+
+ for (int i = 0; i < (1 << FastLogNumBits); i++) {
+ log_table[i] = (log(1.0 + static_cast(i+0.5) / (1 << FastLogNumBits))
+ / log(2.0));
+ }
+
+ log_table_initialized = true;
+}
+
+void ThreadHeapSampler::set_tlab_heap_sampling(int sampling_rate) {
+ MutexLocker mu(ThreadHeapSampler_lock);
+ _sampling_rate = sampling_rate;
+}
diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp
new file mode 100644
--- /dev/null
+++ b/src/hotspot/share/runtime/threadHeapSampler.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef RUNTIME_THREADHEAPSAMPLER_HPP
+#define RUNTIME_THREADHEAPSAMPLER_HPP
+
+#include "memory/allocation.hpp"
+
+class ThreadHeapSampler {
+ private:
+ size_t _bytes_until_sample;
+ Thread* _thread;
+ // Cheap random number generator
+ static uint64_t _rnd;
+
+ void pick_next_geometric_sample();
+ void pick_next_sample(size_t overflowed_bytes = 0);
+ static int _enabled;
+ static int _sampling_rate;
+
+ void init_log_table();
+
+ public:
+ ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) {
+ _rnd = static_cast(reinterpret_cast(this));
+ if (_rnd == 0) {
+ _rnd = 1;
+ }
+ init_log_table();
+ }
+
+ void set_thread(Thread* t) { _thread = t; }
+
+ size_t bytes_until_sample() { return _bytes_until_sample; }
+ void set_bytes_until_sample(size_t bytes) { _bytes_until_sample = bytes; }
+
+ void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0);
+
+ static int enabled() { return OrderAccess::load_acquire(&_enabled); }
+ static void enable() { OrderAccess::release_store(&_enabled, 1); }
+ static void disable() { OrderAccess::release_store(&_enabled, 0); }
+ static void set_tlab_heap_sampling(int sampling_rate);
+};
+
+#endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/Frame.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+class Frame {
+ Frame(String method, String signature, String fileName, int lineNumber) {
+ this.method = method;
+ this.signature = signature;
+ this.fileName = fileName;
+ this.lineNumber = lineNumber;
+ }
+
+ public String method;
+ public String signature;
+ public String fileName;
+ public int lineNumber;
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** API for handling the underlying heap sampling monitoring system. */
+public class HeapMonitor {
+ private static int[][] arrays;
+
+ static {
+ try {
+ System.loadLibrary("HeapMonitor");
+ } catch (UnsatisfiedLinkError ule) {
+ System.err.println("Could not load HeapMonitor library");
+ System.err.println("java.library.path: " + System.getProperty("java.library.path"));
+ throw ule;
+ }
+ }
+
+ /** Set a specific sampling rate, 0 samples every allocation. */
+ public native static void setSamplingRate(int rate);
+
+ public native static void enableSamplingEvents();
+ public native static void disableSamplingEvents();
+
+ /**
+ * Allocate memory but first create a stack trace of a particular depth.
+ *
+ * @return list of frames for the allocation.
+ */
+ public static List allocate(int depth) {
+ List frames = new ArrayList();
+ if (depth > 1) {
+ createStackDepth(depth - 1, frames);
+ frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 57));
+ } else {
+ actuallyAllocate();
+ frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
+ frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 60));
+ }
+ return frames;
+ }
+
+ /**
+ * Allocate memory but first create a stack trace.
+ *
+ * @return list of frames for the allocation.
+ */
+ public static List allocate() {
+ int sum = 0;
+ List frames = new ArrayList();
+ allocate(frames);
+ frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 75));
+ return frames;
+ }
+
+ private static void createStackDepth(int depth, List frames) {
+ if (depth > 1) {
+ createStackDepth(depth - 1, frames);
+ frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 82));
+ } else {
+ allocate(frames);
+ frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 85));
+ }
+ }
+
+ private static void allocate(List frames) {
+ int sum = 0;
+ for (int j = 0; j < 1000; j++) {
+ sum += actuallyAllocate();
+ }
+ frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
+ frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 93));
+ }
+
+ public static List repeatAllocate(int max) {
+ List frames = null;
+ for (int i = 0; i < max; i++) {
+ frames = allocate();
+ }
+ frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 102));
+ return frames;
+ }
+
+ private static int actuallyAllocate() {
+ int sum = 0;
+
+ // Let us assume that a 1-element array is 24 bytes of memory and we want
+ // 2MB allocated.
+ int iterations = (1 << 19) / 6;
+
+ if (arrays == null) {
+ arrays = new int[iterations][];
+ }
+
+ for (int i = 0; i < iterations; i++) {
+ int tmp[] = new int[1];
+ // Force it to be kept and, at the same time, wipe out any previous data.
+ arrays[i] = tmp;
+ sum += arrays[0][0];
+ }
+ return sum;
+ }
+
+ public static int allocateSize(int totalSize) {
+ int sum = 0;
+
+ // Let us assume that a 1-element array is 24 bytes.
+ int iterations = totalSize / 24;
+
+ if (arrays == null) {
+ arrays = new int[iterations][];
+ }
+
+ System.out.println("Allocating for " + iterations);
+ for (int i = 0; i < iterations; i++) {
+ int tmp[] = new int[1];
+
+ // Force it to be kept and, at the same time, wipe out any previous data.
+ arrays[i] = tmp;
+ sum += arrays[0][0];
+ }
+
+ return sum;
+ }
+
+ /** Remove the reference to the global array to free data at the next GC. */
+ public static void freeStorage() {
+ arrays = null;
+ }
+
+ public native static boolean obtainedEvents(Frame[] frames);
+ public native static boolean garbageContains(Frame[] frames);
+ public native static boolean eventStorageIsEmpty();
+ public native static void resetEventStorage();
+ public native static int getEventStorageElementCount();
+ public native static void forceGarbageCollection();
+
+ public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
+ double actual = getEventStorageElementCount();
+ double diffPercentage = Math.abs(actual - expected) / expected;
+ return diffPercentage < acceptedErrorPercentage;
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
+ * @compile HeapMonitorArrayAllSampledTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorArrayAllSampledTest
+ */
+
+public class HeapMonitorArrayAllSampledTest {
+
+ // Do 1000 iterations and expect maxIteration samples.
+ private static final int maxIteration = 1000;
+ private static int array[];
+
+ private static void allocate(int size) {
+ for (int j = 0; j < maxIteration; j++) {
+ array = new int[size];
+ }
+ }
+
+ public static void main(String[] args) {
+ int sizes[] = {1000, 10000, 100000, 1000000};
+
+ HeapMonitor.setSamplingRate(0);
+ HeapMonitor.enableSamplingEvents();
+
+ for (int currentSize : sizes) {
+ System.out.println("Testing size " + currentSize);
+
+ HeapMonitor.resetEventStorage();
+ allocate(currentSize);
+
+ // 10% error ensures a sanity test without becoming flaky.
+ if (!HeapMonitor.statsHaveExpectedNumberSamples(maxIteration, 10)) {
+ throw new RuntimeException("Statistics should show about " + maxIteration + " samples.");
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies if turning off the event notification stops events.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorEventOnOffTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorEventOnOffTest
+ */
+public class HeapMonitorEventOnOffTest {
+ private static void checkNoEventsAreBeingSent() {
+ HeapMonitor.resetEventStorage();
+ HeapMonitor.repeatAllocate(10);
+
+ // Check that the data is available while heap sampling is enabled.
+ boolean status = HeapMonitor.eventStorageIsEmpty();
+ if (!status) {
+ throw new RuntimeException("Failed to find the traces before the wipe out.");
+ }
+ }
+
+ private static void checkEventsAreBeingSent() {
+ List frameList = HeapMonitor.repeatAllocate(10);
+
+ frameList.add(new Frame("checkEventsAreBeingSent", "()V", "HeapMonitorEventOnOffTest.java", 48));
+ Frame[] frames = frameList.toArray(new Frame[0]);
+
+ // Check that the data is available while heap sampling is enabled.
+ boolean status = HeapMonitor.obtainedEvents(frames);
+ if (!status) {
+ throw new RuntimeException("Failed to find the traces before the wipe out.");
+ }
+ }
+
+ public static void main(String[] args) {
+ HeapMonitor.enableSamplingEvents();
+ checkEventsAreBeingSent();
+
+ // Disabling the notification system should stop events.
+ HeapMonitor.disableSamplingEvents();
+ checkNoEventsAreBeingSent();
+
+ // Enabling the notification system should start events again.
+ HeapMonitor.enableSamplingEvents();
+ checkEventsAreBeingSent();
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using CMS GC
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorGCCMSTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using ParallelGc
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorGCTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseParallelGC MyPackage.HeapMonitorGCTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor Statistics using SerialGC
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorGCTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseSerialGC MyPackage.HeapMonitorGCTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the default GC with the Heap Monitor event system.
+ * @compile HeapMonitorGCTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorGCTest
+ */
+
+/**
+ * This test is checking that various GCs work as intended: events are sent, forcing GC works, etc.
+ */
+public class HeapMonitorGCTest {
+ public static void main(String[] args) {
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Statistics should be null to begin with.");
+ }
+
+ HeapMonitor.enableSamplingEvents();
+
+ List frameList = HeapMonitor.allocate();
+ frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorGCTest.java", 47));
+ Frame[] frames = frameList.toArray(new Frame[0]);
+
+ if (!HeapMonitor.obtainedEvents(frames)) {
+ throw new RuntimeException("No expected events were found.");
+ }
+
+ HeapMonitor.forceGarbageCollection();
+
+ if (!HeapMonitor.garbageContains(frames)) {
+ throw new RuntimeException("Forcing GC did not work, not a single object was collected.");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI SetHeapSamplingRate returns an illegal argument for negative ints.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorIllegalArgumentTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorIllegalArgumentTest
+ */
+
+public class HeapMonitorIllegalArgumentTest {
+ private native static int testIllegalArgument();
+
+ public static void main(String[] args) {
+ int result = testIllegalArgument();
+
+ if (result == 0) {
+ throw new RuntimeException("Test illegal argument failed.");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor using the interpreter.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor using the interpreter.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorStatObjectCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest
+ */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor does not work without the required capability.
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorNoCapabilityTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorNoCapabilityTest
+ */
+
+public class HeapMonitorNoCapabilityTest {
+ private native static int allSamplingMethodsFail();
+
+ public static void main(String[] args) {
+ int result = allSamplingMethodsFail();
+
+ if (result == 0) {
+ throw new RuntimeException("Some methods could be called without a capability.");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
+ * @compile HeapMonitorStatArrayCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatArrayCorrectnessTest
+ */
+
+public class HeapMonitorStatArrayCorrectnessTest {
+
+ // Do 100000 iterations and expect maxIteration / multiplier samples.
+ private static final int maxIteration = 100000;
+ private static int array[];
+
+ private static void allocate(int size) {
+ for (int j = 0; j < maxIteration; j++) {
+ array = new int[size];
+ }
+ }
+
+ public static void main(String[] args) {
+ int sizes[] = {1000, 10000, 100000};
+
+ HeapMonitor.enableSamplingEvents();
+
+ for (int currentSize : sizes) {
+ System.out.println("Testing size " + currentSize);
+
+ HeapMonitor.resetEventStorage();
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Should not have any events stored yet.");
+ }
+
+ // 111 is as good a number as any.
+ final int samplingMultiplier = 111;
+ HeapMonitor.setSamplingRate(samplingMultiplier * currentSize);
+
+ allocate(currentSize);
+
+ // For simplifications, we ignore the array memory usage for array internals (with the array
+ // sizes requested, it should be a negligible oversight).
+ //
+ // That means that with maxIterations, the loop in the method allocate requests:
+ // maxIterations * currentSize * 4 bytes (4 for integers)
+ //
+ // Via the enable sampling, the code requests a sample every samplingMultiplier * currentSize bytes.
+ //
+ // Therefore, the expected sample number is:
+ // (maxIterations * currentSize * 4) / (samplingMultiplier * currentSize);
+ double expected = maxIteration;
+ expected *= 4;
+ expected /= samplingMultiplier;
+
+ // 10% error ensures a sanity test without becoming flaky.
+ if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+ throw new RuntimeException("Statistics should show about " + expected + " samples.");
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor sampling via object allocation.
+ * @compile HeapMonitorStatObjectCorrectnessTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatObjectCorrectnessTest
+ */
+
+/** This test is checking the object allocation path works with heap sampling. */
+public class HeapMonitorStatObjectCorrectnessTest {
+
+ // Do 200000 iterations and expect maxIteration / multiplier samples.
+ private static final int maxIteration = 200000;
+ private static BigObject obj;
+
+ private native static boolean statsHaveExpectedNumberSamples(int expected, int percentError);
+
+ private static void allocate() {
+ for (int j = 0; j < maxIteration; j++) {
+ obj = new BigObject();
+ }
+ }
+
+ private static void testBigAllocationRate() {
+ final int sizeObject = 1400;
+
+ // 111 is as good a number as any.
+ final int samplingMultiplier = 111;
+ HeapMonitor.setSamplingRate(samplingMultiplier * sizeObject);
+
+ emptyStorage();
+ allocate();
+
+ // For simplifications, the code is allocating:
+ // (BigObject size) * maxIteration.
+ //
+ // We ignore the class memory usage apart from field memory usage for BigObject. BigObject
+ // allocates 250 long, so 2000 bytes, so whatever is used for the class is negligible.
+ //
+ // That means that with maxIterations, the loop in the method allocate requests:
+ // maxIterations * 2000 bytes.
+ //
+ // Via the enable sampling, the code requests a sample every samplingMultiplier * sizeObject bytes.
+ //
+ // Therefore, the expected sample number is:
+ // (maxIterations * sizeObject) / (samplingMultiplier * sizeObject);
+ //
+ // Which becomes:
+ // maxIterations / samplingMultiplier
+ double expected = maxIteration;
+ expected /= samplingMultiplier;
+
+ // 10% error ensures a sanity test without becoming flaky.
+ if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+ throw new RuntimeException("Statistics should show about " + expected + " samples.");
+ }
+ }
+
+ private static void emptyStorage() {
+ HeapMonitor.resetEventStorage();
+
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Statistics should be null to begin with.");
+ }
+ }
+
+ private static void testEveryAllocationSampled() {
+ // 0 means sample every allocation.
+ HeapMonitor.setSamplingRate(0);
+
+ emptyStorage();
+ allocate();
+
+ double expected = maxIteration;
+
+ // 10% error ensures a sanity test without becoming flaky.
+ if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) {
+ throw new RuntimeException("Statistics should show about " + expected + " samples.");
+ }
+ }
+
+ public static void main(String[] args) {
+ HeapMonitor.enableSamplingEvents();
+
+ testBigAllocationRate();
+ testEveryAllocationSampled();
+ }
+
+ /**
+ * Big class on purpose to just be able to ignore the class memory space overhead.
+ *
+ * Class contains 175 long fields, so 175 * 8 = 1400 bytes.
+ */
+ private static class BigObject {
+ private long a0_0, a0_1, a0_2, a0_3, a0_4, a0_5, a0_6, a0_7, a0_8, a0_9;
+ private long a1_0, a1_1, a1_2, a1_3, a1_4, a1_5, a1_6, a1_7, a1_8, a1_9;
+ private long a2_0, a2_1, a2_2, a2_3, a2_4, a2_5, a2_6, a2_7, a2_8, a2_9;
+ private long a3_0, a3_1, a3_2, a3_3, a3_4, a3_5, a3_6, a3_7, a3_8, a3_9;
+ private long a4_0, a4_1, a4_2, a4_3, a4_4, a4_5, a4_6, a4_7, a4_8, a4_9;
+ private long a5_0, a5_1, a5_2, a5_3, a5_4, a5_5, a5_6, a5_7, a5_8, a5_9;
+ private long a6_0, a6_1, a6_2, a6_3, a6_4, a6_5, a6_6, a6_7, a6_8, a6_9;
+ private long a7_0, a7_1, a7_2, a7_3, a7_4, a7_5, a7_6, a7_7, a7_8, a7_9;
+ private long a8_0, a8_1, a8_2, a8_3, a8_4, a8_5, a8_6, a8_7, a8_8, a8_9;
+ private long a9_0, a9_1, a9_2, a9_3, a9_4, a9_5, a9_6, a9_7, a9_8, a9_9;
+ private long a10_0, a10_1, a10_2, a10_3, a10_4, a10_5, a10_6, a10_7, a10_8, a10_9;
+ private long a11_0, a11_1, a11_2, a11_3, a11_4, a11_5, a11_6, a11_7, a11_8, a11_9;
+ private long a12_0, a12_1, a12_2, a12_3, a12_4, a12_5, a12_6, a12_7, a12_8, a12_9;
+ private long a13_0, a13_1, a13_2, a13_3, a13_4, a13_5, a13_6, a13_7, a13_8, a13_9;
+ private long a14_0, a14_1, a14_2, a14_3, a14_4, a14_5, a14_6, a14_7, a14_8, a14_9;
+ private long a15_0, a15_1, a15_2, a15_3, a15_4, a15_5, a15_6, a15_7, a15_8, a15_9;
+ private long a16_0, a16_1, a16_2, a16_3, a16_4, a16_5, a16_6, a16_7, a16_8, a16_9;
+ private long a17_0, a17_1, a17_2, a17_3, a17_4;
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor sampling rate average
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorStatRateTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatRateTest
+ */
+
+public class HeapMonitorStatRateTest {
+
+ private native static double getAverageRate();
+
+ public static void main(String[] args) {
+ int[] tab = {1024, 16384, 524288};
+
+ for (int rateIdx = 0; rateIdx < tab.length; rateIdx++) {
+ int rate = tab[rateIdx];
+
+ HeapMonitor.resetEventStorage();
+ HeapMonitor.setSamplingRate(rate);
+
+ HeapMonitor.enableSamplingEvents();
+
+ int allocationTotal = 512 * 1024 * 1024;
+ HeapMonitor.allocateSize(allocationTotal);
+
+ HeapMonitor.disableSamplingEvents();
+
+ double actualCount = HeapMonitor.getEventStorageElementCount();
+ double expectedCount = allocationTotal / rate;
+
+ double error = Math.abs(actualCount - expectedCount);
+ double errorPercentage = error / expectedCount * 100;
+
+ if (errorPercentage > 10) {
+ throw new RuntimeException("Rate average over 10% for rate " + rate + " -> " + actualCount
+ + ", " + expectedCount);
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor events are only sent after enabling.
+ * @compile HeapMonitorStatSimpleTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatSimpleTest
+ */
+
+public class HeapMonitorStatSimpleTest {
+ private native static int areSamplingStatisticsZero();
+
+ public static void main(String[] args) {
+ HeapMonitor.allocate();
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Statistics should be null to begin with.");
+ }
+
+ HeapMonitor.enableSamplingEvents();
+ HeapMonitor.allocate();
+
+ if (HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Statistics should not be null now.");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.List;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI Heap Monitor API
+ * @build Frame HeapMonitor
+ * @compile HeapMonitorTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorTest
+ */
+
+public class HeapMonitorTest {
+
+ private static native boolean framesAreNotLive(Frame[] frames);
+
+ public static void main(String[] args) {
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Storage is not empty at test start...");
+ }
+
+ HeapMonitor.enableSamplingEvents();
+ List frameList = HeapMonitor.allocate();
+ frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 46));
+
+ Frame[] frames = frameList.toArray(new Frame[0]);
+ if (!HeapMonitor.obtainedEvents(frames)) {
+ throw new RuntimeException("Events not found with the right frames.");
+ }
+
+ HeapMonitor.disableSamplingEvents();
+ HeapMonitor.resetEventStorage();
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Storage is not empty after reset.");
+ }
+
+ HeapMonitor.allocate();
+ if (!HeapMonitor.eventStorageIsEmpty()) {
+ throw new RuntimeException("Storage is not empty after allocation while disabled.");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor Thread sanity
+ * @compile HeapMonitorThreadOnOffTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadOnOffTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HeapMonitorThreadOnOffTest {
+ public static void main(String[] args) {
+ final int numThreads = 24;
+ ArrayList list = new ArrayList<>();
+
+ // Add one thread that consistently turns on/off the sampler to ensure correctness with
+ // potential resets.
+ Switch switchPlayer = new Switch();
+ Thread switchThread = new Thread(switchPlayer, "Switch Player");
+ switchThread.start();
+
+ for (int i = 0 ; i < numThreads; i++) {
+ Thread thread = new Thread(new Allocator(i), "Allocator" + i);
+ thread.start();
+ list.add(thread);
+ }
+
+ for (Thread elem : list) {
+ try {
+ elem.join();
+ } catch(InterruptedException e) {
+ throw new RuntimeException("Thread got interrupted...");
+ }
+ }
+
+ switchPlayer.stop();
+ try {
+ switchThread.join();
+ } catch(InterruptedException e) {
+ throw new RuntimeException("Thread got interrupted while waiting for the switch player...");
+ }
+
+ // We don't check here for correctness of data. If we made it here, the test succeeded:
+ // Threads can allocate like crazy
+ // Other threads can turn on/off the system
+ }
+}
+
+class Allocator implements Runnable {
+ private int depth;
+ private volatile int tmp[];
+
+ public Allocator(int depth) {
+ this.depth = depth;
+ }
+
+ private int helper() {
+ int sum = 0;
+ // Let us assume that the array is 24 bytes of memory.
+ for (int i = 0; i < 127000 / 6; i++) {
+ int newTmp[] = new int[1];
+ // Force it to be kept.
+ tmp = newTmp;
+ sum += tmp[0];
+ }
+ return sum;
+ }
+
+ private int recursiveWrapper(int depth) {
+ if (depth > 0) {
+ return recursiveWrapper(depth - 1);
+ }
+ return helper();
+ }
+
+ public void run() {
+ int sum = 0;
+ for (int j = 0; j < 500; j++) {
+ sum += recursiveWrapper(depth);
+ }
+ }
+}
+
+class Switch implements Runnable {
+ private volatile boolean keepGoing;
+
+ public Switch() {
+ keepGoing = true;
+ }
+
+ public void stop() {
+ keepGoing = false;
+ }
+
+ public void run() {
+ while (keepGoing) {
+ HeapMonitor.disableSamplingEvents();
+ HeapMonitor.resetEventStorage();
+ HeapMonitor.enableSamplingEvents();
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor
+ * @summary Verifies the JVMTI Heap Monitor Thread information sanity
+ * @compile HeapMonitorThreadTest.java
+ * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class HeapMonitorThreadTest {
+ private native static boolean checkSamples(int numThreads);
+
+ public static void main(String[] args) {
+ final int numThreads = 24;
+ ArrayList threadList = new ArrayList<>();
+
+ HeapMonitor.enableSamplingEvents();
+
+ // The test creates numThreads threads, each allocate a lot. Then they inform the main thread
+ // they are done allocating. The test looks if all threads are available in the collected event
+ // information and then proceeds to tell each thread to stop running before leaving the test.
+ for (int i = 0 ; i < numThreads; i++) {
+ BlockingQueue
+ id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" filtered="thread" since="11">
Sent when an allocated object is sampled via the
Heap Sampling Monitoring system .
diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp
--- a/src/hotspot/share/prims/jvmtiEventController.cpp
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp
@@ -96,7 +96,8 @@
static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT |
FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS |
- BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT;
+ BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT |
+ SAMPLED_OBJECT_ALLOC_BIT;
static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT;
static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT |
VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT |
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -2284,7 +2284,7 @@
// Can not take safepoint here so can not use state_for to get
// jvmti thread state.
JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state();
- if (state != NULL ) {
+ if (state != NULL) {
// state is non NULL when VMObjectAllocEventCollector is enabled.
JvmtiVMObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector();
@@ -2308,10 +2308,11 @@
// Can not take safepoint here so can not use state_for to get
// jvmti thread state.
JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state();
- if (state != NULL ) {
+ if (state != NULL) {
// state is non NULL when SampledObjectAllocEventCollector is enabled.
JvmtiSampledObjectAllocEventCollector *collector;
collector = state->get_sampled_object_alloc_event_collector();
+
if (collector != NULL && collector->is_enabled()) {
collector->record_allocation(obj);
}
@@ -2508,7 +2509,7 @@
}
}
-void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
+void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
if (object == NULL) {
@@ -2534,7 +2535,7 @@
}
}
-void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
+void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Trg sampled object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
@@ -2543,19 +2544,25 @@
return;
}
+ JvmtiThreadState *state = thread->jvmti_thread_state();
+ if (state == NULL) {
+ return;
+ }
+
HandleMark hm(thread);
Handle h(thread, object);
- JvmtiEnvIterator it;
- for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
- if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
+ JvmtiEnvThreadStateIterator it(state);
+ for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
+ if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Evt sampled object alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
object == NULL ? "NULL" : object->klass()->external_name()));
JvmtiObjectAllocEventMark jem(thread, h());
- JvmtiJavaThreadEventTransition jet(thread);
+ JvmtiEnv *env = ets->get_env();
+ JvmtiThreadEventTransition jet(thread);
jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
@@ -2820,7 +2827,7 @@
// GC support.
void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) {
if (_allocated != NULL) {
- for(int i=_allocated->length() - 1; i >= 0; i--) {
+ for(int i = _allocated->length() - 1; i >= 0; i--) {
if (_allocated->at(i) != NULL) {
f->do_oop(_allocated->adr_at(i));
}
@@ -2903,11 +2910,21 @@
_post_callback = JvmtiExport::post_sampled_object_alloc;
setup_jvmti_thread_state();
}
+ // Set the sampling collector as present in assertion mode only.
+ assert(JavaThread::current()->heap_sampler().add_sampling_collector(),
+ "Should never return false.");
}
JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() {
- generate_call_for_allocated();
+ if (JvmtiExport::should_post_sampled_object_alloc()) {
+ generate_call_for_allocated();
+ }
+ // Note: we set ourselves in the thread state only if we should post but
+ // always unset in case the user has disabled the posting but we were set.
unset_jvmti_thread_state();
+ // Set the sampling collector as present in assertion mode only.
+ assert(JavaThread::current()->heap_sampler().remove_sampling_collector(),
+ "Should never return false.");
}
JvmtiGCMarker::JvmtiGCMarker() {
diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
+++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
@@ -130,6 +130,7 @@
memset(&jc, 0, sizeof(jc));
jc.can_suspend = 1;
+ jc.can_generate_sampled_alloc_events = 1;
return jc;
}
@@ -141,7 +142,6 @@
jc.can_generate_field_modification_events = 1;
jc.can_generate_field_access_events = 1;
jc.can_generate_breakpoint_events = 1;
- jc.can_generate_sampled_alloc_events = 1;
return jc;
}
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -289,7 +289,7 @@
def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes);
def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always);
- def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_never);
+ def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_sometimes);
if (WhiteBoxAPI) {
def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never);
diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp
--- a/src/hotspot/share/runtime/threadHeapSampler.cpp
+++ b/src/hotspot/share/runtime/threadHeapSampler.cpp
@@ -141,3 +141,24 @@
MutexLocker mu(ThreadHeapSampler_lock);
_sampling_rate = sampling_rate;
}
+
+bool ThreadHeapSampler::sampling_collector_present() const {
+ return _collectors_present > 0;
+}
+
+bool ThreadHeapSampler::remove_sampling_collector() {
+ _collectors_present--;
+ return true;
+}
+
+bool ThreadHeapSampler::add_sampling_collector() {
+ _collectors_present++;
+ return true;
+}
+
+void ThreadHeapSampler::enable() {
+ // Done here to be done when things have settled. This adds a mutex lock but
+ // presumably, users won't be enabling and disabling all the time.
+ init_log_table();
+ OrderAccess::release_store(&_enabled, 1);
+}
diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp
--- a/src/hotspot/share/runtime/threadHeapSampler.hpp
+++ b/src/hotspot/share/runtime/threadHeapSampler.hpp
@@ -39,7 +39,11 @@
static int _enabled;
static int _sampling_rate;
- void init_log_table();
+ // Used for assertion mode to determine if there is a path to a TLAB slow path
+ // without a collector present.
+ int _collectors_present;
+
+ static void init_log_table();
public:
ThreadHeapSampler() : _bytes_until_sample(0), _thread(NULL) {
@@ -47,7 +51,8 @@
if (_rnd == 0) {
_rnd = 1;
}
- init_log_table();
+
+ _collectors_present = 0;
}
void set_thread(Thread* t) { _thread = t; }
@@ -58,9 +63,13 @@
void check_for_sampling(HeapWord* obj, size_t size_in_bytes, size_t bytes_allocated_before = 0);
static int enabled() { return OrderAccess::load_acquire(&_enabled); }
- static void enable() { OrderAccess::release_store(&_enabled, 1); }
+ static void enable();
static void disable() { OrderAccess::release_store(&_enabled, 0); }
static void set_tlab_heap_sampling(int sampling_rate);
+
+ bool sampling_collector_present() const;
+ bool remove_sampling_collector();
+ bool add_sampling_collector();
};
#endif // SHARE_RUNTIME_THREADHEAPSAMPLER_HPP
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
@@ -29,10 +29,11 @@
/** API for handling the underlying heap sampling monitoring system. */
public class HeapMonitor {
private static int[][] arrays;
+ private static int allocationIterations = 1000;
static {
try {
- System.loadLibrary("HeapMonitor");
+ System.loadLibrary("HeapMonitorTest");
} catch (UnsatisfiedLinkError ule) {
System.err.println("Could not load HeapMonitor library");
System.err.println("java.library.path: " + System.getProperty("java.library.path"));
@@ -42,8 +43,8 @@
/** Set a specific sampling rate, 0 samples every allocation. */
public native static void setSamplingRate(int rate);
-
public native static void enableSamplingEvents();
+ public native static void enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
public native static void disableSamplingEvents();
/**
@@ -55,11 +56,11 @@
List frames = new ArrayList();
if (depth > 1) {
createStackDepth(depth - 1, frames);
- frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 57));
+ frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 58));
} else {
actuallyAllocate();
- frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
- frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 60));
+ frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 121));
+ frames.add(new Frame("allocate", "(I)Ljava/util/List;", "HeapMonitor.java", 61));
}
return frames;
}
@@ -73,27 +74,27 @@
int sum = 0;
List frames = new ArrayList();
allocate(frames);
- frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 75));
+ frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 76));
return frames;
}
private static void createStackDepth(int depth, List frames) {
if (depth > 1) {
createStackDepth(depth - 1, frames);
- frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 82));
+ frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 83));
} else {
allocate(frames);
- frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 85));
+ frames.add(new Frame("createStackDepth", "(ILjava/util/List;)V", "HeapMonitor.java", 86));
}
}
private static void allocate(List frames) {
int sum = 0;
- for (int j = 0; j < 1000; j++) {
+ for (int j = 0; j < allocationIterations; j++) {
sum += actuallyAllocate();
}
- frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 120));
- frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 93));
+ frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 121));
+ frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 94));
}
public static List repeatAllocate(int max) {
@@ -101,7 +102,7 @@
for (int i = 0; i < max; i++) {
frames = allocate();
}
- frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 102));
+ frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 103));
return frames;
}
@@ -164,4 +165,8 @@
double diffPercentage = Math.abs(actual - expected) / expected;
return diffPercentage < acceptedErrorPercentage;
}
+
+ public static void setAllocationIterations(int iterations) {
+ allocationIterations = iterations;
+ }
}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java
@@ -28,7 +28,7 @@
* @build Frame HeapMonitor
* @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
* @compile HeapMonitorArrayAllSampledTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorArrayAllSampledTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorArrayAllSampledTest
*/
public class HeapMonitorArrayAllSampledTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java
@@ -30,7 +30,7 @@
* @summary Verifies if turning off the event notification stops events.
* @build Frame HeapMonitor
* @compile HeapMonitorEventOnOffTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorEventOnOffTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventOnOffTest
*/
public class HeapMonitorEventOnOffTest {
private static void checkNoEventsAreBeingSent() {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @summary Verifies the JVMTI Heap Monitor for only two threads
+ * @compile HeapMonitorEventsForTwoThreadsTest.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest
+ */
+
+import java.util.List;
+
+public class HeapMonitorEventsForTwoThreadsTest {
+ private native static boolean checkSamples(String first, String second);
+
+ public static void main(String[] args) {
+ final int numThreads = 24;
+ List threadList = ThreadInformation.createThreadList(numThreads);
+
+ Thread firstThread = threadList.get(0).getThread();
+ Thread secondThread = threadList.get(1).getThread();
+ // HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread);
+
+ ThreadInformation.startThreads(threadList);
+ ThreadInformation.waitForThreads(threadList);
+
+ // Should only see events for the two threads.
+ if (!checkSamples(firstThread.getName(), secondThread.getName())) {
+ throw new RuntimeException("Problem with checkSamples...");
+ }
+
+ // Now inform each thread we are done and wait for them to be done.
+ ThreadInformation.stopThreads(threadList);
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCCMSTest.java
@@ -28,5 +28,5 @@
* @summary Verifies the JVMTI Heap Monitor Statistics using CMS GC
* @build Frame HeapMonitor
* @compile HeapMonitorGCCMSTest.java
- * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseConcMarkSweepGC MyPackage.HeapMonitorGCTest
*/
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java
@@ -28,5 +28,5 @@
* @summary Verifies the JVMTI Heap Monitor Statistics using ParallelGc
* @build Frame HeapMonitor
* @compile HeapMonitorGCTest.java
- * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseParallelGC MyPackage.HeapMonitorGCTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseParallelGC MyPackage.HeapMonitorGCTest
*/
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java
@@ -28,5 +28,5 @@
* @summary Verifies the JVMTI Heap Monitor Statistics using SerialGC
* @build Frame HeapMonitor
* @compile HeapMonitorGCTest.java
- * @run main/othervm/native -agentlib:HeapMonitor -XX:+UseSerialGC MyPackage.HeapMonitorGCTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest -XX:+UseSerialGC MyPackage.HeapMonitorGCTest
*/
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java
@@ -30,7 +30,7 @@
* @build Frame HeapMonitor
* @summary Verifies the default GC with the Heap Monitor event system.
* @compile HeapMonitorGCTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorGCTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorGCTest
*/
/**
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java
@@ -28,7 +28,7 @@
* @summary Verifies the JVMTI SetHeapSamplingRate returns an illegal argument for negative ints.
* @build Frame HeapMonitor
* @compile HeapMonitorIllegalArgumentTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorIllegalArgumentTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorIllegalArgumentTest
*/
public class HeapMonitorIllegalArgumentTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java
@@ -28,5 +28,5 @@
* @summary Verifies the JVMTI Heap Monitor using the interpreter.
* @build Frame HeapMonitor
* @compile HeapMonitorTest.java
- * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorTest 10
*/
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java
@@ -28,5 +28,5 @@
* @summary Verifies the JVMTI Heap Monitor using the interpreter.
* @build Frame HeapMonitor
* @compile HeapMonitorStatObjectCorrectnessTest.java
- * @run main/othervm/native -agentlib:HeapMonitor -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest -Xint MyPackage.HeapMonitorStatObjectCorrectnessTest
*/
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java
@@ -28,7 +28,7 @@
* @summary Verifies the JVMTI Heap Monitor does not work without the required capability.
* @build Frame HeapMonitor
* @compile HeapMonitorNoCapabilityTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorNoCapabilityTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorNoCapabilityTest
*/
public class HeapMonitorNoCapabilityTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java
@@ -28,7 +28,7 @@
* @build Frame HeapMonitor
* @summary Verifies the JVMTI Heap Monitor rate when allocating arrays.
* @compile HeapMonitorStatArrayCorrectnessTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatArrayCorrectnessTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatArrayCorrectnessTest
*/
public class HeapMonitorStatArrayCorrectnessTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java
@@ -28,7 +28,7 @@
* @build Frame HeapMonitor
* @summary Verifies the JVMTI Heap Monitor sampling via object allocation.
* @compile HeapMonitorStatObjectCorrectnessTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatObjectCorrectnessTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatObjectCorrectnessTest
*/
/** This test is checking the object allocation path works with heap sampling. */
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java
@@ -28,7 +28,7 @@
* @summary Verifies the JVMTI Heap Monitor sampling rate average
* @build Frame HeapMonitor
* @compile HeapMonitorStatRateTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatRateTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatRateTest
*/
public class HeapMonitorStatRateTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java
@@ -28,7 +28,7 @@
* @build Frame HeapMonitor
* @summary Verifies the JVMTI Heap Monitor events are only sent after enabling.
* @compile HeapMonitorStatSimpleTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorStatSimpleTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorStatSimpleTest
*/
public class HeapMonitorStatSimpleTest {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java
@@ -30,7 +30,7 @@
* @summary Verifies the JVMTI Heap Monitor API
* @build Frame HeapMonitor
* @compile HeapMonitorTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorTest
*/
public class HeapMonitorTest {
@@ -38,13 +38,18 @@
private static native boolean framesAreNotLive(Frame[] frames);
public static void main(String[] args) {
+ if (args.length > 0) {
+ // For interpreter mode, have a means to reduce the default iteration count.
+ HeapMonitor.setAllocationIterations(Integer.parseInt(args[0]));
+ }
+
if (!HeapMonitor.eventStorageIsEmpty()) {
throw new RuntimeException("Storage is not empty at test start...");
}
HeapMonitor.enableSamplingEvents();
List frameList = HeapMonitor.allocate();
- frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 46));
+ frameList.add(new Frame("main", "([Ljava/lang/String;)V", "HeapMonitorTest.java", 51));
Frame[] frames = frameList.toArray(new Frame[0]);
if (!HeapMonitor.obtainedEvents(frames)) {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java
@@ -28,7 +28,7 @@
* @build Frame HeapMonitor
* @summary Verifies the JVMTI Heap Monitor Thread sanity
* @compile HeapMonitorThreadOnOffTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadOnOffTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadOnOffTest
*/
import java.util.ArrayList;
@@ -101,7 +101,7 @@
public void run() {
int sum = 0;
- for (int j = 0; j < 500; j++) {
+ for (int j = 0; j < 100; j++) {
sum += recursiveWrapper(depth);
}
}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java
@@ -25,148 +25,31 @@
/**
* @test
- * @build Frame HeapMonitor
+ * @build Frame HeapMonitor ThreadInformation
* @summary Verifies the JVMTI Heap Monitor Thread information sanity
* @compile HeapMonitorThreadTest.java
- * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadTest
*/
-import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
public class HeapMonitorThreadTest {
private native static boolean checkSamples(int numThreads);
public static void main(String[] args) {
final int numThreads = 24;
- ArrayList threadList = new ArrayList<>();
+ List threadList = ThreadInformation.createThreadList(numThreads);
HeapMonitor.enableSamplingEvents();
- // The test creates numThreads threads, each allocate a lot. Then they inform the main thread
- // they are done allocating. The test looks if all threads are available in the collected event
- // information and then proceeds to tell each thread to stop running before leaving the test.
- for (int i = 0 ; i < numThreads; i++) {
- BlockingQueue queue = new LinkedBlockingQueue<>();
- Allocator allocator = new Allocator(i, queue);
- Thread thread = new Thread(allocator, "Allocator" + i);
- thread.start();
-
- ThreadInformation info = new ThreadInformation(thread, queue, allocator);
- threadList.add(info);
- }
-
- // Wait until all threads have put an object in the queue.
- for (ThreadInformation info : threadList) {
- info.waitForAllocations();
- }
+ ThreadInformation.startThreads(threadList);
+ ThreadInformation.waitForThreads(threadList);
if (!checkSamples(numThreads)) {
throw new RuntimeException("Problem with checkSamples...");
}
// Now inform each thread we are done and wait for them to be done.
- for (ThreadInformation info : threadList) {
- info.stop();
- }
- }
-}
-
-class ThreadInformation {
- private Thread thread;
- private BlockingQueue finishedAllocationQueue;
- private Allocator allocator;
-
- public ThreadInformation(Thread thread, BlockingQueue finishedAllocationQueue,
- Allocator allocator) {
- this.thread = thread;
- this.finishedAllocationQueue = finishedAllocationQueue;
- this.allocator = allocator;
- }
-
- public void waitForAllocations() {
- try {
- finishedAllocationQueue.take();
- } catch(InterruptedException e) {
- throw new RuntimeException("Thread got interrupted...");
- }
- }
-
- public void stop() {
- try {
- allocator.stopRun();
- thread.join();
-
- if (!allocator.endedNormally()) {
- throw new RuntimeException("Thread did not end normally...");
- }
-
- } catch(InterruptedException e) {
- throw new RuntimeException("Thread got interrupted...");
- }
+ ThreadInformation.stopThreads(threadList);
}
}
-
-class Allocator implements Runnable {
- private int depth;
- private List currentList;
- private BlockingQueue finishedAllocationQueue;
- private BlockingQueue jobCanStop;
- private boolean failed;
-
- public Allocator(int depth, BlockingQueue finishedAllocationQueue) {
- jobCanStop = new LinkedBlockingQueue<>();
- this.finishedAllocationQueue = finishedAllocationQueue;
- this.depth = depth;
- }
-
- public boolean endedNormally() {
- return !failed;
- }
-
- private void helper() {
- List newList = new ArrayList<>();
- // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in
- // memory at least 2MB without counting the link-list itself, which adds to this.
- int iterations = (1 << 21) / 24;
- for (int i = 0; i < iterations; i++) {
- int newTmp[] = new int[1];
- // Force it to be kept.
- newList.add(newTmp);
- }
-
- // Replace old list with new list, which provokes two things:
- // Old list will get GC'd at some point.
- // New list forces that this thread has some allocations still sampled.
- currentList = newList;
- }
-
- private void recursiveWrapper(int depth) {
- if (depth > 0) {
- recursiveWrapper(depth - 1);
- }
- helper();
- }
-
- public void stopRun() throws InterruptedException {
- jobCanStop.put(new Object());
- }
-
- public void run() {
- for (int j = 0; j < 50; j++) {
- recursiveWrapper(depth);
- }
-
- try {
- // Tell the main thread we are done.
- finishedAllocationQueue.put(new Object());
-
- // Wait until the main thread says we can stop.
- jobCanStop.take();
- } catch (InterruptedException e) {
- failed = true;
- }
- }
-}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/ThreadInformation.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** API for handling heap allocating threads. */
+class ThreadInformation {
+ private Thread thread;
+ private BlockingQueue waitingQueue;
+ private Allocator allocator;
+
+ public ThreadInformation(Thread thread, BlockingQueue waitingQueue,
+ Allocator allocator) {
+ this.thread = thread;
+ this.waitingQueue = waitingQueue;
+ this.allocator = allocator;
+ }
+
+ public void waitForToken() {
+ allocator.waitForToken();
+ }
+
+ public void stop() {
+ try {
+ allocator.stopRun();
+ thread.join();
+
+ if (!allocator.endedNormally()) {
+ throw new RuntimeException("Thread did not end normally...");
+ }
+
+ } catch(InterruptedException e) {
+ throw new RuntimeException("Thread got interrupted...");
+ }
+ }
+
+ private void start() {
+ allocator.start();
+ }
+
+ public static void startThreads(List threadList) {
+ for (ThreadInformation info : threadList) {
+ info.start();
+ }
+ }
+
+ public static void stopThreads(List threadList) {
+ for (ThreadInformation info : threadList) {
+ info.stop();
+ }
+ }
+
+ public Thread getThread() {
+ return thread;
+ }
+
+ public static void waitForThreads(List threadList) {
+ // Wait until all threads have put an object in the queue.
+ for (ThreadInformation info : threadList) {
+ info.waitForToken();
+ }
+ }
+
+ public static List createThreadList(int numThreads) {
+ List threadList = new ArrayList<>();
+ for (int i = 0 ; i < numThreads; i++) {
+ BlockingQueue queue = new LinkedBlockingQueue<>();
+ Allocator allocator = new Allocator(i, queue);
+ Thread thread = new Thread(allocator, "Allocator" + i);
+
+ ThreadInformation info = new ThreadInformation(thread, queue, allocator);
+ threadList.add(info);
+ thread.start();
+ }
+ return threadList;
+ }
+}
+
+class Allocator implements Runnable {
+ private int depth;
+ private List currentList;
+ private BlockingQueue waitingQueue;
+ private BlockingQueue jobCanStop;
+ private boolean failed;
+
+ public Allocator(int depth, BlockingQueue waitingQueue) {
+ jobCanStop = new LinkedBlockingQueue<>();
+ this.waitingQueue = waitingQueue;
+ this.depth = depth;
+ }
+
+ public boolean endedNormally() {
+ return !failed;
+ }
+
+ private void helper() {
+ List newList = new ArrayList<>();
+ // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in
+ // memory at least 2MB without counting the link-list itself, which adds to this.
+ int iterations = (1 << 21) / 24;
+ for (int i = 0; i < iterations; i++) {
+ int newTmp[] = new int[1];
+ // Force it to be kept.
+ newList.add(newTmp);
+ }
+
+ // Replace old list with new list, which provokes two things:
+ // Old list will get GC'd at some point.
+ // New list forces that this thread has some allocations still sampled.
+ currentList = newList;
+ }
+
+ private void recursiveWrapper(int depth) {
+ if (depth > 0) {
+ recursiveWrapper(depth - 1);
+ }
+ helper();
+ }
+
+ public void stopRun() throws InterruptedException {
+ jobCanStop.put(new Object());
+ }
+
+ public void run() {
+ // Wait till we are told to really start.
+ waitForToken();
+
+ for (int j = 0; j < 50; j++) {
+ recursiveWrapper(depth);
+ }
+
+ try {
+ // Tell the main thread we are done.
+ waitingQueue.put(new Object());
+
+ // Wait until the main thread says we can stop.
+ jobCanStop.take();
+ } catch (InterruptedException e) {
+ failed = true;
+ }
+ }
+
+ public void waitForToken() {
+ try {
+ waitingQueue.take();
+ } catch(InterruptedException e) {
+ throw new RuntimeException("Thread got interrupted...");
+ }
+ }
+
+ public void start() {
+ try {
+ waitingQueue.put(new Object());
+ } catch(InterruptedException e) {
+ throw new RuntimeException("Thread got interrupted...");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
@@ -50,6 +50,7 @@
#define MAX_TRACES 400
static jvmtiEnv *jvmti = NULL;
+static jvmtiEnv *second_jvmti = NULL;
typedef struct _ObjectTrace{
jweak object;
@@ -522,6 +523,28 @@
"Set event notifications");
}
+static int enable_notifications_for_two_threads(jthread first, jthread second) {
+ if (check_error((*jvmti)->SetEventNotificationMode(
+ jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
+ "Set event notifications")) {
+ return 1;
+ }
+
+ if (check_error((*jvmti)->SetEventNotificationMode(
+ jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first),
+ "Set event notifications")) {
+ return 1;
+ }
+
+ if (check_error((*jvmti)->SetEventNotificationMode(
+ jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second),
+ "Set event notifications")) {
+ return 1;
+ }
+
+ return 0;
+}
+
static
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res;
@@ -529,7 +552,15 @@
res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
JVMTI_VERSION_9);
if (res != JNI_OK || jvmti == NULL) {
- printf(" Error: wrong result of a valid call to GetEnv!\n");
+ fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
+ return JNI_ERR;
+ }
+
+ // Get second jvmti environment.
+ res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &second_jvmti),
+ JVMTI_VERSION_9);
+ if (res != JNI_OK || second_jvmti == NULL) {
+ fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
return JNI_ERR;
}
@@ -544,16 +575,16 @@
jvmtiCapabilities caps;
memset(&caps, 0, sizeof(caps));
- // Get line numbers, sample heap, sample events, and filename for the test.
+ // Get line numbers, sample events, filename, and gc events for the tests.
caps.can_get_line_numbers = 1;
caps.can_generate_sampled_alloc_events = 1;
caps.can_get_source_file_name = 1;
caps.can_generate_garbage_collection_events = 1;
- if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){
+ if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")) {
return JNI_ERR;
}
- if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
+ if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
sizeof(jvmtiEventCallbacks)),
" Set Event Callbacks")) {
return JNI_ERR;
@@ -582,6 +613,14 @@
}
JNIEXPORT void JNICALL
+Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env,
+ jclass cls,
+ jthread first,
+ jthread second) {
+ enable_notifications_for_two_threads(first, second);
+}
+
+JNIEXPORT void JNICALL
Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
check_error((*jvmti)->SetEventNotificationMode(
jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL),
@@ -743,6 +782,85 @@
return thread_data.num_threads == num_threads;
}
+
+static void release_data(JNIEnv* env, jstring first, const char* first_str,
+ jstring second, const char* second_str,
+ jthread* threads) {
+ (*env)->ReleaseStringUTFChars(env, first, first_str);
+ (*env)->ReleaseStringUTFChars(env, second, second_str);
+ free(threads);
+}
+
+static int check_threads(JNIEnv* env, jstring first, jstring second,
+ jthread* threads) {
+ const char* first_str = (*env)->GetStringUTFChars(env, first, 0);
+ const char* second_str = (*env)->GetStringUTFChars(env, second, 0);
+
+ jthread first_thread = threads[0];
+ jthread second_thread = threads[1];
+
+ jvmtiThreadInfo first_info;
+ jvmtiThreadInfo second_info;
+ (*jvmti)->GetThreadInfo(jvmti, first_thread, &first_info);
+ (*jvmti)->GetThreadInfo(jvmti, second_thread, &second_info);
+
+ if (strcmp(first_str, first_info.name) &&
+ strcmp(first_str, second_info.name)) {
+ release_data(env, first, first_str, second, second_str, threads);
+ return FALSE;
+ }
+
+ if (strcmp(second_str, first_info.name) &&
+ strcmp(second_str, second_info.name)) {
+ release_data(env, first, first_str, second, second_str, threads);
+ return FALSE;
+ }
+
+ release_data(env, first, first_str, second, second_str, threads);
+ return TRUE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorEventsForTwoThreadsTest_checkSamples(JNIEnv* env,
+ jclass cls,
+ jstring first,
+ jstring second) {
+ pthread_mutex_lock(&global_event_storage.storage_mutex);
+ jint trace_counter;
+
+ const int expected_num_threads = 2;
+ ThreadsFound thread_data;
+ thread_data.num_threads = 0;
+ thread_data.threads = malloc(sizeof(jthread) * expected_num_threads);
+ memset(thread_data.threads, 0, sizeof(jthread) * expected_num_threads);
+
+ find_threads_in_array(&thread_data, global_event_storage.live_objects,
+ global_event_storage.live_object_count);
+
+ pthread_mutex_unlock(&global_event_storage.storage_mutex);
+ fprintf(stderr, "Obtained %d - %d\n", thread_data.num_threads,
+ expected_num_threads);
+
+ if (thread_data.num_threads != expected_num_threads) {
+ free(thread_data.threads);
+ return FALSE;
+ }
+
+ return check_threads(env, first, second, thread_data.threads);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorOnlyOneAgent_enablingSamplingInSecondaryAgent(
+ JNIEnv* env, jclass cls) {
+ // Because sampling allocations is in solo mode only, trying to grab the
+ // capability with the second agent should fail.
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_generate_sampled_alloc_events = 1;
+ return (*second_jvmti)->AddCapabilities(second_jvmti, &caps)
+ == JVMTI_ERROR_NONE;
+}
+
#ifdef __cplusplus
}
#endif
# HG changeset patch
# User jcbeyler
# Date 1522951679 25200
# Thu Apr 05 11:07:59 2018 -0700
# Node ID b942aed1891de99d78ea4e3ed891e1269e1814e7
# Parent 1fba53555a7b286ee1c8e34ce229298e346488c8
[mq]: heap11
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -329,7 +329,6 @@
JRT_ENTRY(void, Runtime1::new_instance(JavaThread* thread, Klass* klass))
NOT_PRODUCT(_new_instance_slowcase_cnt++;)
- JvmtiSampledObjectAllocEventCollector collector;
assert(klass->is_klass(), "not a class");
Handle holder(THREAD, klass->klass_holder()); // keep the klass alive
@@ -345,7 +344,6 @@
JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* thread, Klass* klass, jint length))
NOT_PRODUCT(_new_type_array_slowcase_cnt++;)
- JvmtiSampledObjectAllocEventCollector collector;
// Note: no handle for klass needed since they are not used
// anymore after new_typeArray() and no GC can happen before.
// (This may have to change if this code changes!)
@@ -364,7 +362,6 @@
JRT_ENTRY(void, Runtime1::new_object_array(JavaThread* thread, Klass* array_klass, jint length))
NOT_PRODUCT(_new_object_array_slowcase_cnt++;)
- JvmtiSampledObjectAllocEventCollector collector;
// Note: no handle for klass needed since they are not used
// anymore after new_objArray() and no GC can happen before.
@@ -384,7 +381,6 @@
JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* thread, Klass* klass, int rank, jint* dims))
NOT_PRODUCT(_new_multi_array_slowcase_cnt++;)
- JvmtiSampledObjectAllocEventCollector collector;
assert(klass->is_klass(), "not a class");
assert(rank >= 1, "rank must be nonzero");
diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
@@ -217,7 +217,6 @@
}
oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
@@ -228,7 +227,6 @@
}
oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
@@ -242,7 +240,6 @@
int size,
int length,
TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
@@ -256,7 +253,6 @@
int size,
int length,
TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
@@ -850,7 +850,6 @@
IRT_END
void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode) {
- JvmtiSampledObjectAllocEventCollector collector;
Thread* THREAD = thread;
LastFrameAccessor last_frame(thread);
// extract receiver from the outgoing argument list if necessary
diff --git a/src/hotspot/share/memory/oopFactory.cpp b/src/hotspot/share/memory/oopFactory.cpp
--- a/src/hotspot/share/memory/oopFactory.cpp
+++ b/src/hotspot/share/memory/oopFactory.cpp
@@ -41,7 +41,6 @@
typeArrayOop oopFactory::new_charArray(const char* utf8_str, TRAPS) {
int length = utf8_str == NULL ? 0 : UTF8::unicode_length(utf8_str);
- JvmtiSampledObjectAllocEventCollector collector;
typeArrayOop result = new_charArray(length, CHECK_NULL);
if (length > 0) {
UTF8::convert_to_unicode(utf8_str, result->char_at_addr(0), length);
@@ -50,12 +49,10 @@
}
typeArrayOop oopFactory::new_tenured_charArray(int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
return TypeArrayKlass::cast(Universe::charArrayKlassObj())->allocate(length, THREAD);
}
typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
Klass* type_asKlassOop = Universe::typeArrayKlassObj(type);
TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop);
typeArrayOop result = type_asArrayKlass->allocate(length, THREAD);
@@ -67,7 +64,6 @@
// int or long depending on pointer size. Only stack trace elements in Throwable use
// this. They cast Symbol* into this type.
typeArrayOop oopFactory::new_symbolArray(int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
BasicType type = LP64_ONLY(T_LONG) NOT_LP64(T_INT);
Klass* type_asKlassOop = Universe::typeArrayKlassObj(type);
TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop);
@@ -76,7 +72,6 @@
}
typeArrayOop oopFactory::new_typeArray_nozero(BasicType type, int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
Klass* type_asKlassOop = Universe::typeArrayKlassObj(type);
TypeArrayKlass* type_asArrayKlass = TypeArrayKlass::cast(type_asKlassOop);
typeArrayOop result = type_asArrayKlass->allocate_common(length, false, THREAD);
@@ -85,7 +80,6 @@
objArrayOop oopFactory::new_objArray(Klass* klass, int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
assert(klass->is_klass(), "must be instance class");
if (klass->is_array_klass()) {
return ArrayKlass::cast(klass)->allocate_arrayArray(1, length, THREAD);
@@ -95,13 +89,11 @@
}
objArrayHandle oopFactory::new_objArray_handle(Klass* klass, int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
objArrayOop obj = new_objArray(klass, length, CHECK_(objArrayHandle()));
return objArrayHandle(THREAD, obj);
}
typeArrayHandle oopFactory::new_byteArray_handle(int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
typeArrayOop obj = new_byteArray(length, CHECK_(typeArrayHandle()));
return typeArrayHandle(THREAD, obj);
}
diff --git a/src/hotspot/share/memory/oopFactory.hpp b/src/hotspot/share/memory/oopFactory.hpp
--- a/src/hotspot/share/memory/oopFactory.hpp
+++ b/src/hotspot/share/memory/oopFactory.hpp
@@ -42,38 +42,14 @@
class oopFactory: AllStatic {
public:
// Basic type leaf array allocation
- static typeArrayOop new_boolArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD);
- }
- static typeArrayOop new_charArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD);
- }
- static typeArrayOop new_singleArray(int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD);
- }
- static typeArrayOop new_doubleArray(int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD);
- }
- static typeArrayOop new_byteArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD);
- }
- static typeArrayOop new_shortArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD);
- }
- static typeArrayOop new_intArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD);
- }
- static typeArrayOop new_longArray (int length, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
- return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD);
- }
+ static typeArrayOop new_boolArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::boolArrayKlassObj ())->allocate(length, THREAD); }
+ static typeArrayOop new_charArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::charArrayKlassObj ())->allocate(length, THREAD); }
+ static typeArrayOop new_singleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::singleArrayKlassObj())->allocate(length, THREAD); }
+ static typeArrayOop new_doubleArray(int length, TRAPS) { return TypeArrayKlass::cast(Universe::doubleArrayKlassObj())->allocate(length, THREAD); }
+ static typeArrayOop new_byteArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::byteArrayKlassObj ())->allocate(length, THREAD); }
+ static typeArrayOop new_shortArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::shortArrayKlassObj ())->allocate(length, THREAD); }
+ static typeArrayOop new_intArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::intArrayKlassObj ())->allocate(length, THREAD); }
+ static typeArrayOop new_longArray (int length, TRAPS) { return TypeArrayKlass::cast(Universe::longArrayKlassObj ())->allocate(length, THREAD); }
// create java.lang.Object[]
static objArrayOop new_objectArray(int length, TRAPS) {
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -1002,7 +1002,6 @@
}
instanceOop InstanceKlass::allocate_instance(TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
int size = size_helper(); // Query before forming handle.
diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp
--- a/src/hotspot/share/oops/objArrayKlass.cpp
+++ b/src/hotspot/share/oops/objArrayKlass.cpp
@@ -58,7 +58,6 @@
Klass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_data,
int n, Klass* element_klass, TRAPS) {
- JvmtiSampledObjectAllocEventCollector collector;
// Eagerly allocate the direct array supertype.
Klass* super_klass = NULL;
if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) {
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -279,6 +279,7 @@
SharedRuntime::_new_array_ctr++; // new array requires GC
#endif
assert(check_compiled_frame(thread), "incorrect caller");
+ JvmtiSampledObjectAllocEventCollector collector;
// Scavenge and allocate an instance.
oop result;
@@ -323,12 +324,12 @@
// multianewarray for 2 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread *thread))
- JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi2_ctr++; // multianewarray for 1 dimension
#endif
assert(check_compiled_frame(thread), "incorrect caller");
assert(elem_type->is_klass(), "not a class");
+ JvmtiSampledObjectAllocEventCollector collector;
jint dims[2];
dims[0] = len1;
dims[1] = len2;
@@ -340,7 +341,6 @@
// multianewarray for 3 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread *thread))
- JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi3_ctr++; // multianewarray for 1 dimension
#endif
@@ -358,12 +358,12 @@
// multianewarray for 4 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread *thread))
- JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi4_ctr++; // multianewarray for 1 dimension
#endif
assert(check_compiled_frame(thread), "incorrect caller");
assert(elem_type->is_klass(), "not a class");
+ JvmtiSampledObjectAllocEventCollector collector;
jint dims[4];
dims[0] = len1;
dims[1] = len2;
@@ -377,12 +377,12 @@
// multianewarray for 5 dimensions
JRT_ENTRY(void, OptoRuntime::multianewarray5_C(Klass* elem_type, int len1, int len2, int len3, int len4, int len5, JavaThread *thread))
- JvmtiSampledObjectAllocEventCollector collector;
#ifndef PRODUCT
SharedRuntime::_multi5_ctr++; // multianewarray for 1 dimension
#endif
assert(check_compiled_frame(thread), "incorrect caller");
assert(elem_type->is_klass(), "not a class");
+ JvmtiSampledObjectAllocEventCollector collector;
jint dims[5];
dims[0] = len1;
dims[1] = len2;
@@ -399,6 +399,7 @@
assert(check_compiled_frame(thread), "incorrect caller");
assert(elem_type->is_klass(), "not a class");
assert(oop(dims)->is_typeArray(), "not an array");
+ JvmtiSampledObjectAllocEventCollector collector;
ResourceMark rm;
jint len = dims->length();
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -631,7 +631,6 @@
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
- JvmtiSampledObjectAllocEventCollector collector;
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
Klass* klass = obj->klass();
JvmtiVMObjectAllocEventCollector oam;
@@ -3315,7 +3314,6 @@
JVM_ENTRY(jobject, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length))
JVMWrapper("JVM_NewArray");
- JvmtiSampledObjectAllocEventCollector collector;
JvmtiVMObjectAllocEventCollector oam;
oop element_mirror = JNIHandles::resolve(eltClass);
oop result = Reflection::reflect_new_array(element_mirror, length, CHECK_NULL);
@@ -3325,7 +3323,6 @@
JVM_ENTRY(jobject, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim))
JVMWrapper("JVM_NewMultiArray");
- JvmtiSampledObjectAllocEventCollector collector;
JvmtiVMObjectAllocEventCollector oam;
arrayOop dim_array = check_array(env, dim, true, CHECK_NULL);
oop element_mirror = JNIHandles::resolve(eltClass);
@@ -3490,9 +3487,6 @@
if (JvmtiExport::should_post_vm_object_alloc()) {
JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);
}
- if (JvmtiExport::should_post_sampled_object_alloc()) {
- JvmtiExport::post_sampled_object_alloc(JavaThread::current(), result);
- }
return res;
JVM_END
diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml
--- a/src/hotspot/share/prims/jvmti.xml
+++ b/src/hotspot/share/prims/jvmti.xml
@@ -13563,7 +13563,7 @@
+ id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11">
Sent when an allocated object is sampled via the
Heap Sampling Monitoring system .
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -549,7 +549,6 @@
JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
} else {
// We have a specified event_thread.
-
JavaThread* java_thread = NULL;
ThreadsListHandle tlh;
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
@@ -577,14 +576,6 @@
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
record_class_file_load_hook_enabled();
}
-
- if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) {
- if (enabled) {
- ThreadHeapSampler::enable();
- } else {
- ThreadHeapSampler::disable();
- }
- }
JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
}
@@ -3653,7 +3644,7 @@
if (sampling_rate < 0) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
- ThreadHeapSampler::set_tlab_heap_sampling(sampling_rate);
+ ThreadHeapSampler::set_sampling_rate(sampling_rate);
return JVMTI_ERROR_NONE;
} /* end SetHeapSamplingRate */
diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp
--- a/src/hotspot/share/prims/jvmtiEventController.cpp
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp
@@ -96,8 +96,7 @@
static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT |
FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS |
- BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT |
- SAMPLED_OBJECT_ALLOC_BIT;
+ BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT;
static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT;
static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT |
VM_START_BIT | VM_INIT_BIT | VM_DEATH_BIT | NATIVE_METHOD_BIND_BIT |
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -2539,30 +2539,22 @@
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Trg sampled object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
-
if (object == NULL) {
return;
}
- JvmtiThreadState *state = thread->jvmti_thread_state();
- if (state == NULL) {
- return;
- }
-
HandleMark hm(thread);
Handle h(thread, object);
-
- JvmtiEnvThreadStateIterator it(state);
- for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) {
- if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
+ JvmtiEnvIterator it;
+ for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
+ if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Evt sampled object alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
object == NULL ? "NULL" : object->klass()->external_name()));
JvmtiObjectAllocEventMark jem(thread, h());
- JvmtiEnv *env = ets->get_env();
- JvmtiThreadEventTransition jet(thread);
+ JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
@@ -2812,7 +2804,7 @@
oop obj = _allocated->at(i);
_post_callback(JavaThread::current(), obj);
}
- delete _allocated;
+ delete _allocated, _allocated = NULL;
}
}
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -126,7 +126,7 @@
Monitor* PeriodicTask_lock = NULL;
Monitor* RedefineClasses_lock = NULL;
-Monitor* ThreadHeapSampler_lock = NULL;
+Mutex* ThreadHeapSampler_lock = NULL;
#if INCLUDE_TRACE
Mutex* JfrStacktrace_lock = NULL;
@@ -289,7 +289,7 @@
def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes);
def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always);
- def(ThreadHeapSampler_lock , PaddedMonitor, nonleaf, false, Monitor::_safepoint_check_sometimes);
+ def(ThreadHeapSampler_lock , PaddedMutex, nonleaf, false, Monitor::_safepoint_check_never);
if (WhiteBoxAPI) {
def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never);
diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -124,7 +124,7 @@
extern Monitor* Service_lock; // a lock used for service thread operation
extern Monitor* PeriodicTask_lock; // protects the periodic task structure
extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition
-extern Monitor* ThreadHeapSampler_lock; // protects the static data for initialization.
+extern Mutex* ThreadHeapSampler_lock; // protects the static data for initialization.
#if INCLUDE_TRACE
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp
--- a/src/hotspot/share/runtime/threadHeapSampler.cpp
+++ b/src/hotspot/share/runtime/threadHeapSampler.cpp
@@ -88,12 +88,12 @@
// negative answer.
double log_val = (fast_log2(q) - 26);
size_t rate = static_cast(
- (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (_sampling_rate)) + 1);
+ (0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_rate())) + 1);
_bytes_until_sample = rate;
}
void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) {
- if (_sampling_rate == 1) {
+ if (get_sampling_rate() == 1) {
_bytes_until_sample = 1;
return;
}
@@ -123,7 +123,7 @@
}
void ThreadHeapSampler::init_log_table() {
- MutexLocker mu(ThreadHeapSampler_lock);
+ MutexLockerEx mu(ThreadHeapSampler_lock);
if (log_table_initialized) {
return;
@@ -137,11 +137,16 @@
log_table_initialized = true;
}
-void ThreadHeapSampler::set_tlab_heap_sampling(int sampling_rate) {
- MutexLocker mu(ThreadHeapSampler_lock);
- _sampling_rate = sampling_rate;
+void ThreadHeapSampler::enable() {
+ // Done here to be done when things have settled. This adds a mutex lock but
+ // presumably, users won't be enabling and disabling all the time.
+ init_log_table();
+ OrderAccess::release_store(&_enabled, 1);
}
+
+// Methods used in assertion mode to check if a collector is present or not at
+// the moment of TLAB sampling, ie a slow allocation path.
bool ThreadHeapSampler::sampling_collector_present() const {
return _collectors_present > 0;
}
@@ -155,10 +160,3 @@
_collectors_present++;
return true;
}
-
-void ThreadHeapSampler::enable() {
- // Done here to be done when things have settled. This adds a mutex lock but
- // presumably, users won't be enabling and disabling all the time.
- init_log_table();
- OrderAccess::release_store(&_enabled, 1);
-}
diff --git a/src/hotspot/share/runtime/threadHeapSampler.hpp b/src/hotspot/share/runtime/threadHeapSampler.hpp
--- a/src/hotspot/share/runtime/threadHeapSampler.hpp
+++ b/src/hotspot/share/runtime/threadHeapSampler.hpp
@@ -26,6 +26,7 @@
#define RUNTIME_THREADHEAPSAMPLER_HPP
#include "memory/allocation.hpp"
+#include "runtime/orderAccess.inline.hpp"
class ThreadHeapSampler {
private:
@@ -65,7 +66,13 @@
static int enabled() { return OrderAccess::load_acquire(&_enabled); }
static void enable();
static void disable() { OrderAccess::release_store(&_enabled, 0); }
- static void set_tlab_heap_sampling(int sampling_rate);
+
+ static void set_sampling_rate(int sampling_rate) {
+ OrderAccess::release_store(&_sampling_rate, sampling_rate);
+ }
+ static int get_sampling_rate() {
+ return OrderAccess::load_acquire(&_sampling_rate);
+ }
bool sampling_collector_present() const;
bool remove_sampling_collector();
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitor.java
@@ -44,7 +44,7 @@
/** Set a specific sampling rate, 0 samples every allocation. */
public native static void setSamplingRate(int rate);
public native static void enableSamplingEvents();
- public native static void enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
+ public native static boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
public native static void disableSamplingEvents();
/**
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
@@ -26,7 +26,7 @@
/**
* @test
* @build Frame HeapMonitor ThreadInformation
- * @summary Verifies the JVMTI Heap Monitor for only two threads
+ * @summary Ensures the JVMTI Heap Monitor is not thread enable (test to change when it becomes so)
* @compile HeapMonitorEventsForTwoThreadsTest.java
* @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest
*/
@@ -34,25 +34,14 @@
import java.util.List;
public class HeapMonitorEventsForTwoThreadsTest {
- private native static boolean checkSamples(String first, String second);
-
public static void main(String[] args) {
final int numThreads = 24;
List threadList = ThreadInformation.createThreadList(numThreads);
Thread firstThread = threadList.get(0).getThread();
Thread secondThread = threadList.get(1).getThread();
- // HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread);
-
- ThreadInformation.startThreads(threadList);
- ThreadInformation.waitForThreads(threadList);
-
- // Should only see events for the two threads.
- if (!checkSamples(firstThread.getName(), secondThread.getName())) {
- throw new RuntimeException("Problem with checkSamples...");
+ if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) {
+ throw new RuntimeException("Sampling event is thread enabled, that is unexpected.");
}
-
- // Now inform each thread we are done and wait for them to be done.
- ThreadInformation.stopThreads(threadList);
}
}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java
new file mode 100644
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorOnlyOneAgent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @build Frame HeapMonitor ThreadInformation
+ * @summary Verifies the JVMTI Heap Monitor is only availabe for a single agent.
+ * @compile HeapMonitorOnlyOneAgent.java
+ * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorOnlyOneAgent
+ */
+
+import java.util.List;
+
+public class HeapMonitorOnlyOneAgent {
+ private native static boolean enablingSamplingInSecondaryAgent();
+
+ public static void main(String[] args) {
+ HeapMonitor.enableSamplingEvents();
+
+ if (enablingSamplingInSecondaryAgent() == true) {
+ throw new RuntimeException("Enabling sampling in second agent did not fail...");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
@@ -527,22 +527,23 @@
if (check_error((*jvmti)->SetEventNotificationMode(
jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
"Set event notifications")) {
- return 1;
+ return 0;
}
if (check_error((*jvmti)->SetEventNotificationMode(
jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first),
"Set event notifications")) {
- return 1;
+ return 0;
}
+ // Second thread should fail.
if (check_error((*jvmti)->SetEventNotificationMode(
jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second),
"Set event notifications")) {
- return 1;
+ return 0;
}
- return 0;
+ return 1;
}
static
@@ -612,12 +613,12 @@
enable_notifications();
}
-JNIEXPORT void JNICALL
+JNIEXPORT jboolean JNICALL
Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env,
jclass cls,
jthread first,
jthread second) {
- enable_notifications_for_two_threads(first, second);
+ return enable_notifications_for_two_threads(first, second);
}
JNIEXPORT void JNICALL