This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
[RFA] JVMTI/JDWP GetAllLoadedClasses
- From: Kyle Galloway <kgallowa at redhat dot com>
- To: Java Patch List <java-patches at gcc dot gnu dot org>
- Date: Tue, 24 Apr 2007 11:53:46 -0400
- Subject: [RFA] JVMTI/JDWP GetAllLoadedClasses
This patch implements the JVMTI and JDWP functions to get all loaded
classes in the VM. To accomplish this, each class is marked as loaded
in _Jv_PushClass, by calling ClassList::add () and stored as a referent
to an element of a list of WeakReferences. When retrieving the list of
loaded classes, the list is first checked to see if any of the classes
has been GCed (there is a WeakReference to null), which will remove its
entry from the list before it is returned. In order to avoid problems
with circular class loading(try to load a class, then try to add it to
the class list, whihc requires WeakReference to be loaded, which needs
to be stored in the list, whichi requires WeakReference to be
loaded...and so on), until it can be assured that WeakReference is
loaded (which is checked in ClassList::setup ()), the classes that have
been loaded are stored in a temporary linked list of jclass *. When
setup is called, these classes are transferred over to the WeakReference
list, and the memory used by this temporary list is freed.
ChangeLog
* jvmti.cc (_Jv_JVMTI_GetLoadedClasses): New method.
* prims.cc (_Jv_CreateJavaVM): Call ClassList::setup ().
* gnu/gcj/jvmti/ClassList.h: New file.
* gnu/gcj/jvmti/natClassList.cc: New file.
* gnu/classpath/jdwp/natVMVirtualMachine.cc (getAllLoadedClasses):
Implement.
* java/lang/natClassLosder.cc (_Jv_PushClass): Call ClassList::add ().
* Makefile.am: Add gnu/gcj/jvmti/natClassList.cc.
* Makefile.in: Regenerated.
Questions/comments/concerns?
Thanks,
Kyle
Index: jvmti.cc
===================================================================
--- jvmti.cc (revision 124076)
+++ jvmti.cc (working copy)
@@ -24,6 +24,7 @@
#include <gnu/gcj/runtime/BootClassLoader.h>
#include <gnu/gcj/jvmti/Breakpoint.h>
#include <gnu/gcj/jvmti/BreakpointManager.h>
+#include <gnu/gcj/jvmti/ClassList.h>
#include <java/lang/Class.h>
#include <java/lang/ClassLoader.h>
@@ -34,6 +35,7 @@
#include <java/lang/VMClassLoader.h>
#include <java/lang/reflect/Field.h>
#include <java/lang/reflect/Modifier.h>
+#include <java/util/ArrayList.h>
#include <java/util/Collection.h>
#include <java/util/HashMap.h>
#include <java/util/concurrent/locks/Lock.h>
@@ -1062,6 +1064,34 @@
}
static jvmtiError JNICALL
+_Jv_JVMTI_GetLoadedClasses (jvmtiEnv *env, jint *num_classes, jclass **classes)
+{
+ REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+ NULL_CHECK (num_classes);
+ NULL_CHECK (classes);
+
+ ::java::util::ArrayList *list
+ = gnu::gcj::jvmti::ClassList::getAllLoadedClasses ();
+
+ *num_classes = list->size ();
+
+ jvmtiError jerr = env->Allocate ((*num_classes) * sizeof (jclass),
+ reinterpret_cast<unsigned char **>
+ (classes));
+
+ if (jerr != JVMTI_ERROR_NONE)
+ return jerr;
+
+ for (int i = 0; i < *num_classes; i++)
+ {
+ jclass kls = reinterpret_cast<jclass> (list->get (i));
+ (*classes)[i] = kls;
+ }
+
+ return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
_Jv_JVMTI_GetMaxLocals (jvmtiEnv *env, jmethodID method, jint *max_locals)
{
REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
@@ -2065,7 +2095,7 @@
UNIMPLEMENTED, // GetBytecodes
_Jv_JVMTI_IsMethodNative, // IsMethodNative
_Jv_JVMTI_IsMethodSynthetic, // IsMethodSynthetic
- UNIMPLEMENTED, // GetLoadedClasses
+ _Jv_JVMTI_GetLoadedClasses, // GetLoadedClasses
_Jv_JVMTI_GetClassLoaderClasses, // GetClassLoaderClasses
UNIMPLEMENTED, // PopFrame
RESERVED, // reserved81
Index: prims.cc
===================================================================
--- prims.cc (revision 124075)
+++ prims.cc (working copy)
@@ -64,6 +64,7 @@
#include <java/io/PrintStream.h>
#include <java/lang/UnsatisfiedLinkError.h>
#include <java/lang/VirtualMachineError.h>
+#include <gnu/gcj/jvmti/ClassList.h>
#include <gnu/gcj/runtime/ExtensionClassLoader.h>
#include <gnu/gcj/runtime/FinalizerThread.h>
#include <execution.h>
@@ -1646,6 +1647,9 @@
// Set up the system class loader and the bootstrap class loader.
gnu::gcj::runtime::ExtensionClassLoader::initialize();
java::lang::VMClassLoader::initialize(JvNewStringLatin1(TOOLEXECLIBDIR));
+
+ // Initialize the class list for JVMTI.
+ gnu::gcj::jvmti::ClassList::setup ();
_Jv_RegisterBootstrapPackages();
Index: gnu/classpath/jdwp/natVMVirtualMachine.cc
===================================================================
--- gnu/classpath/jdwp/natVMVirtualMachine.cc (revision 124075)
+++ gnu/classpath/jdwp/natVMVirtualMachine.cc (working copy)
@@ -449,8 +450,22 @@
java::util::Collection *
gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
{
- using namespace ::java::util;
- return (Collection *) new ArrayList ();
+ jclass *classes = NULL;
+ jint num_classes;
+
+ jvmtiError jerr = _jdwp_jvmtiEnv->GetLoadedClasses (&num_classes, &classes);
+ if (jerr != JVMTI_ERROR_NONE)
+ throw_jvmti_error (jerr);
+
+ ::java::util::ArrayList *cls_lst = new ::java::util::ArrayList (num_classes);
+ for (int i = 0; i < num_classes; i++)
+ {
+ cls_lst->add (reinterpret_cast<jobject> (classes[i]));
+ }
+
+ _jdwp_jvmtiEnv->Deallocate (reinterpret_cast<unsigned char *> (classes));
+
+ return (::java::util::Collection *) (cls_lst);
}
jint
Index: gnu/gcj/jvmti/ClassList.h
===================================================================
--- gnu/gcj/jvmti/ClassList.h (revision 0)
+++ gnu/gcj/jvmti/ClassList.h (revision 0)
@@ -0,0 +1,57 @@
+#ifndef __gnu_gcj_jvmti_ClassList__
+#define __gnu_gcj_jvmti_ClassList__
+
+#define JDWP_ALL_CLASSES_STATE_LOCK_LIST 0x01
+#define JDWP_ALL_CLASSES_STATE_LOADED 0x02
+
+extern "Java"
+{
+ namespace gnu
+ {
+ namespace gcj
+ {
+ namespace jvmti
+ {
+ class ClassList;
+ }
+ }
+ }
+}
+
+typedef struct _Jv_TempClassListElement
+{
+ ::java::lang::Class *klass;
+ _Jv_TempClassListElement *next;
+} _Jv_TempClassListElement;
+
+typedef struct _Jv_ClassListElement
+{
+ ::java::lang::ref::WeakReference *ref;
+ _Jv_ClassListElement *next;
+} _Jv_ClassListElement;
+
+class gnu::gcj::jvmti::ClassList
+{
+private:
+ static _Jv_ClassListElement *_classes_head;
+ static _Jv_ClassListElement *_classes_tail;
+ static int flags;
+
+ static _Jv_TempClassListElement *t_list_head;
+ static _Jv_TempClassListElement *t_list_tail;
+
+ static ::java::lang::Object *mutex;
+
+ static void t_list_put (::java::lang::Class *klass);
+
+ static void removeDeadEntries ();
+
+public:
+ static void setup ();
+
+ static void add (jclass klass);
+
+ static ::java::util::ArrayList *getAllLoadedClasses ();
+};
+
+#endif /* __gnu_gcj_jvmti_ClassList__ */
Index: gnu/gcj/jvmti/natClassList.cc
===================================================================
--- gnu/gcj/jvmti/natClassList.cc (revision 0)
+++ gnu/gcj/jvmti/natClassList.cc (revision 0)
@@ -0,0 +1,158 @@
+#include <config.h>
+#include <gcj/cni.h>
+#include <gcj/method.h>
+#include <java-interp.h>
+#include <java-insns.h>
+#include <java-assert.h>
+#include <jvmti.h>
+#include <jvmti-int.h>
+
+#include <java/lang/Object.h>
+#include <java/lang/ref/WeakReference.h>
+#include <java/util/ArrayList.h>
+
+#include <gnu/gcj/jvmti/ClassList.h>
+
+// Initialize static variables.
+_Jv_ClassListElement *gnu::gcj::jvmti::ClassList::_classes_head = NULL;
+_Jv_ClassListElement *gnu::gcj::jvmti::ClassList::_classes_tail = NULL;
+int gnu::gcj::jvmti::ClassList::flags = 0;
+_Jv_TempClassListElement *gnu::gcj::jvmti::ClassList::t_list_head = NULL;
+_Jv_TempClassListElement *gnu::gcj::jvmti::ClassList::t_list_tail= NULL;
+::java::lang::Object *gnu::gcj::jvmti::ClassList::mutex = NULL;
+
+void
+gnu::gcj::jvmti::ClassList::t_list_put (::java::lang::Class *klass)
+{
+ if (t_list_head == NULL)
+ {
+ // Make a new list.
+ t_list_head
+ = reinterpret_cast<_Jv_TempClassListElement *>
+ (_Jv_MallocUnchecked (sizeof (_Jv_TempClassListElement)));
+ t_list_tail = t_list_head;
+ }
+ else
+ {
+ // Append to the list.
+ t_list_tail->next
+ = reinterpret_cast<_Jv_TempClassListElement *>
+ (_Jv_MallocUnchecked (sizeof (_Jv_TempClassListElement)));
+ t_list_tail = t_list_tail->next;
+ }
+
+ t_list_tail->klass = klass;
+ t_list_tail->next = NULL;
+}
+
+void
+gnu::gcj::jvmti::ClassList::setup ()
+{
+ using namespace java::util;
+ using namespace java::lang;
+
+ // Ensure WeakReference is loaded.
+ _Jv_InitClass (&::java::lang::ref::WeakReference::class$);
+
+ mutex = new ::java::lang::Object ();
+
+ JvSynchronize dummy (mutex);
+ flags |= JDWP_ALL_CLASSES_STATE_LOCK_LIST;
+
+ _Jv_TempClassListElement *t_list_ptr = t_list_head;
+
+ while (t_list_ptr != NULL)
+ {
+ if (_classes_head == NULL)
+ {
+ _classes_head = reinterpret_cast<_Jv_ClassListElement *>
+ (_Jv_AllocBytes (sizeof (_Jv_ClassListElement)));
+ _classes_tail = _classes_head;
+ }
+ else
+ {
+ _classes_tail->next = reinterpret_cast<_Jv_ClassListElement *>
+ (_Jv_AllocBytes (sizeof (_Jv_ClassListElement)));
+ _classes_tail = _classes_tail->next;
+ }
+
+ _classes_tail->ref = new ::java::lang::ref::WeakReference (t_list_ptr->klass);
+ _classes_tail->next = NULL;
+ t_list_ptr = t_list_ptr->next;
+
+ // Free the entry since it is no longer needed.
+ _Jv_Free (t_list_head);
+ t_list_head = t_list_ptr;
+ }
+
+ t_list_head = NULL;
+ t_list_ptr = NULL;
+
+ flags |= JDWP_ALL_CLASSES_STATE_LOADED;
+}
+
+void
+gnu::gcj::jvmti::ClassList::add (jclass klass)
+{
+ if (!flags)
+ {
+ t_list_put (klass);
+ }
+ else if (flags & JDWP_ALL_CLASSES_STATE_LOCK_LIST)
+ {
+ JvSynchronize dummy (mutex);
+
+ _classes_tail->next = reinterpret_cast<_Jv_ClassListElement *>
+ (_Jv_AllocBytes (sizeof (_Jv_ClassListElement)));
+ _classes_tail = _classes_tail->next;
+ _classes_tail->ref = new ::java::lang::ref::WeakReference (klass);
+ _classes_tail->next = NULL;
+ }
+}
+
+void
+gnu::gcj::jvmti::ClassList::removeDeadEntries ()
+{
+ if (flags & JDWP_ALL_CLASSES_STATE_LOADED)
+ {
+ JvSynchronize dummy (mutex);
+
+ // First remove any dead entries from the front of the list so we know
+ // that the head of the list still contains a valid reference.
+ while (_classes_head != NULL && _classes_head->ref->get () == NULL)
+ _classes_head = _classes_head->next;
+
+ _Jv_ClassListElement *classes_ptr = _classes_head;
+
+ // We know that _classes_head points to a valid elelment, so go through
+ // the list removing any entries which reference collected Classes.
+ while (classes_ptr != NULL && classes_ptr->next != NULL)
+ {
+ if (classes_ptr->next->ref->get () == NULL)
+ classes_ptr->next = classes_ptr->next->next;
+ else
+ classes_ptr = classes_ptr->next;
+ }
+ }
+}
+
+::java::util::ArrayList *
+gnu::gcj::jvmti::ClassList::getAllLoadedClasses ()
+{
+ ::java::util::ArrayList *list = new ::java::util::ArrayList ();
+ if (flags & JDWP_ALL_CLASSES_STATE_LOADED)
+ {
+ removeDeadEntries ();
+
+ JvSynchronize dummy (mutex);
+ _Jv_ClassListElement *classes_ptr = _classes_head;
+
+ while (classes_ptr != NULL)
+ {
+ list->add (classes_ptr->ref->get ());
+ classes_ptr = classes_ptr->next;
+ }
+ }
+
+ return list;
+}
Index: java/lang/natClassLoader.cc
===================================================================
--- java/lang/natClassLoader.cc (revision 124075)
+++ java/lang/natClassLoader.cc (working copy)
@@ -42,6 +42,7 @@
#include <java/io/Serializable.h>
#include <java/lang/Cloneable.h>
#include <java/util/HashMap.h>
+#include <gnu/gcj/jvmti/ClassList.h>
#include <gnu/gcj/runtime/BootClassLoader.h>
#include <gnu/gcj/runtime/SystemClassLoader.h>
@@ -654,6 +655,8 @@
void
_Jv_PushClass (jclass k)
{
+ gnu::gcj::jvmti::ClassList::add (k);
+
JvSynchronize sync (&java::lang::Class::class$);
jclass tmp = stack_head;
stack_head = k;
Index: Makefile.am
===================================================================
--- Makefile.am (revision 124075)
+++ Makefile.am (working copy)
@@ -851,6 +851,7 @@
gnu/gcj/io/natSimpleSHSStream.cc \
gnu/gcj/io/shs.cc \
gnu/gcj/jvmti/natBreakpoint.cc \
+gnu/gcj/jvmti/natClassList.cc \
gnu/gcj/jvmti/natExceptionEvent.cc \
gnu/gcj/runtime/natFinalizerThread.cc \
gnu/gcj/runtime/natSharedLibLoader.cc \