8201655: Add thread-enabled support for the Heap Sampling
authorjcbeyler
Fri, 14 Dec 2018 13:13:06 -0800
changeset 53032 89bb635ed093
parent 53031 0873841d1669
child 53033 dcbb71b9e7c0
8201655: Add thread-enabled support for the Heap Sampling Summary: Added thread-enabled support Reviewed-by: amenkov, sspitsyn
src/hotspot/share/prims/jvmti.xml
src/hotspot/share/prims/jvmtiEventController.cpp
src/hotspot/share/prims/jvmtiExport.cpp
test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventsForTwoThreadsTest.java
test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java
test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c
--- 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