changeset 5710:e64f1fe9756b

8024423: JVMTI: GetLoadedClasses doesn't enumerate anonymous classes Summary: Rewrite of the getLoadedClasses() method implementation to include anonymous classes. Reviewed-by: coleenp, sspitsyn
author farvidsson
date Thu, 24 Oct 2013 10:02:02 +0200
parents b658cfe35857
children d70a665e25d7
files src/share/vm/classfile/classLoaderData.cpp src/share/vm/classfile/classLoaderData.hpp src/share/vm/oops/instanceKlass.cpp src/share/vm/prims/jvmtiGetLoadedClasses.cpp
diffstat 4 files changed, 99 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/classfile/classLoaderData.cpp	Wed Oct 23 10:24:28 2013 +0200
+++ b/src/share/vm/classfile/classLoaderData.cpp	Thu Oct 24 10:02:02 2013 +0200
@@ -131,6 +131,17 @@
   }
 }
 
+void ClassLoaderData::loaded_classes_do(KlassClosure* klass_closure) {
+  // Lock to avoid classes being modified/added/removed during iteration
+  MutexLockerEx ml(metaspace_lock(),  Mutex::_no_safepoint_check_flag);
+  for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
+    // Do not filter ArrayKlass oops here...
+    if (k->oop_is_array() || (k->oop_is_instance() && InstanceKlass::cast(k)->is_loaded())) {
+      klass_closure->do_klass(k);
+    }
+  }
+}
+
 void ClassLoaderData::classes_do(void f(InstanceKlass*)) {
   for (Klass* k = _klasses; k != NULL; k = k->next_link()) {
     if (k->oop_is_instance()) {
@@ -600,6 +611,12 @@
   }
 }
 
+void ClassLoaderDataGraph::loaded_classes_do(KlassClosure* klass_closure) {
+  for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
+    cld->loaded_classes_do(klass_closure);
+  }
+}
+
 void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
   for (ClassLoaderData* cld = _unloading; cld != NULL; cld = cld->next()) {
--- a/src/share/vm/classfile/classLoaderData.hpp	Wed Oct 23 10:24:28 2013 +0200
+++ b/src/share/vm/classfile/classLoaderData.hpp	Thu Oct 24 10:02:02 2013 +0200
@@ -78,6 +78,7 @@
   static void keep_alive_oops_do(OopClosure* blk, KlassClosure* klass_closure, bool must_claim);
   static void classes_do(KlassClosure* klass_closure);
   static void classes_do(void f(Klass* const));
+  static void loaded_classes_do(KlassClosure* klass_closure);
   static void classes_unloading_do(void f(Klass* const));
   static bool do_unloading(BoolObjectClosure* is_alive);
 
@@ -186,6 +187,7 @@
   bool keep_alive() const       { return _keep_alive; }
   bool is_alive(BoolObjectClosure* is_alive_closure) const;
   void classes_do(void f(Klass*));
+  void loaded_classes_do(KlassClosure* klass_closure);
   void classes_do(void f(InstanceKlass*));
 
   // Deallocate free list during class unloading.
--- a/src/share/vm/oops/instanceKlass.cpp	Wed Oct 23 10:24:28 2013 +0200
+++ b/src/share/vm/oops/instanceKlass.cpp	Thu Oct 24 10:02:02 2013 +0200
@@ -2393,15 +2393,38 @@
 
 
 const char* InstanceKlass::signature_name() const {
+  int hash_len = 0;
+  char hash_buf[40];
+
+  // If this is an anonymous class, append a hash to make the name unique
+  if (is_anonymous()) {
+    assert(EnableInvokeDynamic, "EnableInvokeDynamic was not set.");
+    intptr_t hash = (java_mirror() != NULL) ? java_mirror()->identity_hash() : 0;
+    sprintf(hash_buf, "/" UINTX_FORMAT, (uintx)hash);
+    hash_len = (int)strlen(hash_buf);
+  }
+
+  // Get the internal name as a c string
   const char* src = (const char*) (name()->as_C_string());
   const int src_length = (int)strlen(src);
-  char* dest = NEW_RESOURCE_ARRAY(char, src_length + 3);
-  int src_index = 0;
+
+  char* dest = NEW_RESOURCE_ARRAY(char, src_length + hash_len + 3);
+
+  // Add L as type indicator
   int dest_index = 0;
   dest[dest_index++] = 'L';
-  while (src_index < src_length) {
+
+  // Add the actual class name
+  for (int src_index = 0; src_index < src_length; ) {
     dest[dest_index++] = src[src_index++];
   }
+
+  // If we have a hash, append it
+  for (int hash_index = 0; hash_index < hash_len; ) {
+    dest[dest_index++] = hash_buf[hash_index++];
+  }
+
+  // Add the semicolon and the NULL
   dest[dest_index++] = ';';
   dest[dest_index] = '\0';
   return dest;
--- a/src/share/vm/prims/jvmtiGetLoadedClasses.cpp	Wed Oct 23 10:24:28 2013 +0200
+++ b/src/share/vm/prims/jvmtiGetLoadedClasses.cpp	Thu Oct 24 10:02:02 2013 +0200
@@ -29,8 +29,43 @@
 #include "runtime/thread.hpp"
 
 
+// The closure for GetLoadedClasses
+class LoadedClassesClosure : public KlassClosure {
+private:
+  Stack<jclass, mtInternal> _classStack;
+  JvmtiEnv* _env;
 
-// The closure for GetLoadedClasses and GetClassLoaderClasses
+public:
+  LoadedClassesClosure(JvmtiEnv* env) {
+    _env = env;
+  }
+
+  void do_klass(Klass* k) {
+    // Collect all jclasses
+    _classStack.push((jclass) _env->jni_reference(k->java_mirror()));
+  }
+
+  int extract(jclass* result_list) {
+    // The size of the Stack will be 0 after extract, so get it here
+    int count = (int)_classStack.size();
+    int i = count;
+
+    // Pop all jclasses, fill backwards
+    while (!_classStack.is_empty()) {
+      result_list[--i] = _classStack.pop();
+    }
+
+    // Return the number of elements written
+    return count;
+  }
+
+  // Return current size of the Stack
+  int get_count() {
+    return (int)_classStack.size();
+  }
+};
+
+// The closure for GetClassLoaderClasses
 class JvmtiGetLoadedClassesClosure : public StackObj {
   // Since the SystemDictionary::classes_do callback
   // doesn't pass a closureData pointer,
@@ -165,19 +200,6 @@
     }
   }
 
-  // Finally, the static methods that are the callbacks
-  static void increment(Klass* k) {
-    JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this();
-    if (that->get_initiatingLoader() == NULL) {
-      for (Klass* l = k; l != NULL; l = l->array_klass_or_null()) {
-        that->set_count(that->get_count() + 1);
-      }
-    } else if (k != NULL) {
-      // if initiating loader not null, just include the instance with 1 dimension
-      that->set_count(that->get_count() + 1);
-    }
-  }
-
   static void increment_with_loader(Klass* k, ClassLoaderData* loader_data) {
     JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this();
     oop class_loader = loader_data->class_loader();
@@ -196,24 +218,6 @@
     }
   }
 
-  static void add(Klass* k) {
-    JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this();
-    if (that->available()) {
-      if (that->get_initiatingLoader() == NULL) {
-        for (Klass* l = k; l != NULL; l = l->array_klass_or_null()) {
-          oop mirror = l->java_mirror();
-          that->set_element(that->get_index(), mirror);
-          that->set_index(that->get_index() + 1);
-        }
-      } else if (k != NULL) {
-        // if initiating loader not null, just include the instance with 1 dimension
-        oop mirror = k->java_mirror();
-        that->set_element(that->get_index(), mirror);
-        that->set_index(that->get_index() + 1);
-      }
-    }
-  }
-
   static void add_with_loader(Klass* k, ClassLoaderData* loader_data) {
     JvmtiGetLoadedClassesClosure* that = JvmtiGetLoadedClassesClosure::get_this();
     if (that->available()) {
@@ -255,39 +259,30 @@
 
 jvmtiError
 JvmtiGetLoadedClasses::getLoadedClasses(JvmtiEnv *env, jint* classCountPtr, jclass** classesPtr) {
-  // Since SystemDictionary::classes_do only takes a function pointer
-  // and doesn't call back with a closure data pointer,
-  // we can only pass static methods.
 
-  JvmtiGetLoadedClassesClosure closure;
+  LoadedClassesClosure closure(env);
   {
     // To get a consistent list of classes we need MultiArray_lock to ensure
-    // array classes aren't created, and SystemDictionary_lock to ensure that
-    // classes aren't added to the system dictionary,
+    // array classes aren't created.
     MutexLocker ma(MultiArray_lock);
-    MutexLocker sd(SystemDictionary_lock);
+
+    // Iterate through all classes in ClassLoaderDataGraph
+    // and collect them using the LoadedClassesClosure
+    ClassLoaderDataGraph::loaded_classes_do(&closure);
+  }
 
-    // First, count the classes
-    SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::increment);
-    Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::increment);
-    // Next, fill in the classes
-    closure.allocate();
-    SystemDictionary::classes_do(&JvmtiGetLoadedClassesClosure::add);
-    Universe::basic_type_classes_do(&JvmtiGetLoadedClassesClosure::add);
-    // Drop the SystemDictionary_lock, so the results could be wrong from here,
-    // but we still have a snapshot.
+  // Return results by extracting the collected contents into a list
+  // allocated via JvmtiEnv
+  jclass* result_list;
+  jvmtiError error = env->Allocate(closure.get_count() * sizeof(jclass),
+                               (unsigned char**)&result_list);
+
+  if (error == JVMTI_ERROR_NONE) {
+    int count = closure.extract(result_list);
+    *classCountPtr = count;
+    *classesPtr = result_list;
   }
-  // Post results
-  jclass* result_list;
-  jvmtiError err = env->Allocate(closure.get_count() * sizeof(jclass),
-                                 (unsigned char**)&result_list);
-  if (err != JVMTI_ERROR_NONE) {
-    return err;
-  }
-  closure.extract(env, result_list);
-  *classCountPtr = closure.get_count();
-  *classesPtr = result_list;
-  return JVMTI_ERROR_NONE;
+  return error;
 }
 
 jvmtiError