--- old/src/hotspot/share/prims/jvmti.xml 2018-03-28 08:20:23.141206793 -0700 +++ new/src/hotspot/share/prims/jvmti.xml 2018-03-28 08:20:22.821207964 -0700 @@ -13565,7 +13565,7 @@ - Sent when an object is sampled via the + Sent when an allocated object is sampled via the Heap Sampling Monitoring system .

By default, the sampling rate used is geometric variable with a 512kb mean, meaning one object every 512k bytes, @@ -13577,7 +13577,7 @@ with a strictly positive integer value, representing the new sampling rate to be used by the default sampler.

- This event is sent once the allocation has been done and provides the object, stack trace + 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. --- old/src/hotspot/share/prims/jvmtiEnv.cpp 2018-03-28 08:20:24.373202282 -0700 +++ new/src/hotspot/share/prims/jvmtiEnv.cpp 2018-03-28 08:20:24.049203468 -0700 @@ -3640,6 +3640,9 @@ jvmtiError 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 SetHeapSamplingRate */ --- old/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java 2018-03-28 08:20:25.409198489 -0700 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java 2018-03-28 08:20:25.085199675 -0700 @@ -607,7 +607,7 @@ public final int tlabRefillWasteIncrement = getFlag("TLABWasteIncrement", Integer.class); private final int threadLocalAllocBufferStartOffset = getFieldOffset("ThreadLocalAllocBuffer::_start", Integer.class, "HeapWord*"); - private final int threadLocalAllocBufferEndOffset = getFieldOffset("ThreadLocalAllocBuffer::_end", Integer.class, "HeapWord*"); + private final int threadLocalAllocBufferEndOffset = getFieldOffset("ThreadLocalAllocBuffer::_current_end", Integer.class, "HeapWord*"); private final int threadLocalAllocBufferTopOffset = getFieldOffset("ThreadLocalAllocBuffer::_top", Integer.class, "HeapWord*"); private final int threadLocalAllocBufferPfTopOffset = getFieldOffset("ThreadLocalAllocBuffer::_pf_top", Integer.class, "HeapWord*"); private final int threadLocalAllocBufferSlowAllocationsOffset = getFieldOffset("ThreadLocalAllocBuffer::_slow_allocations", Integer.class, "unsigned"); --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java 2018-03-28 08:20:26.341195076 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java 2018-03-28 08:20:26.037196190 -0700 @@ -53,10 +53,6 @@ System.out.println("Testing size " + currentSize); HeapMonitor.resetEventStorage(); - if (!HeapMonitor.eventStorageIsEmpty()) { - throw new RuntimeException("Should not have any events stored yet."); - } - allocate(currentSize); // 10% error ensures a sanity test without becoming flaky. --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java 2018-03-28 08:20:27.269191679 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java 2018-03-28 08:20:26.949192851 -0700 @@ -26,22 +26,12 @@ /** * @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 { - static { - try { - System.loadLibrary("HeapMonitor"); - } catch (UnsatisfiedLinkError ule) { - System.err.println("Could not load HeapMonitor library"); - System.err.println("java.library.path: " - + System.getProperty("java.library.path")); - throw ule; - } - } - private native static int allSamplingMethodsFail(); public static void main(String[] args) { --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java 2018-03-28 08:20:28.189188311 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java 2018-03-28 08:20:27.873189468 -0700 @@ -51,6 +51,7 @@ 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."); } @@ -79,8 +80,6 @@ if (!HeapMonitor.statsHaveExpectedNumberSamples((int) expected, 10)) { throw new RuntimeException("Statistics should show about " + expected + " samples."); } - - HeapMonitor.resetEventStorage(); } } } --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java 2018-03-28 08:20:29.141184826 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatRateTest.java 2018-03-28 08:20:28.825185983 -0700 @@ -38,18 +38,18 @@ public static void main(String[] args) { int[] tab = {1024, 16384, 524288}; - HeapMonitor.enableSamplingEvents(); - 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.setSamplingRate(0); + HeapMonitor.disableSamplingEvents(); double actualCount = HeapMonitor.getEventStorageElementCount(); double expectedCount = allocationTotal / rate; --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java 2018-03-28 08:20:30.077181399 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java 2018-03-28 08:20:29.765182542 -0700 @@ -43,7 +43,6 @@ // Remember a lot of garbage to have space for all thread samples. HeapMonitor.enableSamplingEvents(); - HeapMonitor.setGarbageHistory(10000); for (int i = 0 ; i < numThreads; i++) { Thread thread = new Thread(new Allocator(i), "Allocator" + i); @@ -67,36 +66,39 @@ class Allocator implements Runnable { private int depth; - private volatile int tmp[]; + private List currentList; 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++) { + 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. - tmp = newTmp; - sum += tmp[0]; + newList.add(newTmp); } - return sum; + + // 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 int recursiveWrapper(int depth) { + private void recursiveWrapper(int depth) { if (depth > 0) { - return recursiveWrapper(depth - 1); + recursiveWrapper(depth - 1); } - return helper(); + helper(); } public void run() { - int sum = 0; for (int j = 0; j < 50; j++) { - sum += recursiveWrapper(depth); + recursiveWrapper(depth); } - System.out.println(sum); } } --- old/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c 2018-03-28 08:20:31.037177885 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c 2018-03-28 08:20:30.717179056 -0700 @@ -566,6 +566,7 @@ (*jvmti)->SetHeapSamplingRate(jvmti, value); } +// TODO: should be removed, no tests using it. JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_setGarbageHistory(JNIEnv* env, jclass cls, jint value) { event_storage_set_garbage_history(&global_event_storage, value); @@ -642,6 +643,32 @@ return TRUE; } +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env, + jclass cls) { + if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 0), + "Sampling rate 0 failed\n")){ + return FALSE; + } + + if (check_error((*jvmti)->SetHeapSamplingRate(jvmti, 1024), + "Sampling rate 1024 failed\n")){ + return FALSE; + } + + if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1), + "Sampling rate -1 passed\n")){ + return FALSE; + } + + if (!check_error((*jvmti)->SetHeapSamplingRate(jvmti, -1024), + "Sampling rate -1024 passed\n")){ + return FALSE; + } + + return TRUE; +} + JNIEXPORT jdouble JNICALL Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { return event_storage_get_average_rate(&global_event_storage); @@ -713,12 +740,12 @@ 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.storage_mutex); + fprintf(stderr, "Obtained %d - %d\n", + thread_data.num_threads, + num_threads); return thread_data.num_threads == num_threads; } --- /dev/null 2018-03-08 15:11:46.002676589 -0800 +++ new/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java 2018-03-28 08:20:31.741175307 -0700 @@ -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."); + } + } +}