# HG changeset patch # User jcbeyler # Date 1540226218 25200 # Mon Oct 22 09:36:58 2018 -0700 # Node ID 560726b6f651eea1606756729bb46d2a8e843534 # Parent e9727e6b5fc1dd2918137260951b8ee8e8b56db8 8201655: Add thread-enabled support for the Heap Sampling Summary: Reviewed-by: diff --git a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -32,6 +32,8 @@ #include "oops/arrayOop.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/jvmtiEventController.inline.hpp" +#include "prims/jvmtiThreadState.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/handles.inline.hpp" #include "runtime/thread.inline.hpp" @@ -183,6 +185,23 @@ } #endif +static bool thread_enabled_for_one_jvmti_env() { + JavaThread *thread = JavaThread::current(); + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return false; + } + + JvmtiEnvThreadStateIterator it(state); + for (JvmtiEnvThreadState* ets = it.first(); ets != NULL; ets = it.next(ets)) { + if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) { + return true; + } + } + + return false; +} + void MemAllocator::Allocation::notify_allocation_jvmti_sampler() { // support for JVMTI VMObjectAlloc event (no-op if not enabled) JvmtiExport::vm_object_alloc_event_collector(obj()); @@ -192,6 +211,11 @@ return; } + // Sampling is enabled for at least one thread, is it this one? + if (!thread_enabled_for_one_jvmti_env()) { + return; + } + if (!_allocated_outside_tlab && _allocated_tlab_size == 0 && !_tlab_end_reset_for_sample) { // Sample if it's a non-TLAB allocation, or a TLAB allocation that either refills the TLAB // or expands it due to taking a sampler induced slow path. 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 @@ -13595,7 +13595,7 @@ + id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" filtered="thread" num="86" since="11"> Sent when an allocated object is sampled. By default, the sampling interval is set to 512KB. The sampling is semi-random to avoid 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 @@ -2542,6 +2542,11 @@ } void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + return; + } + EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, ("[%s] Trg sampled object alloc triggered", JvmtiTrace::safe_get_thread_name(thread))); @@ -2550,14 +2555,16 @@ } 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())); + JvmtiEnv *env = ets->get_env(); JvmtiObjectAllocEventMark jem(thread, h()); JvmtiJavaThreadEventTransition jet(thread); jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc; diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java deleted file mode 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018, Oracle 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package MyPackage; - -/** - * @test - * @build Frame HeapMonitor ThreadInformation - * @summary Ensures the JVMTI Heap Monitor is not thread enable (test to change when it becomes so) - * @compile HeapMonitorEventsForTwoThreadsTest.java - * @run main/othervm/native -agentlib:HeapMonitorTest MyPackage.HeapMonitorEventsForTwoThreadsTest - */ - -import java.util.List; - -public class HeapMonitorEventsForTwoThreadsTest { - public native static boolean checkSamples(); - - public static void main(String[] args) { - final int numThreads = 24; - List threadList = ThreadInformation.createThreadList(numThreads); - - Thread firstThread = threadList.get(0).getThread(); - Thread secondThread = threadList.get(1).getThread(); - if (HeapMonitor.enableSamplingEventsForTwoThreads(firstThread, secondThread)) { - throw new RuntimeException("Sampling event is thread enabled, that is unexpected."); - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018, Oracle 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package MyPackage; + +/** + * @test + * @build Frame HeapMonitor ThreadInformation + * @summary Verifies the JVMTI Heap Monitor Thread can disable events for a given thread. + * @compile HeapMonitorThreadDisabledTest.java + * @run main/othervm/native -Xmx512m -agentlib:HeapMonitorTest MyPackage.HeapMonitorThreadDisabledTest + * @requires !vm.gc.Z + */ + +import java.util.List; + +public class HeapMonitorThreadDisabledTest { + private native static void enableSamplingEvents(Thread thread); + private native static boolean checkThreadSamplesOnlyFrom(Thread thread); + + public static void main(String[] args) { + final int numThreads = 4; + List threadList = ThreadInformation.createThreadList(numThreads); + + // Sample at a interval of 8k. + HeapMonitor.setSamplingInterval(1 << 13); + + // Only enable the sampling for a given thread. + Thread thread = threadList.get(0).getThread(); + enableSamplingEvents(thread); + + System.err.println("Starting threads"); + ThreadInformation.startThreads(threadList); + ThreadInformation.waitForThreads(threadList); + System.err.println("Waited for threads"); + + // Only have the samples for a given thread should be captured. + if (!checkThreadSamplesOnlyFrom(thread)) { + throw new RuntimeException( + "Problem with checkSamples: got no events from the expected thread"); + } + + // 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/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 @@ -701,16 +701,39 @@ typedef struct ThreadStats { int number_threads; int counts[MAX_THREADS]; - int not_helper_counts[MAX_THREADS]; - int index[MAX_THREADS]; - jthread threads[MAX_THREADS]; - - int method_resolution_problem; + char* threads[MAX_THREADS]; } ThreadStats; static ThreadStats thread_stats; -static void add_thread_count(jthread thread, int lock, int helper) { +JNIEXPORT jboolean JNICALL +Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom( + JNIEnv* env, jclass cls, jthread thread) { + int i; + jvmtiThreadInfo info; + int err; + char* expected_name; + int found_thread = FALSE; + + err = (*jvmti)->GetThreadInfo(jvmti, thread, &info); + expected_name = info.name; + + if (err != JVMTI_ERROR_NONE) { + fprintf(stderr, "Failed to get thread information\n"); + return FALSE; + } + + for (i = 0; i < thread_stats.number_threads; i++) { + if (strcmp(expected_name, thread_stats.threads[i])) { + return FALSE; + } else { + found_thread = TRUE; + } + } + return found_thread; +} + +static void add_thread_count(jthread thread) { int i; jvmtiThreadInfo info; const char* name; @@ -718,82 +741,41 @@ int idx; int err; - if (lock) { - event_storage_lock(&global_event_storage); + err = (*jvmti)->GetThreadInfo(jvmti, thread, &info); + if (err != JVMTI_ERROR_NONE) { + return; } + event_storage_lock(&global_event_storage); for (i = 0; i < thread_stats.number_threads; i++) { - if (thread_stats.threads[i] == thread) { - if (helper) { - thread_stats.counts[i]++; - } else { - thread_stats.not_helper_counts[i]++; - } - - if (lock) { - event_storage_unlock(&global_event_storage); - } + if (!strcmp(thread_stats.threads[i], info.name)) { + thread_stats.counts[i]++; + event_storage_unlock(&global_event_storage); return; } } - thread_stats.threads[thread_stats.number_threads] = thread; - - err = (*jvmti)->GetThreadInfo(jvmti, thread, &info); - if (err != JVMTI_ERROR_NONE) { - if (lock) { - event_storage_unlock(&global_event_storage); - } - - // Just to have it accounted as an error... - info.name = "Allocator99"; - } - - if (!strstr(info.name, "Allocator")) { - if (lock) { - event_storage_unlock(&global_event_storage); - } - - // Just to have it accounted as an error... - info.name = "Allocator98"; - } + thread_stats.threads[thread_stats.number_threads] = info.name; + thread_stats.counts[thread_stats.number_threads]++; + thread_stats.number_threads++; + event_storage_unlock(&global_event_storage); +} - name = info.name + 9; - end = NULL; - idx = strtol(name, &end, 0); - - if (*end == '\0') { - if (helper) { - thread_stats.counts[thread_stats.number_threads]++; - } else { - thread_stats.not_helper_counts[thread_stats.number_threads]++; - } - - thread_stats.index[thread_stats.number_threads] = idx; - thread_stats.number_threads++; - } else { - fprintf(stderr, "Problem with thread name...: %p %s\n", thread, name); - } - - if (PRINT_OUT) { - fprintf(stderr, "Added %s - %p - %d - lock: %d\n", info.name, thread, idx, lock); - } - - if (lock) { - event_storage_unlock(&global_event_storage); - } +JNIEXPORT void JNICALL +Java_MyPackage_HeapMonitorThreadDisabledTest_enableSamplingEvents( + JNIEnv* env, jclass cls, jthread thread) { + fprintf(stderr, "Enabling for %p\n", thread); + check_error((*jvmti)->SetEventNotificationMode( + jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, thread), + "Set event notifications for a single thread"); } static void print_thread_stats() { int i; event_storage_lock(&global_event_storage); - fprintf(stderr, "Method resolution problem: %d\n", thread_stats.method_resolution_problem); fprintf(stderr, "Thread count:\n"); for (i = 0; i < thread_stats.number_threads; i++) { - fprintf(stderr, "\t%p: %d: %d - %d\n", thread_stats.threads[i], - thread_stats.index[i], - thread_stats.counts[i], - thread_stats.not_helper_counts[i]); + fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]); } event_storage_unlock(&global_event_storage); } @@ -805,7 +787,7 @@ jobject object, jclass object_klass, jlong size) { - add_thread_count(thread, 1, 1); + add_thread_count(thread); if (event_storage_get_compaction_required(&global_event_storage)) { event_storage_compact(&global_event_storage, jni_env); @@ -843,29 +825,6 @@ "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 0; - } - - if (check_error((*jvmti)->SetEventNotificationMode( - jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first), - "Set event notifications")) { - return 0; - } - - // Second thread should fail. - if (check_error((*jvmti)->SetEventNotificationMode( - jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second), - "Set event notifications")) { - return 0; - } - - return 1; -} - static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { jint res; @@ -949,14 +908,6 @@ enable_notifications(); } -JNIEXPORT jboolean JNICALL -Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env, - jclass cls, - jthread first, - jthread second) { - return enable_notifications_for_two_threads(first, second); -} - JNIEXPORT void JNICALL Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { check_error((*jvmti)->SetEventNotificationMode( @@ -1099,7 +1050,6 @@ JNIEXPORT jboolean JNICALL Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, jint num_threads) { - print_thread_stats(); // Ensure we got stacks from at least num_threads. return thread_stats.number_threads >= num_threads;