This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA] JDWP LocalVariables


This patch implements the functions to allow JDWP to get and set local variables through JVMTI. It fills in the functions getValue and setValue in gnu/classpath/jdwp/natVMFrame.cc. When debugging, when a method is called, the VM will now set all the variable slots in its frame info to 'x' indicating they are invalid. It must then go back and set the types for the method arguments, this is done based on the method signature. When JDWP calls one of these functions to do a local variable operation, a call is made to the appropriate JVMTI function GetLocalXXXX where "XXXX" is the type of the variable to get. The type is determined either by the tag of the Value type passed in (if setting) or by the sig byte passed by JDWP in the case of getting. After this call, the JVMTI error code is checked to determine if the value was get/set successfully, and if not the error is translated for JDWP by the checkJVMTIError function since all of these calls return the same set of possible errors. Note that there are hidden errors which have error codes assosiated with them, but are not in the JDWP spec for these particular functions (TypeMismatch and InvalidSlot for example), the addition of these has been verified against output from Sun's VM.

ChecngeLog
2007-03-28  Kyle Galloway  <kgallowa@redhat.com>

* interpret-run.cc: Add code to properly set up variable slots when debugging.
* gnu/classpath/jdwp/natVMFrame.cc (checkJVMTIError): New function.
(getObjectJVMTI): New function.
(setObjectJVMTI): New function.
(getIntJVMTI): New function.
(setIntJVMTI): New function.
(getLongJVMTI): New function.
(setLongJVMTI): New function.
(getFloatJVMTI): New function.
(setFloatJVMTI): New function.
(getDoubleJVMTI): New function.
(setDoubleJVMTI): New function.
(getFrameDepth): New function.
(getValue): Implement.
(setValue): Implement.


Questions/comments/concerns?

Thanks,
Kyle
Index: libjava/gnu/classpath/jdwp/natVMFrame.cc
===================================================================
--- libjava/gnu/classpath/jdwp/natVMFrame.cc	(revision 123266)
+++ libjava/gnu/classpath/jdwp/natVMFrame.cc	(working copy)
@@ -8,23 +8,316 @@
 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
 details. */
 
+#include <config.h>
 #include <gcj/cni.h>
 #include <jvm.h>
+#include <jvmti.h>
+#include "jvmti-int.h"
 
+#include <java-interp.h>
+
 #include <gnu/classpath/jdwp/VMFrame.h>
+#include <gnu/classpath/jdwp/VMVirtualMachine.h>
+#include <gnu/classpath/jdwp/exception/InvalidFrameException.h>
+#include <gnu/classpath/jdwp/exception/InvalidSlotException.h>
+#include <gnu/classpath/jdwp/exception/InvalidThreadException.h>
+#include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
+#include <gnu/classpath/jdwp/exception/TypeMismatchException.h>
+#include <gnu/classpath/jdwp/util/NullObject.h>
+#include <gnu/classpath/jdwp/value/ByteValue.h>
+#include <gnu/classpath/jdwp/value/BooleanValue.h>
+#include <gnu/classpath/jdwp/value/CharValue.h>
+#include <gnu/classpath/jdwp/value/DoubleValue.h>
+#include <gnu/classpath/jdwp/value/FloatValue.h>
+#include <gnu/classpath/jdwp/value/IntValue.h>
+#include <gnu/classpath/jdwp/value/LongValue.h>
+#include <gnu/classpath/jdwp/value/ObjectValue.h>
+#include <gnu/classpath/jdwp/value/ShortValue.h>
 #include <gnu/classpath/jdwp/value/Value.h>
+#include <gnu/classpath/jdwp/value/VoidValue.h>
 
 using namespace java::lang;
+using namespace gnu::classpath::jdwp;
+using namespace gnu::classpath::jdwp::exception;
 
-gnu::classpath::jdwp::value::Value *
-gnu::classpath::jdwp::VMFrame::getValue (MAYBE_UNUSED jint slot,
-					 MAYBE_UNUSED jbyte tag)
+
+// All the jvmti GetLocalXX and SetLocalXX functions return the same potential
+// errors, so this function handles them all and throws the appropriate JDWP
+// exception.
+void
+checkJVMTIError (jvmtiEnv *env, jthread thread, jvmtiError jerr, jint slot,
+                 jbyte sig)
 {
-  return 0;
+  if (jerr != JVMTI_ERROR_NONE)
+    {
+      char *error;
+      env->GetErrorName (jerr, &error);
+      String *msg = reinterpret_cast<String *> (JvNewStringUTF (error));
+      env->Deallocate ((unsigned char *) error);
+      
+      if (jerr == JVMTI_ERROR_INVALID_THREAD)
+        throw new InvalidThreadException ((jlong) thread);
+      else if (jerr == JVMTI_ERROR_NO_MORE_FRAMES)
+        throw new InvalidFrameException (msg);
+      else if (jerr == JVMTI_ERROR_INVALID_SLOT)
+        throw new InvalidSlotException (slot);
+      else if (jerr == JVMTI_ERROR_TYPE_MISMATCH)
+        throw new TypeMismatchException (sig);
+      else
+        throw new JdwpInternalErrorException (msg);
+    }
 }
 
+
+jobject
+getObjectJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig)
+{
+  jobject value;
+  jvmtiError jerr = env->GetLocalObject (thread, depth, slot, &value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+  
+  return value;
+}
+
 void
-gnu::classpath::jdwp::VMFrame::setValue (MAYBE_UNUSED jint slot,
-					 MAYBE_UNUSED gnu::classpath::jdwp::value::Value *value)
+setObjectJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth,
+                jbyte sig, jobject value)
 {
+  if (value->getClass ()->isAssignableFrom (&util::NullObject::class$))
+    value = NULL;
+	
+  jvmtiError jerr = env->SetLocalObject (thread, depth, slot, value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
 }
+
+jint
+getIntJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig)
+{
+  jint value;
+  jvmtiError jerr = env->GetLocalInt (thread, depth, slot, &value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+  return value;
+}
+
+void
+setIntJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig,
+             jint value)
+{
+  jvmtiError jerr = env->SetLocalInt (thread, depth, slot, value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+}
+
+jlong
+getLongJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig)
+{
+  jlong value;
+  jvmtiError jerr = env->GetLocalLong (thread, depth, slot, &value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+  
+  return value;
+}
+
+void
+setLongJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig,
+              jlong value)
+{
+  jvmtiError jerr = env->SetLocalInt (thread, depth, slot, value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+}
+
+jfloat
+getFloatJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig)
+{
+  jfloat value;
+  jvmtiError jerr = env->GetLocalFloat (thread, depth, slot, &value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+  
+  return value;
+}
+
+void
+setFloatJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig,
+               jfloat value)
+{
+  jvmtiError jerr = env->SetLocalInt (thread, depth, slot, value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+}
+
+jdouble
+getDoubleJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth,
+                jbyte sig)
+{
+  jdouble value;
+  jvmtiError jerr = env->GetLocalDouble (thread, depth, slot, &value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+  
+  return value;
+}
+
+void
+setDoubleJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, 
+                jbyte sig, jdouble value)
+{
+  jvmtiError jerr = env->SetLocalInt (thread, depth, slot, value);
+  
+  checkJVMTIError (env, thread, jerr, slot, sig);
+}
+
+// This is necessary since JVMTI requires a stack depth as a parameter in all
+// its local variable functions.  Since JDWP needs frameids, we have to run
+// through the call stack to translate these ids into the parameters JVMTI
+// wants.
+jint
+getFrameDepth (_Jv_Frame *frame)
+{
+  jint depth = 0;
+  _Jv_Frame *top_frame = (_Jv_Frame *) frame->thread->frame;
+  jint num_frames = VMVirtualMachine::getFrameCount (frame->thread);
+  
+  while (frame != top_frame)
+    {
+      top_frame = top_frame->next;
+      depth++;
+      
+      if (depth >= num_frames || top_frame == NULL)
+        throw new InvalidFrameException ((jlong) frame);
+    }
+  
+  return depth;
+}
+
+using namespace gnu::classpath::jdwp::value;
+
+Value *
+gnu::classpath::jdwp::VMFrame::getValue (jint slot, jbyte sig)
+{
+  _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (id);
+  jint depth = getFrameDepth (frame);
+  jthread thread = reinterpret_cast<jthread> (frame->thread);
+  jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv ();
+  
+  Value *value = NULL;
+
+  switch (sig)
+    {
+    case 'B':
+      value = new ByteValue ((jbyte) getIntJVMTI (env, thread, slot, depth, 
+                                                  sig));
+      break;
+    case 'Z':
+      value = new BooleanValue ((jboolean) getIntJVMTI (env, thread, slot,
+                                                        depth, sig));
+      break;
+    case 'C':
+      value = new CharValue ((jchar) getIntJVMTI (env, thread, slot, depth,
+                                                  sig));
+      break;
+    case 'S':
+      value = new ShortValue ((jshort) getIntJVMTI (env, thread, slot, depth,
+                                                    sig));
+      break;
+    case 'I':
+      value = new IntValue (getIntJVMTI (env, thread, slot, depth, sig));
+      break;
+    case 'J':
+      value = new LongValue (getLongJVMTI (env, thread, slot, depth, sig));
+      break;
+    case 'F':
+      value = new FloatValue (getFloatJVMTI (env, thread, slot, depth, sig));
+      break;
+    case 'D':
+      value = new DoubleValue (getDoubleJVMTI (env, thread, slot, depth, sig));
+      break;
+    case 'V':
+      value = new VoidValue ();
+      break;
+    default:
+      Object *obj = getObjectJVMTI (env, thread, slot, depth, sig);
+      if (obj == NULL)
+        obj = new util::NullObject ();
+      value = new ObjectValue (obj);
+      break;
+    }
+  
+  return value;
+}
+
+void
+gnu::classpath::jdwp::VMFrame::setValue (jint slot, Value* value)
+{	
+  jbyte sig = value->getTag ();
+  
+  _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (id);
+  jint depth = getFrameDepth (frame);
+  jthread thread = reinterpret_cast<jthread> (frame->thread);
+  jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv ();
+  
+  switch (sig)
+    {
+    case 'B':
+      {
+        ByteValue *val = reinterpret_cast<ByteValue *> (value);
+        setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ());
+        break;
+      }
+    case 'Z':
+      {
+        BooleanValue *val = reinterpret_cast<BooleanValue *> (value);
+        setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ());
+        break;
+      }
+    case 'C':
+      {
+        CharValue *val = reinterpret_cast<CharValue *> (value);
+        setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ());
+        break;
+      }
+    case 'S':
+      {
+        ShortValue *val = reinterpret_cast<ShortValue *> (value);
+        setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ());
+        break;
+      }
+    case 'I':
+      {
+        IntValue *val = reinterpret_cast<IntValue *> (value);
+        setIntJVMTI (env, thread, slot, depth, sig, val->getValue ());
+        break;
+      }
+    case 'J':
+      {
+        LongValue *val = reinterpret_cast<LongValue *> (value);
+        setLongJVMTI (env, thread, slot, depth, sig, val->getValue ());
+        break;
+      }
+    case 'F':
+      {
+        FloatValue *val = reinterpret_cast<FloatValue *> (value);
+        setFloatJVMTI (env, thread, slot, depth, sig, val->getValue ());
+        break;
+      }
+    case 'D':
+      {
+        DoubleValue *val = reinterpret_cast<DoubleValue *> (value);
+        setDoubleJVMTI (env, thread, slot, depth, sig, val->getValue ());
+        break;
+      }
+    case 'V':
+      break;
+    default:
+      {
+        ObjectValue *val = reinterpret_cast<ObjectValue *> (value);
+        setObjectJVMTI (env, thread, slot, depth, sig, val->getObject());
+        break;
+      }
+    }
+}
Index: libjava/interpret-run.cc
===================================================================
--- libjava/interpret-run.cc	(revision 123266)
+++ libjava/interpret-run.cc	(working copy)
@@ -27,12 +27,88 @@
 
   _Jv_word locals[meth->max_locals];
 
-#ifdef DEBUG  
+#ifdef DEBUG
+  // This is the information needed to get and set local variables with
+  // proper type checking.
   frame_desc.locals = locals;
   char locals_type[meth->max_locals];
+  frame_desc.locals_type = locals_type;
+  
+  // Set all slots as invalid until they are written to.
   memset (locals_type, 'x', meth->max_locals);
-  frame_desc.locals_type = locals_type;
-#endif
+  
+  // We need to set the local variable types for the method arguments since
+  // they are valid at invocation.
+  
+  _Jv_Method *method = meth->get_method ();
+  int type_ctr = 0;
+  
+  // If the method is non-static, we need to set the type for the "this" pointer.
+  if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0)
+    {
+      frame_desc.locals_type[0] = 'o';
+      type_ctr++;
+    }
+  
+  // Now parse the method signature to set the types of the other arguments.  
+  int sig_len = method->signature->len ();
+  char *signature = method->signature->chars ();
+  for (int i = 0; signature[i] != ')' && i <= sig_len; i++)
+    {
+      if (signature[i] == 'Z' || signature[i] == 'B' || signature[i] == 'C' 
+          || signature[i] == 'S' || signature[i] == 'I')
+        {
+          frame_desc.locals_type[type_ctr] = 'i';
+          type_ctr++;
+          continue;
+        }
+      else if (signature[i] == 'F')
+        {
+          frame_desc.locals_type[type_ctr] = 'f';
+          type_ctr++;
+          continue;
+        }
+      else if (signature[i] == 'J')
+        {
+          frame_desc.locals_type[type_ctr] = 'l';
+          frame_desc.locals_type[type_ctr+1] = 'x';
+          type_ctr += 2;
+          continue;
+        }
+      else if (signature[i] == 'D')
+        {
+          frame_desc.locals_type[type_ctr] = 'd';
+          frame_desc.locals_type[type_ctr+1] = 'x';
+          type_ctr += 2;
+          continue;
+        }
+      else if (signature[i] == 'L')
+        {
+          frame_desc.locals_type[type_ctr] = 'o';
+          type_ctr++;
+          while (signature[i] != ';')
+            i++;
+          continue;
+        }
+      else if (signature[i] == '[')
+        {
+          frame_desc.locals_type[type_ctr] = 'o';
+          type_ctr++;
+          
+          // Ignore multi-dimensional arrays.
+          while (signature[i] == '[')
+            i++;
+          
+          // Check for an object array
+          if (signature[i] == 'L')
+            {
+              while (signature[i] != ';')
+                i++;
+            }
+          continue;
+        }
+    }
+#endif /* DEBUG */
 
 #define INSN_LABEL(op) &&insn_##op
 
@@ -359,8 +435,6 @@
 #ifdef DEBUG
   // Get the object pointer for this method, after checking that it is
   // non-static.
-  _Jv_Method *method = meth->get_method ();
-   
   if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0)
     frame_desc.obj_ptr = locals[0].o;
 #endif

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]