8201655: Add thread-enabled support for the Heap Sampling
Summary: Added thread-enabled support
Reviewed-by: amenkov, sspitsyn
--- a/src/hotspot/share/prims/jvmti.xml Fri Dec 14 14:17:22 2018 -0500
+++ b/src/hotspot/share/prims/jvmti.xml Fri Dec 14 13:13:06 2018 -0800
@@ -13595,7 +13595,7 @@
</event>
<event label="Sampled Object Allocation"
- id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11">
+ id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" filtered="thread" num="86" since="11">
<description>
Sent when an allocated object is sampled.
By default, the sampling interval is set to 512KB. The sampling is semi-random to avoid
--- a/src/hotspot/share/prims/jvmtiEventController.cpp Fri Dec 14 14:17:22 2018 -0500
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp Fri Dec 14 13:13:06 2018 -0800
@@ -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 |
--- a/src/hotspot/share/prims/jvmtiExport.cpp Fri Dec 14 14:17:22 2018 -0500
+++ b/src/hotspot/share/prims/jvmtiExport.cpp Fri Dec 14 13:13:06 2018 -0800
@@ -2567,6 +2567,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)));
@@ -2575,14 +2580,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;
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java Fri Dec 14 14:17:22 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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<ThreadInformation> 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.");
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java Fri Dec 14 13:13:06 2018 -0800
@@ -0,0 +1,66 @@
+/*
+ * 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
+ */
+
+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<ThreadInformation> 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);
+ }
+}
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c Fri Dec 14 14:17:22 2018 -0500
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c Fri Dec 14 13:13:06 2018 -0800
@@ -720,101 +720,86 @@
#define MAX_THREADS 500
typedef struct ThreadStats {
- int number_threads;
+ int thread_count;
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) {
- int i;
+JNIEXPORT jboolean JNICALL
+Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom(
+ JNIEnv* env, jclass cls, jthread thread) {
jvmtiThreadInfo info;
- const char* name;
- char* end;
- int idx;
- int err;
+ jvmtiError err;
+ char* expected_name;
+ int found_thread = FALSE;
+
+ err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
+ expected_name = info.name;
- if (lock) {
- event_storage_lock(&global_event_storage);
+ if (err != JVMTI_ERROR_NONE) {
+ fprintf(stderr, "Failed to get thread information\n");
+ return FALSE;
+ }
+
+ if (thread_stats.thread_count != 1) {
+ fprintf(stderr, "Wrong thread number: %d (expected 1)\n",
+ thread_stats.thread_count);
+ return FALSE;
}
- 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 (strcmp(expected_name, thread_stats.threads[0]) != 0) {
+ fprintf(stderr, "Unexpected thread name: '%s' (expected '%s')\n",
+ thread_stats.threads[0], expected_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
- if (lock) {
- event_storage_unlock(&global_event_storage);
- }
+static void add_thread_count(jthread thread) {
+ int i;
+ jvmtiThreadInfo info;
+ jvmtiError err;
+
+ err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
+ if (err != JVMTI_ERROR_NONE) {
+ fprintf(stderr, "Thread info for %p failed, ignoring thread count\n",
+ thread);
+ return;
+ }
+
+ event_storage_lock(&global_event_storage);
+ for (i = 0; i < thread_stats.thread_count; i++) {
+ 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.thread_count] = info.name;
+ thread_stats.counts[thread_stats.thread_count]++;
+ thread_stats.thread_count++;
+ 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]);
+ for (i = 0; i < thread_stats.thread_count; i++) {
+ fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]);
}
event_storage_unlock(&global_event_storage);
}
@@ -826,7 +811,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);
@@ -864,29 +849,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;
@@ -970,14 +932,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(
@@ -1130,10 +1084,9 @@
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;
+ return thread_stats.thread_count >= num_threads;
}
JNIEXPORT