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