8024423: JVMTI: GetLoadedClasses doesn't enumerate anonymous classes
Summary: Rewrite of the getLoadedClasses() method implementation to include anonymous classes.
Reviewed-by: coleenp, sspitsyn
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Wed Oct 23 10:24:28 2013 +0200
+++ b/hotspot/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/hotspot/src/share/vm/classfile/classLoaderData.hpp Wed Oct 23 10:24:28 2013 +0200
+++ b/hotspot/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/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Oct 23 10:24:28 2013 +0200
+++ b/hotspot/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/hotspot/src/share/vm/prims/jvmtiGetLoadedClasses.cpp Wed Oct 23 10:24:28 2013 +0200
+++ b/hotspot/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