< prev index next >

test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitor.c

Print this page
rev 49244 : [mq]: event-only
rev 49246 : [mq]: event4
rev 49247 : [mq]: event5

@@ -20,10 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 #include <assert.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jvmti.h"
 

@@ -48,19 +49,29 @@
 #define PRINT_OUT 1
 #define MAX_TRACES 400
 
 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 {
   const char *name;
   const char *signature;

@@ -108,11 +119,11 @@
     return -1;
   }
 }
 
 static jboolean check_sample_content(JNIEnv *env,
-                                     LiveObjectTrace* trace,
+                                     ObjectTrace* trace,
                                      ExpectedContentFrame *expected,
                                      size_t expected_count,
                                      int print_out_comparisons) {
   if (expected_count > trace->frame_count) {
     return FALSE;

@@ -220,77 +231,204 @@
 
 // Internal storage system implementation.
 
 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));
   free(storage->live_objects);
   storage->live_objects = new_objects;
 
   storage->live_object_size = new_max;
 }
 
 static void event_storage_add(EventStorage* storage,
+                              JNIEnv* jni,
                               jthread thread,
                               jobject object,
                               jclass klass,
                               jlong size) {
   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);
     }
     assert(storage->live_object_count < storage->live_object_size);
 
     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.
 
 static const char *EXC_CNAME = "java/lang/Exception";

@@ -347,14 +485,30 @@
                                 JNIEnv* jni_env,
                                 jthread thread,
                                 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");
 }
 

@@ -367,20 +521,25 @@
   if (res != JNI_OK || jvmti == NULL) {
     printf("    Error: wrong result of a valid call to GetEnv!\n");
     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));
   // Get line numbers, sample heap, sample events, and filename for the test.
   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;
   }
 
   if (enable_notifications()) {

@@ -395,42 +554,66 @@
   return JNI_OK;
 }
 
 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
 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
   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();
 }
 
 JNIEXPORT void JNICALL
 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
   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
 Java_MyPackage_HeapMonitor_obtainedEvents(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_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);
 }
 

@@ -443,15 +626,95 @@
   if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps),
                   "Add capabilities\n")){
     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
< prev index next >