# HG changeset patch # User farvidsson # Date 1382601722 -7200 # Node ID e64f1fe9756b1b9666635344973ad94e3f13217a # Parent b658cfe3585725fcefaf3b8da108d68175ee3703 8024423: JVMTI: GetLoadedClasses doesn't enumerate anonymous classes Summary: Rewrite of the getLoadedClasses() method implementation to include anonymous classes. Reviewed-by: coleenp, sspitsyn diff -r b658cfe35857 -r e64f1fe9756b src/share/vm/classfile/classLoaderData.cpp --- 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()) { diff -r b658cfe35857 -r e64f1fe9756b src/share/vm/classfile/classLoaderData.hpp --- 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. diff -r b658cfe35857 -r e64f1fe9756b src/share/vm/oops/instanceKlass.cpp --- 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; diff -r b658cfe35857 -r e64f1fe9756b src/share/vm/prims/jvmtiGetLoadedClasses.cpp --- 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 _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