OSDN Git Service

gcc:
[pf3gnuchains/gcc-fork.git] / libjava / jvmti.cc
index 8bebef8..7c7923b 100644 (file)
@@ -1,6 +1,6 @@
 // jvmti.cc - JVMTI implementation
 
-/* Copyright (C) 2006 Free Software Foundation
+/* Copyright (C) 2006, 2007, 2010 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -9,9 +9,2040 @@ Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
 details.  */
 
 #include <config.h>
+#include <platform.h>
 
 #include <jvm.h>
+#include <java-threads.h>
+#include <java-gc.h>
+#include <java-interp.h>
 #include <jvmti.h>
+#include "jvmti-int.h"
+
+#include <gcj/method.h>
+
+#include <gnu/classpath/SystemProperties.h>
+#include <gnu/gcj/runtime/BootClassLoader.h>
+#include <gnu/gcj/jvmti/Breakpoint.h>
+#include <gnu/gcj/jvmti/BreakpointManager.h>
+
+#include <java/lang/Class.h>
+#include <java/lang/ClassLoader.h>
+#include <java/lang/OutOfMemoryError.h>
+#include <java/lang/Thread.h>
+#include <java/lang/ThreadGroup.h>
+#include <java/lang/Thread$State.h>
+#include <java/lang/Throwable.h>
+#include <java/lang/VMClassLoader.h>
+#include <java/lang/reflect/Field.h>
+#include <java/lang/reflect/Modifier.h>
+#include <java/util/Collection.h>
+#include <java/util/HashMap.h>
+#include <java/util/concurrent/locks/Lock.h>
+#include <java/util/concurrent/locks/ReentrantReadWriteLock.h>
+#include <java/net/URL.h>
+
+static void check_enabled_events (void);
+static void check_enabled_event (jvmtiEvent);
+
+namespace JVMTI
+{
+  // Is JVMTI enabled? (i.e., any jvmtiEnv created?)
+  bool enabled;
+
+  // Event notifications
+  bool VMInit = false;
+  bool VMDeath = false;
+  bool ThreadStart = false;
+  bool ThreadEnd = false;
+  bool ClassFileLoadHook = false;
+  bool ClassLoad = false;
+  bool ClassPrepare = false;
+  bool VMStart = false;
+  bool Exception = false;
+  bool ExceptionCatch = false;
+  bool SingleStep = false;
+  bool FramePop = false;
+  bool Breakpoint = false;
+  bool FieldAccess = false;
+  bool FieldModification = false;
+  bool MethodEntry = false;
+  bool MethodExit = false;
+  bool NativeMethodBind = false;
+  bool CompiledMethodLoad = false;
+  bool CompiledMethodUnload = false;
+  bool DynamicCodeGenerated = false;
+  bool DataDumpRequest = false;
+  bool reserved72 = false;
+  bool MonitorWait = false;
+  bool MonitorWaited = false;
+  bool MonitorContendedEnter = false;
+  bool MonitorContendedEntered = false;
+  bool reserved77 = false;
+  bool reserved78 = false;
+  bool reserved79 = false;
+  bool reserved80 = false;
+  bool GarbageCollectionStart = false;
+  bool GarbageCollectionFinish = false;
+  bool ObjectFree = false;
+  bool VMObjectAlloc = false;
+};
+
+extern struct JNINativeInterface _Jv_JNIFunctions;
+
+struct _Jv_rawMonitorID
+{
+  _Jv_Mutex_t mutex;
+  _Jv_ConditionVariable_t condition;
+};
+
+/* A simple linked list of all JVMTI environments. Since
+   events must be delivered to environments in the order
+   in which the environments were created, new environments
+   are added to the end of the list. */
+struct jvmti_env_list
+{
+  jvmtiEnv *env;
+  struct jvmti_env_list *next;
+};
+static struct jvmti_env_list *_jvmtiEnvironments = NULL;
+static java::util::concurrent::locks::
+ReentrantReadWriteLock *_envListLock = NULL;
+#define FOREACH_ENVIRONMENT(Ele) \
+  for (Ele = _jvmtiEnvironments; Ele != NULL; Ele = Ele->next)
+
+// Some commonly-used checks
+
+#define THREAD_DEFAULT_TO_CURRENT(Ajthread)            \
+  do                                                   \
+    {                                                  \
+      if (Ajthread == NULL)                            \
+       Ajthread = java::lang::Thread::currentThread ();        \
+    }                                                  \
+  while (0)
+
+#define THREAD_CHECK_VALID(Athread)                                    \
+  do                                                                   \
+    {                                                                  \
+      if (!java::lang::Thread::class$.isAssignableFrom (&(Athread->class$))) \
+       return JVMTI_ERROR_INVALID_THREAD;                              \
+    }                                                                  \
+  while (0)
+
+#define THREAD_CHECK_IS_ALIVE(Athread)      \
+  do                                        \
+    {                                       \
+      if (!Athread->isAlive ())                     \
+       return JVMTI_ERROR_THREAD_NOT_ALIVE; \
+    }                                       \
+  while (0)
+
+// FIXME: if current phase is not set in Phases,
+// return JVMTI_ERROR_WRONG_PHASE
+#define REQUIRE_PHASE(Env, Phases)
+
+#define NULL_CHECK(Ptr)                                \
+  do                                           \
+    {                                          \
+      if (Ptr == NULL)                         \
+       return JVMTI_ERROR_NULL_POINTER;        \
+    }                                          \
+  while (0)
+
+#define ILLEGAL_ARGUMENT(Cond)                 \
+  do                                           \
+    {                                          \
+      if ((Cond))                              \
+       return JVMTI_ERROR_ILLEGAL_ARGUMENT;    \
+    }                                          \
+  while (0)
+
+#define CHECK_FOR_NATIVE_METHOD(AjmethodID)    \
+  do                                   \
+    {                                  \
+      jboolean is_native;              \
+      jvmtiError jerr = env->IsMethodNative (AjmethodID, &is_native);  \
+      if (jerr != JVMTI_ERROR_NONE)                                    \
+        return jerr;                                                   \
+      if (is_native)                                                   \
+        return JVMTI_ERROR_NATIVE_METHOD;                              \
+    }                                                                  \
+  while (0)
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
+{
+  using namespace java::lang;
+
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+
+  _Jv_Thread_t *data = _Jv_ThreadGetData (thread);
+  _Jv_SuspendThread (data);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_ResumeThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
+{
+  using namespace java::lang;
+
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+
+  _Jv_Thread_t *data = _Jv_ThreadGetData (thread);
+  _Jv_ResumeThread (data);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_InterruptThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
+{
+  using namespace java::lang;
+
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  // FIXME: capability handling?  'can_signal_thread'
+  if (thread == NULL)
+    return JVMTI_ERROR_INVALID_THREAD;
+
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+  thread->interrupt();
+  return JVMTI_ERROR_NONE;
+}
+
+// This method performs the common tasks to get and set variables of all types.
+// It is called by the _Jv_JVMTI_Get/SetLocalInt/Object/.... methods.
+static jvmtiError
+getLocalFrame (jvmtiEnv *env, jthread thread, jint depth, jint slot, char type,
+               _Jv_InterpFrame **iframe)
+{
+  using namespace java::lang;
+   
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+   
+  ILLEGAL_ARGUMENT (depth < 0);
+  
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+  
+  _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+  
+  for (int i = 0; i < depth; i++)
+    {    
+      frame = frame->next;
+    
+      if (frame == NULL)
+        return JVMTI_ERROR_NO_MORE_FRAMES; 
+    }
+  
+  if (frame->frame_type == frame_native)
+    return JVMTI_ERROR_OPAQUE_FRAME;
+  
+  jint max_locals;
+  jvmtiError jerr = env->GetMaxLocals (reinterpret_cast<jmethodID> 
+                                         (frame->self->get_method ()),
+                                       &max_locals);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr; 
+  
+  _Jv_InterpFrame *tmp_iframe = reinterpret_cast<_Jv_InterpFrame *> (frame);
+  
+  // The second slot taken up by a long type is marked as type 'x' meaning it
+  // is not valid for access since it holds only the 4 low bytes of the value.
+  if (tmp_iframe->locals_type[slot] == 'x')
+    return JVMTI_ERROR_INVALID_SLOT;
+  
+  if (tmp_iframe->locals_type[slot] != type)
+    return JVMTI_ERROR_TYPE_MISMATCH;
+  
+  // Check for invalid slots, if the type is a long type, we must check that
+  // the next slot is valid as well.
+  if (slot < 0 || slot >= max_locals 
+      || ((type == 'l' || type == 'd') && slot + 1 >= max_locals))
+    return JVMTI_ERROR_INVALID_SLOT;
+  
+  *iframe = tmp_iframe;
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                          jobject *value)
+{
+  NULL_CHECK (value);
+
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  *value = frame->locals[slot].o;
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                          jobject value)
+{
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  frame->locals[slot].o = value;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                       jint *value)
+{
+  NULL_CHECK (value);
+  
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame);
+
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  *value = frame->locals[slot].i;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                       jint value)
+{
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  frame->locals[slot].i = value;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                        jlong *value)
+{
+  NULL_CHECK (value);
+
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+#if SIZEOF_VOID_P==8
+  *value = frame->locals[slot].l;
+#else
+  _Jv_word2 val;
+  val.ia[0] = frame->locals[slot].ia[0];
+  val.ia[1] = frame->locals[slot + 1].ia[0];
+  *value = val.l;
+#endif
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                        jlong value)
+{
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+#if SIZEOF_VOID_P==8
+  frame->locals[slot].l = value;
+#else
+  _Jv_word2 val;
+       val.l = value;
+       frame->locals[slot].ia[0] = val.ia[0];
+       frame->locals[slot + 1].ia[0] = val.ia[1];
+#endif
+
+  return JVMTI_ERROR_NONE;
+}
+
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                         jfloat *value)
+{
+  NULL_CHECK (value);
+
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  *value = frame->locals[slot].f;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                         jfloat value)
+{
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  frame->locals[slot].f = value;
+
+  return JVMTI_ERROR_NONE;
+}
+
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                          jdouble *value)
+{
+  NULL_CHECK (value);
+
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+#if SIZEOF_VOID_P==8
+  *value = frame->locals[slot].d;
+#else
+  _Jv_word2 val;
+  val.ia[0] = frame->locals[slot].ia[0];
+  val.ia[1] = frame->locals[slot + 1].ia[0];
+  *value = val.d;
+#endif
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot,
+                          jdouble value)
+{
+  _Jv_InterpFrame *frame;
+  jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame);
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+    
+#if SIZEOF_VOID_P==8
+  frame->locals[slot].d = value;
+#else
+  _Jv_word2 val;
+  val.d = value;
+  frame->locals[slot].ia[0] = val.ia[0];
+  frame->locals[slot + 1].ia[0] = val.ia[1]; 
+#endif
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt,
+                        jthread **threads)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  NULL_CHECK (thread_cnt);
+  NULL_CHECK (threads);
+   
+  using namespace java::lang;
+   
+  ThreadGroup *root_grp = ThreadGroup::root;
+  jint estimate = root_grp->activeCount ();
+
+  JArray<Thread *> *thr_arr;
+
+  // Allocate some extra space since threads can be created between calls
+  try
+    { 
+      thr_arr = reinterpret_cast<JArray<Thread *> *> (JvNewObjectArray 
+                                                     ((estimate * 2),
+                                                      &Thread::class$, NULL));
+    }
+  catch (java::lang::OutOfMemoryError *err)
+    {
+      return JVMTI_ERROR_OUT_OF_MEMORY;
+    }
+    
+  *thread_cnt = root_grp->enumerate (thr_arr);
+   
+  jvmtiError jerr = env->Allocate ((jlong) ((*thread_cnt) * sizeof (jthread)),
+                                   (unsigned char **) threads);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+   
+  // Transfer the threads to the result array
+  jthread *tmp_arr = reinterpret_cast<jthread *> (elements (thr_arr));
+  memcpy ((*threads), tmp_arr, (*thread_cnt));
+   
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetFrameCount (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
+                         jint *frame_count)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  
+  NULL_CHECK (frame_count);
+       
+  using namespace java::lang;
+  
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+   
+  _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+  (*frame_count) = frame->depth ();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetThreadState (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
+                         jint *thread_state_ptr)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  NULL_CHECK (thread_state_ptr);
+
+  jint state = 0;
+  if (thread->isAlive ())
+    {
+      state |= JVMTI_THREAD_STATE_ALIVE;
+
+      _Jv_Thread_t *data = _Jv_ThreadGetData (thread);
+      if (_Jv_IsThreadSuspended (data))
+       state |= JVMTI_THREAD_STATE_SUSPENDED;
+
+      if (thread->isInterrupted ())
+       state |= JVMTI_THREAD_STATE_INTERRUPTED;
+
+      _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+      if (frame != NULL && frame->frame_type == frame_native)
+       state |= JVMTI_THREAD_STATE_IN_NATIVE;
+
+      using namespace java::lang;
+      Thread$State *ts = thread->getState ();
+      if (ts == Thread$State::RUNNABLE)
+       state |= JVMTI_THREAD_STATE_RUNNABLE;
+      else if (ts == Thread$State::BLOCKED)
+       state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
+      else if (ts == Thread$State::TIMED_WAITING
+              || ts == Thread$State::WAITING)
+       {
+         state |= JVMTI_THREAD_STATE_WAITING;
+         state |= ((ts == Thread$State::WAITING)
+                   ? JVMTI_THREAD_STATE_WAITING_INDEFINITELY
+                   : JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT);
+
+         /* FIXME: We don't have a way to tell
+            the caller why the thread is suspended,
+            i.e., JVMTI_THREAD_STATE_SLEEPING,
+            JVMTI_THREAD_STATE_PARKED, and
+            JVMTI_THREAD_STATE_IN_OBJECT_WAIT
+            are never set. */
+       }
+    }
+  else
+    {
+      using namespace java::lang;
+      Thread$State *ts = thread->getState ();
+      if (ts == Thread$State::TERMINATED)
+       state |= JVMTI_THREAD_STATE_TERMINATED;
+    }
+
+  *thread_state_ptr = state;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_CreateRawMonitor (MAYBE_UNUSED jvmtiEnv *env, const char *name,
+                           jrawMonitorID *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
+  NULL_CHECK (name);
+  NULL_CHECK (result);
+  *result = (jrawMonitorID) _Jv_MallocUnchecked (sizeof (_Jv_rawMonitorID));
+  if (*result == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+  _Jv_MutexInit (&(*result)->mutex);
+  _Jv_CondInit (&(*result)->condition);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_DestroyRawMonitor (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
+  // Note we have no better way of knowing whether this object is
+  // really a raw monitor.
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  // FIXME: perform checks on monitor, release it if this thread owns
+  // it.
+#ifdef _Jv_HaveMutexDestroy
+  _Jv_MutexDestroy (&monitor->mutex);
+#endif
+  _Jv_Free (monitor);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_RawMonitorEnter (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
+{
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  _Jv_MutexLock (&monitor->mutex);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_RawMonitorExit (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
+{
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  if (_Jv_MutexUnlock (&monitor->mutex))
+    return JVMTI_ERROR_NOT_MONITOR_OWNER;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_RawMonitorWait (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor,
+                         jlong millis)
+{
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  int r = _Jv_CondWait (&monitor->condition, &monitor->mutex, millis, 0);
+  if (r == _JV_NOT_OWNER)
+    return JVMTI_ERROR_NOT_MONITOR_OWNER;
+  if (r == _JV_INTERRUPTED)
+    return JVMTI_ERROR_INTERRUPT;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_RawMonitorNotify (MAYBE_UNUSED jvmtiEnv *env, jrawMonitorID monitor)
+{
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  if (_Jv_CondNotify (&monitor->condition, &monitor->mutex) == _JV_NOT_OWNER)
+    return JVMTI_ERROR_NOT_MONITOR_OWNER;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_RawMonitorNotifyAll (MAYBE_UNUSED jvmtiEnv *env,
+                              jrawMonitorID monitor)
+{
+  if (monitor == NULL)
+    return JVMTI_ERROR_INVALID_MONITOR;
+  if (_Jv_CondNotifyAll (&monitor->condition, &monitor->mutex)
+      == _JV_NOT_OWNER)
+    return JVMTI_ERROR_NOT_MONITOR_OWNER;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetBreakpoint (jvmtiEnv *env, jmethodID method, jlocation location)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+  using namespace gnu::gcj::jvmti;
+  Breakpoint *bp
+    = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method),
+                                       location);
+  if (bp == NULL)
+    {
+      jclass klass;
+      jvmtiError err = env->GetMethodDeclaringClass (method, &klass);
+      if (err != JVMTI_ERROR_NONE)
+       return err;
+
+      if (!_Jv_IsInterpretedClass (klass))
+       return JVMTI_ERROR_INVALID_CLASS;
+
+      _Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method);
+      if (base == NULL)
+       return JVMTI_ERROR_INVALID_METHODID;
+
+      jint flags;
+      err = env->GetMethodModifiers (method, &flags);
+      if (err != JVMTI_ERROR_NONE)
+       return err;
+
+      if (flags & java::lang::reflect::Modifier::NATIVE)
+       return JVMTI_ERROR_NATIVE_METHOD;
+
+      _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base);
+      if (imeth->get_insn (location) == NULL)
+       return JVMTI_ERROR_INVALID_LOCATION;
+
+      // Now the breakpoint can be safely installed
+      bp = BreakpointManager::newBreakpoint (reinterpret_cast<jlong> (method),
+                                            location);
+    }
+  else
+    {
+      // Duplicate breakpoints are not permitted by JVMTI
+      return JVMTI_ERROR_DUPLICATE;
+    }
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_ClearBreakpoint (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                          jlocation location)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+  using namespace gnu::gcj::jvmti;
+
+  Breakpoint *bp
+    = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method),
+                                       location);
+  if (bp == NULL)
+    return JVMTI_ERROR_NOT_FOUND;
+
+  BreakpointManager::deleteBreakpoint (reinterpret_cast<jlong> (method), location);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_Allocate (MAYBE_UNUSED jvmtiEnv *env, jlong size,
+                   unsigned char **result)
+{
+  ILLEGAL_ARGUMENT (size < 0);
+  NULL_CHECK (result);
+  if (size == 0)
+    *result = NULL;
+  else
+    {
+      *result = (unsigned char *) _Jv_MallocUnchecked (size);
+      if (*result == NULL)
+       return JVMTI_ERROR_OUT_OF_MEMORY;
+    }
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_Deallocate (MAYBE_UNUSED jvmtiEnv *env, unsigned char *mem)
+{
+  if (mem != NULL)
+    _Jv_Free (mem);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetClassStatus (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                         jint *status_ptr)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  NULL_CHECK (status_ptr);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+
+  if (klass->isArray ())
+    *status_ptr = JVMTI_CLASS_STATUS_ARRAY;
+  else if (klass->isPrimitive ())
+    *status_ptr  = JVMTI_CLASS_STATUS_PRIMITIVE;
+  else
+    {
+      jbyte state = _Jv_GetClassState (klass);
+      *status_ptr = 0;
+      if (state >= JV_STATE_LINKED)
+       (*status_ptr) |= JVMTI_CLASS_STATUS_VERIFIED;
+      if (state >= JV_STATE_PREPARED)
+       (*status_ptr) |= JVMTI_CLASS_STATUS_PREPARED;
+      if (state == JV_STATE_ERROR || state == JV_STATE_PHANTOM)
+       (*status_ptr) |= JVMTI_CLASS_STATUS_ERROR;
+      else if (state == JV_STATE_DONE)
+       (*status_ptr) |= JVMTI_CLASS_STATUS_INITIALIZED;
+    }
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetClassModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                            jint *mods)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  // Don't bother checking KLASS' type.
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  NULL_CHECK (mods);
+  *mods = klass->getModifiers();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetClassMethods (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                          jint *count_ptr, jmethodID **methods_ptr)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  // FIXME: capability can_maintain_original_method_order
+  // Don't bother checking KLASS' type.
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  NULL_CHECK (count_ptr);
+  NULL_CHECK (methods_ptr);
+  *count_ptr = JvNumMethods(klass);
+
+  *methods_ptr
+    = (jmethodID *) _Jv_MallocUnchecked (*count_ptr * sizeof (jmethodID));
+  if (*methods_ptr == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+
+  jmethodID start = JvGetFirstMethod (klass);
+  for (jint i = 0; i < *count_ptr; ++i)
+    // FIXME: correct?
+    (*methods_ptr)[i] = start + i;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_IsInterface (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                      jboolean *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  NULL_CHECK (result);
+  *result = klass->isInterface();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_IsArrayClass (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                       jboolean *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  NULL_CHECK (result);
+  *result = klass->isArray();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetClassLoader (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                         jobject *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  NULL_CHECK (result);
+  *result = klass->getClassLoaderInternal();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetObjectHashCode (MAYBE_UNUSED jvmtiEnv *env, jobject obj,
+                            jint *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (obj == NULL)
+    return JVMTI_ERROR_INVALID_OBJECT;
+  NULL_CHECK (result);
+  *result = _Jv_HashCode (obj);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetFieldModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                            jfieldID field, jint *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  if (field == NULL)
+    return JVMTI_ERROR_INVALID_FIELDID;
+  NULL_CHECK (result);
+  *result = field->getModifiers();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_IsFieldSynthetic (MAYBE_UNUSED jvmtiEnv *env, jclass klass,
+                           jfieldID field, jboolean *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (klass == NULL)
+    return JVMTI_ERROR_INVALID_CLASS;
+  if (field == NULL)
+    return JVMTI_ERROR_INVALID_FIELDID;
+  NULL_CHECK (result);
+
+  // FIXME: capability can_get_synthetic_attribute
+  *result = ((field->getModifiers() & java::lang::reflect::Modifier::SYNTHETIC)
+            != 0);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetMethodName (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                        char **name_ptr, char **signature_ptr,
+                        char **generic_ptr)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+
+  if (method == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+
+  if (name_ptr != NULL)
+    {
+      int len = static_cast<int> (method->name->len ());
+      *name_ptr = (char *) _Jv_MallocUnchecked (len + 1);
+      if (*name_ptr == NULL)
+       return JVMTI_ERROR_OUT_OF_MEMORY;
+      strncpy (*name_ptr, method->name->chars (), len);
+      (*name_ptr)[len] = '\0';
+    }
+
+  if (signature_ptr != NULL)
+    {
+      int len = static_cast<int> (method->signature->len ());
+      *signature_ptr = (char *) _Jv_MallocUnchecked (len + 1);
+      if (*signature_ptr == NULL)
+       {
+         if (name_ptr != NULL)
+           _Jv_Free (*name_ptr);
+         return JVMTI_ERROR_OUT_OF_MEMORY;
+       }
+      strncpy (*signature_ptr, method->signature->chars (), len);
+      (*signature_ptr)[len] = '\0';
+    }
+
+  if (generic_ptr != NULL)
+    {
+      *generic_ptr = NULL;
+    }
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetMethodModifiers (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                             jint *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (method == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  NULL_CHECK (result);
+
+  // FIXME: mask off some internal bits...
+  *result = method->accflags;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLineNumberTable (jvmtiEnv *env, jmethodID method,
+                             jint *entry_count_ptr,
+                             jvmtiLineNumberEntry **table_ptr)
+{
+  NULL_CHECK (entry_count_ptr);
+  NULL_CHECK (table_ptr);
+
+  jclass klass;
+  jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  _Jv_MethodBase *base = _Jv_FindInterpreterMethod (klass, method);
+  if (base == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+
+  if (java::lang::reflect::Modifier::isNative (method->accflags)
+      || !_Jv_IsInterpretedClass (klass))
+    return JVMTI_ERROR_NATIVE_METHOD;
+
+  _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> (base);
+  jlong start, end;
+  jintArray lines = NULL;
+  jlongArray indices = NULL;
+  imeth->get_line_table (start, end, lines, indices);
+  if (lines == NULL)
+    return JVMTI_ERROR_ABSENT_INFORMATION;
+
+  jvmtiLineNumberEntry *table;
+  jsize len = lines->length * sizeof (jvmtiLineNumberEntry);
+  table = (jvmtiLineNumberEntry *) _Jv_MallocUnchecked (len);
+  if (table == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+  
+  jint *line = elements (lines);
+  jlong *index = elements (indices);
+  for (int i = 0; i < lines->length; ++i)
+    {
+      table[i].start_location = index[i];
+      table[i].line_number = line[i];
+    }
+
+  *table_ptr = table;
+  *entry_count_ptr = lines->length;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalVariableTable (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                                 jint *num_locals,
+                                 jvmtiLocalVariableEntry **locals)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  NULL_CHECK (num_locals);
+  NULL_CHECK (locals);
+  
+  CHECK_FOR_NATIVE_METHOD(method);
+  
+  jclass klass;
+  jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> 
+                              (_Jv_FindInterpreterMethod (klass, method));
+  
+  if (imeth == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  
+  jerr = env->GetMaxLocals (method, num_locals);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  jerr = env->Allocate (static_cast<jlong> 
+                          ((*num_locals) * sizeof (jvmtiLocalVariableEntry)),
+                        reinterpret_cast<unsigned char **> (locals));
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  //the slot in the methods local_var_table to get
+  int table_slot = 0;
+  char *name;
+  char *sig;
+  char *generic_sig;
+  
+  while (table_slot < *num_locals 
+         && imeth->get_local_var_table (&name, &sig, &generic_sig,
+                                 &((((*locals)[table_slot].start_location))),
+                                 &((*locals)[table_slot].length), 
+                                 &((*locals)[table_slot].slot),
+                                 table_slot) 
+            >= 0)
+    {
+      char **str_ptr = &(*locals)[table_slot].name;
+      jerr = env->Allocate (static_cast<jlong> (strlen (name) + 1),
+                             reinterpret_cast<unsigned char **> (str_ptr));
+      if (jerr != JVMTI_ERROR_NONE)
+        return jerr;
+      strcpy ((*locals)[table_slot].name, name);
+      
+      str_ptr = &(*locals)[table_slot].signature;
+      jerr = env->Allocate (static_cast<jlong> (strlen (sig) + 1),
+                               reinterpret_cast<unsigned char **> (str_ptr));
+      if (jerr != JVMTI_ERROR_NONE)
+        return jerr;
+      strcpy ((*locals)[table_slot].signature, sig);
+      
+      str_ptr = &(*locals)[table_slot].generic_signature;
+      jerr = env->Allocate (static_cast<jlong> (strlen (generic_sig) + 1),
+                               reinterpret_cast<unsigned char **> (str_ptr));
+      if (jerr != JVMTI_ERROR_NONE)
+        return jerr;
+      strcpy ((*locals)[table_slot].generic_signature, generic_sig);
+      
+      table_slot++;
+    }
+
+  if (table_slot == 0)
+    return JVMTI_ERROR_ABSENT_INFORMATION;
+  
+  // If there are double or long variables in the table, the the table will be
+  // smaller than the max number of slots, so correct for this here.
+  if ((table_slot) < *num_locals)
+    *num_locals = table_slot;
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_IsMethodNative (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                         jboolean *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (method == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  NULL_CHECK (result);
+
+  *result = ((method->accflags & java::lang::reflect::Modifier::NATIVE) != 0);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_IsMethodSynthetic (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                            jboolean *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (method == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  NULL_CHECK (result);
+
+  // FIXME capability can_get_synthetic_attribute
+
+  *result = ((method->accflags & java::lang::reflect::Modifier::SYNTHETIC)
+            != 0);
+  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);
+  NULL_CHECK (max_locals);
+  
+  CHECK_FOR_NATIVE_METHOD (method);
+  
+  jclass klass;
+  jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> 
+                              (_Jv_FindInterpreterMethod (klass, method));
+    
+  if (imeth == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  
+  *max_locals = imeth->get_max_locals ();
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetArgumentsSize (jvmtiEnv *env, jmethodID method, jint *size)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  NULL_CHECK (size);
+  
+  CHECK_FOR_NATIVE_METHOD (method);
+  
+  jvmtiError jerr;
+  char *sig;
+  jint num_slots = 0;
+  
+  jerr = env->GetMethodName (method, NULL, &sig, NULL);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  // If the method is non-static add a slot for the "this" pointer.
+  if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0)
+    num_slots++;
+  
+  for (int i = 0; sig[i] != ')'; i++)
+    {
+      if (sig[i] == 'Z' || sig[i] == 'B' || sig[i] == 'C' || sig[i] == 'S'
+          || sig[i] == 'I' || sig[i] == 'F')
+        num_slots++;
+      else if (sig[i] == 'J' || sig[i] == 'D')
+        {
+          // If this is an array of wide types it uses a single slot
+          if (i > 0 && sig[i - 1] == '[')
+            num_slots++;
+          else
+            num_slots += 2;
+        }
+      else if (sig[i] == 'L')
+        {
+          num_slots++;
+          while (sig[i] != ';')
+            i++;
+        }
+    }
+  
+  *size = num_slots;
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetMethodDeclaringClass (MAYBE_UNUSED jvmtiEnv *env,
+                                  jmethodID method,
+                                  jclass *declaring_class_ptr)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  NULL_CHECK (declaring_class_ptr);
+
+  jclass klass = _Jv_GetMethodDeclaringClass (method);
+  if (klass != NULL)
+    {
+      *declaring_class_ptr = klass;
+      return JVMTI_ERROR_NONE;
+    }
+
+  return JVMTI_ERROR_INVALID_METHODID;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetClassLoaderClasses (MAYBE_UNUSED jvmtiEnv *env,
+                                jobject init_loader,
+                                jint *count_ptr,
+                                jclass **result_ptr)
+{
+  using namespace java::lang;
+  using namespace java::util;
+
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  NULL_CHECK (count_ptr);
+  NULL_CHECK (result_ptr);
+
+  ClassLoader *loader = (ClassLoader *) init_loader;
+  if (loader == NULL)
+    loader = VMClassLoader::bootLoader;
+
+  Collection *values = loader->loadedClasses->values();
+  jobjectArray array = values->toArray();
+  *count_ptr = array->length;
+  jobject *elts = elements (array);
+  jclass *result
+    = (jclass *) _Jv_MallocUnchecked (*count_ptr * sizeof (jclass));
+  if (result == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+
+  // FIXME: JNI references...
+  memcpy (result, elts, *count_ptr * sizeof (jclass));
+
+  *result_ptr = result;
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetStackTrace (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
+                         jint start_depth, jint max_frames,
+                         jvmtiFrameInfo *frames, jint *frame_count)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+  ILLEGAL_ARGUMENT (max_frames < 0);
+  
+  NULL_CHECK (frames);
+  NULL_CHECK (frame_count);
+       
+  using namespace java::lang;
+  
+  THREAD_DEFAULT_TO_CURRENT (thread);
+  THREAD_CHECK_VALID (thread);
+  THREAD_CHECK_IS_ALIVE (thread);
+    
+  jvmtiError jerr = env->GetFrameCount (thread, frame_count);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  // start_depth can be either a positive number, indicating the depth of the
+  // stack at which to begin the trace, or a negative number indicating the
+  // number of frames at the bottom of the stack to exclude.  These checks
+  // ensure that it is a valid value in either case
+  
+  ILLEGAL_ARGUMENT (start_depth >= (*frame_count));
+  ILLEGAL_ARGUMENT (start_depth < (-(*frame_count)));
+  
+  _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+
+  // If start_depth is negative use this to determine at what depth to start
+  // the trace by adding it to the length of the call stack.  This allows the
+  // use of the same frame "discarding" mechanism as for a positive start_depth
+  if (start_depth < 0)
+    start_depth = *frame_count + start_depth;
+  
+  // If start_depth > 0 "remove" start_depth frames from the beginning
+  // of the stack before beginning the trace by moving along the frame list.
+  while (start_depth > 0)
+    {
+      frame = frame->next;
+      start_depth--;
+      (*frame_count)--;
+    }
+  
+  // Now check to see if the array supplied by the agent is large enough to
+  // hold frame_count frames, after adjustment for start_depth.
+  if ((*frame_count) > max_frames)
+    (*frame_count) = max_frames;
+  
+  for (int i = 0; i < (*frame_count); i++)
+    {
+      frames[i].method = frame->self->get_method ();
+      
+      // Set the location in the frame, native frames have location = -1
+      if (frame->frame_type == frame_interpreter)
+        {
+          _Jv_InterpMethod *imeth 
+            = static_cast<_Jv_InterpMethod *> (frame->self);
+          _Jv_InterpFrame *interp_frame 
+            = static_cast<_Jv_InterpFrame *> (frame);
+          frames[i].location = imeth->insn_index (interp_frame->get_pc ());
+        }
+      else
+        frames[i].location = -1;
+        
+      frame = frame->next;
+    }
+    
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_ForceGarbageCollection (MAYBE_UNUSED jvmtiEnv *env)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  _Jv_RunGC();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env,
+                              const jniNativeInterface *function_table)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  NULL_CHECK (function_table);
+  memcpy (&_Jv_JNIFunctions, function_table, sizeof (jniNativeInterface));
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetJNIFunctionTable (MAYBE_UNUSED jvmtiEnv *env,
+                              jniNativeInterface **function_table)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  NULL_CHECK (function_table);
+  *function_table
+    = (jniNativeInterface *) _Jv_MallocUnchecked (sizeof (jniNativeInterface));
+  if (*function_table == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+  memcpy (*function_table, &_Jv_JNIFunctions, sizeof (jniNativeInterface));
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env)
+{
+  NULL_CHECK (env);
+
+  if (_jvmtiEnvironments == NULL)
+    return JVMTI_ERROR_INVALID_ENVIRONMENT;
+  else
+    {
+      _envListLock->writeLock ()->lock ();
+      if (_jvmtiEnvironments->env == env)
+       {
+         struct jvmti_env_list *next = _jvmtiEnvironments->next;
+         _Jv_Free (_jvmtiEnvironments);
+         _jvmtiEnvironments = next;
+       }
+      else
+       {
+         struct jvmti_env_list *e = _jvmtiEnvironments; 
+         while (e->next != NULL && e->next->env != env)
+           e = e->next;
+         if (e->next == NULL)
+           {
+             _envListLock->writeLock ()->unlock ();
+             return JVMTI_ERROR_INVALID_ENVIRONMENT;
+           }
+
+         struct jvmti_env_list *next = e->next->next;
+         _Jv_Free (e->next);
+         e->next = next;
+       }
+      _envListLock->writeLock ()->unlock ();
+    }
+
+  _Jv_Free (env);
+
+  check_enabled_events ();
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property,
+                            char **result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
+  NULL_CHECK (property);
+  NULL_CHECK (result);
+
+  jstring name = JvNewStringUTF(property);
+  jstring result_str = gnu::classpath::SystemProperties::getProperty(name);
+
+  if (result_str == NULL)
+    return JVMTI_ERROR_NOT_AVAILABLE;
+
+  int len = JvGetStringUTFLength (result_str);
+  *result = (char *) _Jv_MallocUnchecked (len + 1);
+  if (*result == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+  JvGetStringUTFRegion (result_str, 0, result_str->length(), *result);
+  (*result)[len] = '\0';
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetSystemProperty (MAYBE_UNUSED jvmtiEnv *env, const char *property,
+                            const char *value)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD);
+
+  NULL_CHECK (property);
+  if (value == NULL)
+    {
+      // FIXME: When would a property not be writeable?
+      return JVMTI_ERROR_NONE;
+    }
+
+  jstring prop_str = JvNewStringUTF(property);
+  jstring value_str = JvNewStringUTF(value);
+  gnu::classpath::SystemProperties::setProperty(prop_str, value_str);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetTime (MAYBE_UNUSED jvmtiEnv *env, jlong *nanos_ptr)
+{
+  NULL_CHECK (nanos_ptr);
+  *nanos_ptr = _Jv_platform_nanotime();
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetAvailableProcessors (MAYBE_UNUSED jvmtiEnv *env,
+                                 jint *nprocessors_ptr)
+{
+  NULL_CHECK (nprocessors_ptr);
+#ifdef _SC_NPROCESSORS_ONLN
+  *nprocessors_ptr = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+  *nprocessors_ptr = 1;
+#endif
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_AddToBootstrapClassLoaderSearch (MAYBE_UNUSED jvmtiEnv *env,
+                                          const char *segment)
+{
+  using namespace java::lang;
+  using namespace java::net;
+  using namespace gnu::gcj::runtime;
+
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD);
+  NULL_CHECK (segment);
+
+  jstring str_segment = JvNewStringUTF(segment);
+  URL *url;
+  try
+    {
+      url = new URL(JvNewStringUTF("file"), NULL, str_segment);
+    }
+  catch (jthrowable ignore)
+    {
+      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+  BootClassLoader *loader = VMClassLoader::bootLoader;
+  // Don't call this too early.
+  // assert (loader != NULL);
+  loader->addURL(url);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetVerboseFlag (MAYBE_UNUSED jvmtiEnv *env, jvmtiVerboseFlag flag,
+                         jboolean value)
+{
+  switch (flag)
+    {
+    case JVMTI_VERBOSE_OTHER:
+    case JVMTI_VERBOSE_GC:
+    case JVMTI_VERBOSE_JNI:
+      // Ignore.
+      break;
+    case JVMTI_VERBOSE_CLASS:
+      gcj::verbose_class_flag = value;
+      break;
+    default:
+      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+    }
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetObjectSize (MAYBE_UNUSED jvmtiEnv *env, jobject object,
+                        jlong *result)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  if (object == NULL)
+    return JVMTI_ERROR_INVALID_OBJECT;
+  NULL_CHECK (result);
+
+  jclass klass = object->getClass();
+  if (klass->isArray())
+    {
+      jclass comp = klass->getComponentType();
+      jint base
+       = (jint) (_Jv_uintptr_t) _Jv_GetArrayElementFromElementType(NULL,
+                                                                   klass->getComponentType());
+      // FIXME: correct for primitive types?
+      jint compSize = comp->size();
+      __JArray *array = (__JArray *) object;
+      *result = base + array->length * compSize;
+    }
+  else
+    {
+      // Note that if OBJECT is a String then it may (if
+      // str->data==str) take more space.  Do we care?
+      *result = klass->size();
+    }
+  return JVMTI_ERROR_NONE;
+}
+
+/* An event is enabled only if it has both an event handler
+   and it is enabled in the environment. */
+static void
+check_enabled_event (jvmtiEvent type)
+{
+  bool *enabled;
+  int offset;
+
+#define GET_OFFSET(Event)                              \
+  do                                                   \
+    {                                                  \
+      enabled = &JVMTI::Event;                         \
+      offset = offsetof (jvmtiEventCallbacks, Event);  \
+    }                                                  \
+  while (0)
+
+  switch (type)
+    {
+    case JVMTI_EVENT_VM_INIT:
+      GET_OFFSET (VMInit);
+      break;
+
+    case JVMTI_EVENT_VM_DEATH:
+      GET_OFFSET (VMDeath);
+      break;
+
+    case JVMTI_EVENT_THREAD_START:
+      GET_OFFSET (ThreadStart);
+      break;
+
+    case JVMTI_EVENT_THREAD_END:
+      GET_OFFSET (ThreadEnd);
+      break;
+
+    case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+      GET_OFFSET (ClassFileLoadHook);
+      break;
+
+    case JVMTI_EVENT_CLASS_LOAD:
+      GET_OFFSET (ClassLoad);
+      break;
+
+    case JVMTI_EVENT_CLASS_PREPARE:
+      GET_OFFSET (ClassPrepare);
+      break;
+
+    case JVMTI_EVENT_VM_START:
+      GET_OFFSET (VMStart);
+      break;
+
+    case JVMTI_EVENT_EXCEPTION:
+      GET_OFFSET (Exception);
+      break;
+
+    case JVMTI_EVENT_EXCEPTION_CATCH:
+      GET_OFFSET (ExceptionCatch);
+      break;
+
+    case JVMTI_EVENT_SINGLE_STEP:
+      GET_OFFSET (SingleStep);
+      break;
+
+    case JVMTI_EVENT_FRAME_POP:
+      GET_OFFSET (FramePop);
+      break;
+
+    case JVMTI_EVENT_BREAKPOINT:
+      GET_OFFSET (Breakpoint);
+      break;
+
+    case JVMTI_EVENT_FIELD_ACCESS:
+      GET_OFFSET (FieldAccess);
+      break;
+
+    case JVMTI_EVENT_FIELD_MODIFICATION:
+      GET_OFFSET (FieldModification);
+      break;
+
+    case JVMTI_EVENT_METHOD_ENTRY:
+      GET_OFFSET (MethodEntry);
+      break;
+
+    case JVMTI_EVENT_METHOD_EXIT:
+      GET_OFFSET (MethodExit);
+      break;
+
+    case JVMTI_EVENT_NATIVE_METHOD_BIND:
+      GET_OFFSET (NativeMethodBind);
+      break;
+
+    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+      GET_OFFSET (CompiledMethodLoad);
+      break;
+
+    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+      GET_OFFSET (CompiledMethodUnload);
+      break;
+
+    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+      GET_OFFSET (DynamicCodeGenerated);
+      break;
+
+    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+      GET_OFFSET (DataDumpRequest);
+      break;
+
+    case JVMTI_EVENT_MONITOR_WAIT:
+      GET_OFFSET (MonitorWait);
+      break;
+
+    case JVMTI_EVENT_MONITOR_WAITED:
+      GET_OFFSET (MonitorWaited);
+      break;
+
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+      GET_OFFSET (MonitorContendedEnter);
+      break;
+
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+      GET_OFFSET (MonitorContendedEntered);
+      break;
+
+    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+      GET_OFFSET (GarbageCollectionStart);
+      break;
+
+    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+      GET_OFFSET (GarbageCollectionFinish);
+      break;
+
+    case JVMTI_EVENT_OBJECT_FREE:
+      GET_OFFSET (ObjectFree);
+      break;
+
+    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+      GET_OFFSET (VMObjectAlloc);
+      break;
+
+    default:
+      fprintf (stderr,
+              "libgcj: check_enabled_event for unknown JVMTI event (%d)\n",
+              (int) type);
+      return;
+    }
+#undef GET_OFFSET
+
+  int index = EVENT_INDEX (type); // safe since caller checks this
+
+  if (_jvmtiEnvironments != NULL)
+    {
+      _envListLock->readLock ()->lock ();
+      struct jvmti_env_list *e;
+      FOREACH_ENVIRONMENT (e)
+       {
+         char *addr
+           = reinterpret_cast<char *> (&e->env->callbacks) + offset;
+         void **callback = reinterpret_cast<void **> (addr);
+         if (e->env->enabled[index] && *callback != NULL)
+           {
+             *enabled = true;
+             _envListLock->readLock ()->unlock ();
+             return;
+           }
+       }
+
+      _envListLock->readLock ()->unlock ();
+    }
+
+  *enabled = false;
+}
+
+static void
+check_enabled_events ()
+{
+  check_enabled_event (JVMTI_EVENT_VM_INIT);
+  check_enabled_event (JVMTI_EVENT_VM_DEATH);
+  check_enabled_event (JVMTI_EVENT_THREAD_START);
+  check_enabled_event (JVMTI_EVENT_THREAD_END);
+  check_enabled_event (JVMTI_EVENT_CLASS_FILE_LOAD_HOOK);
+  check_enabled_event (JVMTI_EVENT_CLASS_LOAD);
+  check_enabled_event (JVMTI_EVENT_CLASS_PREPARE);
+  check_enabled_event (JVMTI_EVENT_VM_START);
+  check_enabled_event (JVMTI_EVENT_EXCEPTION);
+  check_enabled_event (JVMTI_EVENT_EXCEPTION_CATCH);
+  check_enabled_event (JVMTI_EVENT_SINGLE_STEP);
+  check_enabled_event (JVMTI_EVENT_FRAME_POP);
+  check_enabled_event (JVMTI_EVENT_BREAKPOINT);
+  check_enabled_event (JVMTI_EVENT_FIELD_ACCESS);
+  check_enabled_event (JVMTI_EVENT_FIELD_MODIFICATION);
+  check_enabled_event (JVMTI_EVENT_METHOD_ENTRY);
+  check_enabled_event (JVMTI_EVENT_METHOD_EXIT);
+  check_enabled_event (JVMTI_EVENT_NATIVE_METHOD_BIND);
+  check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_LOAD);
+  check_enabled_event (JVMTI_EVENT_COMPILED_METHOD_UNLOAD);
+  check_enabled_event (JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
+  check_enabled_event (JVMTI_EVENT_DATA_DUMP_REQUEST);
+  check_enabled_event (JVMTI_EVENT_MONITOR_WAIT);
+  check_enabled_event (JVMTI_EVENT_MONITOR_WAITED);
+  check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTER);
+  check_enabled_event (JVMTI_EVENT_MONITOR_CONTENDED_ENTERED);
+  check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_START);
+  check_enabled_event (JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+  check_enabled_event (JVMTI_EVENT_OBJECT_FREE);
+  check_enabled_event (JVMTI_EVENT_VM_OBJECT_ALLOC);
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetEventNotificationMode (jvmtiEnv *env, jvmtiEventMode mode,
+                                   jvmtiEvent type, jthread event_thread, ...)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
+
+  if (event_thread != NULL)
+    {
+      THREAD_CHECK_VALID (event_thread);
+      THREAD_CHECK_IS_ALIVE (event_thread);
+    }
+
+  bool enabled;
+  switch (mode)
+    {
+    case JVMTI_DISABLE:
+      enabled = false;
+      break;
+    case JVMTI_ENABLE:
+      enabled = true;
+      break;
+
+    default:
+      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+  switch (type)
+    {
+    case JVMTI_EVENT_VM_INIT:
+    case JVMTI_EVENT_VM_DEATH:
+    case JVMTI_EVENT_THREAD_START:
+    case JVMTI_EVENT_VM_START:
+    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+      ILLEGAL_ARGUMENT (event_thread != NULL);
+      break;
+
+    case JVMTI_EVENT_THREAD_END:
+    case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+    case JVMTI_EVENT_CLASS_LOAD:
+    case JVMTI_EVENT_CLASS_PREPARE:
+    case JVMTI_EVENT_EXCEPTION:
+    case JVMTI_EVENT_EXCEPTION_CATCH:
+    case JVMTI_EVENT_SINGLE_STEP:
+    case JVMTI_EVENT_FRAME_POP:
+    case JVMTI_EVENT_BREAKPOINT:
+    case JVMTI_EVENT_FIELD_ACCESS:
+    case JVMTI_EVENT_FIELD_MODIFICATION:
+    case JVMTI_EVENT_METHOD_ENTRY:
+    case JVMTI_EVENT_METHOD_EXIT:
+    case JVMTI_EVENT_NATIVE_METHOD_BIND:
+    case JVMTI_EVENT_MONITOR_WAIT:
+    case JVMTI_EVENT_MONITOR_WAITED:
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+    case JVMTI_EVENT_OBJECT_FREE:
+    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+      break;
+
+    default:
+      return JVMTI_ERROR_INVALID_EVENT_TYPE;
+    }
+
+  env->thread[EVENT_INDEX(type)] = event_thread;
+  env->enabled[EVENT_INDEX(type)] = enabled;
+  check_enabled_event (type);
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_SetEventCallbacks (jvmtiEnv *env,
+                            const jvmtiEventCallbacks *callbacks,
+                            jint size_of_callbacks)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_ONLOAD | JVMTI_PHASE_LIVE);
+  ILLEGAL_ARGUMENT (size_of_callbacks < 0);
+
+  // Copy the list of callbacks into the environment
+  memcpy (&env->callbacks, callbacks, sizeof (jvmtiEventCallbacks));
+
+  /* Check which events are now enabeld (JVMTI makes no requirements
+     about the order in which SetEventCallbacks and SetEventNotifications
+     are called. So we must check all events here. */
+  check_enabled_events ();
+
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
+_Jv_JVMTI_GetErrorName (MAYBE_UNUSED jvmtiEnv *env, jvmtiError error,
+                       char **name_ptr)
+{
+  NULL_CHECK (name_ptr);
+
+  const char *name;
+  switch (error)
+    {
+    case JVMTI_ERROR_NONE:
+      name = "none";
+      break;
+
+    case JVMTI_ERROR_NULL_POINTER:
+      name = "null pointer";
+      break;
+
+    case JVMTI_ERROR_OUT_OF_MEMORY:
+      name = "out of memory";
+      break;
+
+    case JVMTI_ERROR_ACCESS_DENIED:
+      name = "access denied";
+      break;
+
+    case JVMTI_ERROR_WRONG_PHASE:
+      name = "wrong phase";
+      break;
+
+    case JVMTI_ERROR_INTERNAL:
+      name = "internal error";
+      break;
+
+    case JVMTI_ERROR_UNATTACHED_THREAD:
+      name = "unattached thread";
+      break;
+
+    case JVMTI_ERROR_INVALID_ENVIRONMENT:
+      name = "invalid environment";
+      break;
+
+    case JVMTI_ERROR_INVALID_PRIORITY:
+      name = "invalid priority";
+      break;
+
+    case JVMTI_ERROR_THREAD_NOT_SUSPENDED:
+      name = "thread not suspended";
+      break;
+
+    case JVMTI_ERROR_THREAD_SUSPENDED:
+      name = "thread suspended";
+      break;
+
+    case JVMTI_ERROR_THREAD_NOT_ALIVE:
+      name = "thread not alive";
+      break;
+
+    case JVMTI_ERROR_CLASS_NOT_PREPARED:
+      name = "class not prepared";
+      break;
+
+    case JVMTI_ERROR_NO_MORE_FRAMES:
+      name = "no more frames";
+      break;
+
+    case JVMTI_ERROR_OPAQUE_FRAME:
+      name = "opaque frame";
+      break;
+
+    case JVMTI_ERROR_DUPLICATE:
+      name = "duplicate";
+      break;
+
+    case JVMTI_ERROR_NOT_FOUND:
+      name = "not found";
+      break;
+
+    case JVMTI_ERROR_NOT_MONITOR_OWNER:
+      name = "not monitor owner";
+      break;
+
+    case JVMTI_ERROR_INTERRUPT:
+      name = "interrupted";
+      break;
+
+    case JVMTI_ERROR_UNMODIFIABLE_CLASS:
+      name = "unmodifiable class";
+      break;
+
+    case JVMTI_ERROR_NOT_AVAILABLE:
+      name = "not available";
+      break;
+
+    case JVMTI_ERROR_ABSENT_INFORMATION:
+      name = "absent information";
+      break;
+
+    case JVMTI_ERROR_INVALID_EVENT_TYPE:
+      name = "invalid event type";
+      break;
+
+    case JVMTI_ERROR_NATIVE_METHOD:
+      name = "native method";
+      break;
+
+    case JVMTI_ERROR_INVALID_THREAD:
+      name = "invalid thread";
+      break;
+
+    case JVMTI_ERROR_INVALID_THREAD_GROUP:
+      name = "invalid thread group";
+      break;
+
+    case JVMTI_ERROR_INVALID_OBJECT:
+      name = "invalid object";
+      break;
+
+    case JVMTI_ERROR_INVALID_CLASS:
+      name = "invalid class";
+      break;
+
+    case JVMTI_ERROR_INVALID_METHODID:
+      name = "invalid method ID";
+      break;
+
+    case JVMTI_ERROR_INVALID_LOCATION:
+      name = "invalid location";
+      break;
+
+    case JVMTI_ERROR_INVALID_FIELDID:
+      name = "invalid field ID";
+      break;
+
+    case JVMTI_ERROR_TYPE_MISMATCH:
+      name = "type mismatch";
+      break;
+
+    case JVMTI_ERROR_INVALID_SLOT:
+      name = "invalid slot";
+      break;
+
+    case JVMTI_ERROR_INVALID_MONITOR:
+      name = "invalid monitor";
+      break;
+
+    case JVMTI_ERROR_INVALID_CLASS_FORMAT:
+      name = "invalid class format";
+      break;
+
+    case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
+      name = "circular class definition";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
+      name = "unsupported redefinition: method added";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
+      name = "unsupported redefinition: schema changed";
+      break;
+
+    case JVMTI_ERROR_INVALID_TYPESTATE:
+      name = "invalid type state";
+      break;
+
+    case JVMTI_ERROR_FAILS_VERIFICATION:
+      name = "fails verification";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
+      name = "unsupported redefinition: hierarchy changed";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
+      name = "unsupported redefinition: method deleted";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_VERSION:
+      name = "unsupported version";
+      break;
+
+    case JVMTI_ERROR_NAMES_DONT_MATCH:
+      name = "names do not match";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
+      name = "unsupported redefinition: class modifiers changed";
+      break;
+
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
+      name = "unsupported redefinition: method modifiers changed";
+      break;
+
+    case JVMTI_ERROR_MUST_POSSESS_CAPABILITY:
+      name = "must possess capability";
+      break;
+
+    case JVMTI_ERROR_ILLEGAL_ARGUMENT:
+      name = "illegal argument";
+      break;
+
+    default:
+      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+  *name_ptr = (char *) _Jv_MallocUnchecked (strlen (name) + 1);
+  if (*name_ptr == NULL)
+    return JVMTI_ERROR_OUT_OF_MEMORY;
+
+  strcpy (*name_ptr, name);
+  return JVMTI_ERROR_NONE;
+}
 
 #define RESERVED NULL
 #define UNIMPLEMENTED NULL
@@ -19,13 +2050,13 @@ details.  */
 struct _Jv_jvmtiEnv _Jv_JVMTI_Interface =
 {
   RESERVED,                    // reserved1
-  UNIMPLEMENTED,               // SetEventNotification
+  _Jv_JVMTI_SetEventNotificationMode, // SetEventNotificationMode
   RESERVED,                    // reserved3
-  UNIMPLEMENTED,               // GetAllThreads
-  UNIMPLEMENTED,               // SuspendThread
-  UNIMPLEMENTED,               // ResumeThread
+  _Jv_JVMTI_GetAllThreads,             // GetAllThreads
+  _Jv_JVMTI_SuspendThread,     // SuspendThread
+  _Jv_JVMTI_ResumeThread,      // ResumeThread
   UNIMPLEMENTED,               // StopThread
-  UNIMPLEMENTED,               // InterruptThread
+  _Jv_JVMTI_InterruptThread,   // InterruptThread
   UNIMPLEMENTED,               // GetThreadInfo
   UNIMPLEMENTED,               // GetOwnedMonitorInfo
   UNIMPLEMENTED,               // GetCurrentContendedMonitor
@@ -33,70 +2064,70 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface =
   UNIMPLEMENTED,               // GetTopThreadGroups
   UNIMPLEMENTED,               // GetThreadGroupInfo
   UNIMPLEMENTED,               // GetThreadGroupChildren
-  UNIMPLEMENTED,               // GetFrameCount
-  UNIMPLEMENTED,               // GetThreadState
+  _Jv_JVMTI_GetFrameCount,             // GetFrameCount
+  _Jv_JVMTI_GetThreadState,    // GetThreadState
   RESERVED,                    // reserved18
   UNIMPLEMENTED,               // GetFrameLocation
   UNIMPLEMENTED,               // NotifyPopFrame
-  UNIMPLEMENTED,               // GetLocalObject
-  UNIMPLEMENTED,               // GetLocalInt
-  UNIMPLEMENTED,               // GetLocalLong
-  UNIMPLEMENTED,               // GetLocalFloat
-  UNIMPLEMENTED,               // GetLocalDouble
-  UNIMPLEMENTED,               // SetLocalObject
-  UNIMPLEMENTED,               // SetLocalInt
-  UNIMPLEMENTED,               // SetLocalLong
-  UNIMPLEMENTED,               // SetLocalFloat
-  UNIMPLEMENTED,               // SetLocalDouble
-  UNIMPLEMENTED,               // CreateRawMonitor
-  UNIMPLEMENTED,               // DestroyRawMonitor
-  UNIMPLEMENTED,               // RawMonitorEnter
-  UNIMPLEMENTED,               // RawMonitorExit
-  UNIMPLEMENTED,               // RawMonitorWait
-  UNIMPLEMENTED,               // RawMonitorNotify
-  UNIMPLEMENTED,               // RawMonitorNotifyAll
-  UNIMPLEMENTED,               // SetBreakpoint
-  UNIMPLEMENTED,               // CleareBreakpoint
+  _Jv_JVMTI_GetLocalObject,            // GetLocalObject
+  _Jv_JVMTI_GetLocalInt,               // GetLocalInt
+  _Jv_JVMTI_GetLocalLong,              // GetLocalLong
+  _Jv_JVMTI_GetLocalFloat,             // GetLocalFloat
+  _Jv_JVMTI_GetLocalDouble,            // GetLocalDouble
+  _Jv_JVMTI_SetLocalObject,            // SetLocalObject
+  _Jv_JVMTI_SetLocalInt,               // SetLocalInt
+  _Jv_JVMTI_SetLocalLong,              // SetLocalLong
+  _Jv_JVMTI_SetLocalFloat,             // SetLocalFloat
+  _Jv_JVMTI_SetLocalDouble,            // SetLocalDouble
+  _Jv_JVMTI_CreateRawMonitor,  // CreateRawMonitor
+  _Jv_JVMTI_DestroyRawMonitor, // DestroyRawMonitor
+  _Jv_JVMTI_RawMonitorEnter,   // RawMonitorEnter
+  _Jv_JVMTI_RawMonitorExit,    // RawMonitorExit
+  _Jv_JVMTI_RawMonitorWait,    // RawMonitorWait
+  _Jv_JVMTI_RawMonitorNotify,  // RawMonitorNotify
+  _Jv_JVMTI_RawMonitorNotifyAll, // RawMonitorNotifyAll
+  _Jv_JVMTI_SetBreakpoint,     // SetBreakpoint
+  _Jv_JVMTI_ClearBreakpoint,   // ClearBreakpoint
   RESERVED,                    // reserved40
   UNIMPLEMENTED,               // SetFieldAccessWatch
   UNIMPLEMENTED,               // ClearFieldAccessWatch
   UNIMPLEMENTED,               // SetFieldModificationWatch
   UNIMPLEMENTED,               // ClearFieldModificationWatch
   RESERVED,                    // reserved45
-  UNIMPLEMENTED,               // Allocate
-  UNIMPLEMENTED,               // Deallocate
+  _Jv_JVMTI_Allocate,          // Allocate
+  _Jv_JVMTI_Deallocate,                // Deallocate
   UNIMPLEMENTED,               // GetClassSignature
-  UNIMPLEMENTED,               // GetClassStatus
+  _Jv_JVMTI_GetClassStatus,    // GetClassStatus
   UNIMPLEMENTED,               // GetSourceFileName
-  UNIMPLEMENTED,               // GetClassModifiers
-  UNIMPLEMENTED,               // GetClassMethods
+  _Jv_JVMTI_GetClassModifiers, // GetClassModifiers
+  _Jv_JVMTI_GetClassMethods,   // GetClassMethods
   UNIMPLEMENTED,               // GetClassFields
   UNIMPLEMENTED,               // GetImplementedInterfaces
-  UNIMPLEMENTED,               // IsInterface
-  UNIMPLEMENTED,               // IsArrayClass
-  UNIMPLEMENTED,               // GetClassLoader
-  UNIMPLEMENTED,               // GetObjectHashCode
+  _Jv_JVMTI_IsInterface,       // IsInterface
+  _Jv_JVMTI_IsArrayClass,      // IsArrayClass
+  _Jv_JVMTI_GetClassLoader,    // GetClassLoader
+  _Jv_JVMTI_GetObjectHashCode, // GetObjectHashCode
   UNIMPLEMENTED,               // GetObjectMonitorUsage
   UNIMPLEMENTED,               // GetFieldName
   UNIMPLEMENTED,               // GetFieldDeclaringClass
-  UNIMPLEMENTED,               // GetFieldModifiers
-  UNIMPLEMENTED,               // IsFieldSynthetic
-  UNIMPLEMENTED,               // GetMethodName
-  UNIMPLEMENTED,               // GetMethodDeclaringClass
-  UNIMPLEMENTED,               // GetMethodModiifers
+  _Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers
+  _Jv_JVMTI_IsFieldSynthetic,  // IsFieldSynthetic
+  _Jv_JVMTI_GetMethodName,     // GetMethodName
+  _Jv_JVMTI_GetMethodDeclaringClass,  // GetMethodDeclaringClass
+  _Jv_JVMTI_GetMethodModifiers,        // GetMethodModifers
   RESERVED,                    // reserved67
-  UNIMPLEMENTED,               // GetMaxLocals
-  UNIMPLEMENTED,               // GetArgumentsSize
-  UNIMPLEMENTED,               // GetLineNumberTable
+  _Jv_JVMTI_GetMaxLocals,              // GetMaxLocals
+  _Jv_JVMTI_GetArgumentsSize,          // GetArgumentsSize
+  _Jv_JVMTI_GetLineNumberTable,        // GetLineNumberTable
   UNIMPLEMENTED,               // GetMethodLocation
-  UNIMPLEMENTED,               // GetLocalVariableTable
+  _Jv_JVMTI_GetLocalVariableTable,             // GetLocalVariableTable
   RESERVED,                    // reserved73
   RESERVED,                    // reserved74
   UNIMPLEMENTED,               // GetBytecodes
-  UNIMPLEMENTED,               // IsMethodNative
-  UNIMPLEMENTED,               // IsMethodSynthetic
+  _Jv_JVMTI_IsMethodNative,    // IsMethodNative
+  _Jv_JVMTI_IsMethodSynthetic, // IsMethodSynthetic
   UNIMPLEMENTED,               // GetLoadedClasses
-  UNIMPLEMENTED,               // GetClassLoaderClasses
+  _Jv_JVMTI_GetClassLoaderClasses, // GetClassLoaderClasses
   UNIMPLEMENTED,               // PopFrame
   RESERVED,                    // reserved81
   RESERVED,                    // reserved82
@@ -121,11 +2152,11 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface =
   UNIMPLEMENTED,               // GetThreadListStackTraces
   UNIMPLEMENTED,               // GetThreadLocalStorage
   UNIMPLEMENTED,               // SetThreadLocalStorage
-  UNIMPLEMENTED,               // GetStackTrace
+  _Jv_JVMTI_GetStackTrace,             // GetStackTrace
   RESERVED,                    // reserved105
   UNIMPLEMENTED,               // GetTag
   UNIMPLEMENTED,               // SetTag
-  UNIMPLEMENTED,               // ForceGarbageCollection
+  _Jv_JVMTI_ForceGarbageCollection, // ForceGarbageCollection
   UNIMPLEMENTED,               // IterateOverObjectsReachable
   UNIMPLEMENTED,               // IterateOverReachableObjects
   UNIMPLEMENTED,               // IterateOverHeap
@@ -137,41 +2168,41 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface =
   RESERVED,                    // reserved117
   RESERVED,                    // reserved118
   RESERVED,                    // reserved119
-  UNIMPLEMENTED,               // SetJNIFunctionTable
-  UNIMPLEMENTED,               // GetJNIFunctionTable
-  UNIMPLEMENTED,               // SetEventCallbacks
+  _Jv_JVMTI_SetJNIFunctionTable, // SetJNIFunctionTable
+  _Jv_JVMTI_GetJNIFunctionTable, // GetJNIFunctionTable
+  _Jv_JVMTI_SetEventCallbacks, // SetEventCallbacks
   UNIMPLEMENTED,               // GenerateEvents
   UNIMPLEMENTED,               // GetExtensionFunctions
   UNIMPLEMENTED,               // GetExtensionEvents
   UNIMPLEMENTED,               // SetExtensionEventCallback
-  UNIMPLEMENTED,               // DisposeEnvironment
-  UNIMPLEMENTED,               // GetErrorName
+  _Jv_JVMTI_DisposeEnvironment,        // DisposeEnvironment
+  _Jv_JVMTI_GetErrorName,      // GetErrorName
   UNIMPLEMENTED,               // GetJLocationFormat
   UNIMPLEMENTED,               // GetSystemProperties
-  UNIMPLEMENTED,               // GetSystemProperty
-  UNIMPLEMENTED,               // SetSystemProperty
+  _Jv_JVMTI_GetSystemProperty, // GetSystemProperty
+  _Jv_JVMTI_SetSystemProperty, // SetSystemProperty
   UNIMPLEMENTED,               // GetPhase
   UNIMPLEMENTED,               // GetCurrentThreadCpuTimerInfo
   UNIMPLEMENTED,               // GetCurrentThreadCpuTime
   UNIMPLEMENTED,               // GetThreadCpuTimerInfo
   UNIMPLEMENTED,               // GetThreadCpuTime
   UNIMPLEMENTED,               // GetTimerInfo
-  UNIMPLEMENTED,               // GetTime
+  _Jv_JVMTI_GetTime,           // GetTime
   UNIMPLEMENTED,               // GetPotentialCapabilities
   RESERVED,                    // reserved141
   UNIMPLEMENTED,               // AddCapabilities
   UNIMPLEMENTED,               // RelinquishCapabilities
-  UNIMPLEMENTED,               // GetAvailableProcessors
+  _Jv_JVMTI_GetAvailableProcessors, // GetAvailableProcessors
   RESERVED,                    // reserved145
   RESERVED,                    // reserved146
   UNIMPLEMENTED,               // GetEnvironmentLocalStorage
   UNIMPLEMENTED,               // SetEnvironmentLocalStorage
-  UNIMPLEMENTED,               // AddToBootstrapClassLoaderSearch
-  UNIMPLEMENTED,               // SetVerboseFlag
+  _Jv_JVMTI_AddToBootstrapClassLoaderSearch, // AddToBootstrapClassLoaderSearch
+  _Jv_JVMTI_SetVerboseFlag,    // SetVerboseFlag
   RESERVED,                    // reserved151
   RESERVED,                    // reserved152
   RESERVED,                    // reserved153
-  UNIMPLEMENTED                        // GetObjectSize
+  _Jv_JVMTI_GetObjectSize      // GetObjectSize
 };
 
 _Jv_JVMTIEnv *
@@ -179,6 +2210,417 @@ _Jv_GetJVMTIEnv (void)
 {
   _Jv_JVMTIEnv *env
     = (_Jv_JVMTIEnv *) _Jv_MallocUnchecked (sizeof (_Jv_JVMTIEnv));
+  memset (env, 0, sizeof (_Jv_JVMTIEnv));
   env->p = &_Jv_JVMTI_Interface;
+  struct jvmti_env_list *element
+    = (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list));
+  element->env = env;
+  element->next = NULL;
+
+  _envListLock->writeLock ()->lock ();
+  if (_jvmtiEnvironments == NULL)
+    _jvmtiEnvironments = element;
+  else
+    {
+      struct jvmti_env_list *e;
+      for (e = _jvmtiEnvironments; e->next != NULL; e = e->next)
+       ;
+      e->next = element;
+    }
+  _envListLock->writeLock ()->unlock ();
+
+  /* Mark JVMTI active. This is used to force the interpreter
+     to use either debugging or non-debugging code. Once JVMTI
+     has been enabled, the non-debug interpreter cannot be used. */
+  JVMTI::enabled = true;
   return env;
 }
+
+void
+_Jv_JVMTI_Init ()
+{
+  _jvmtiEnvironments = NULL;
+  _envListLock
+    = new java::util::concurrent::locks::ReentrantReadWriteLock ();
+
+  // No environments, so this should set all JVMTI:: members to false
+  check_enabled_events ();
+}
+
+static void
+post_event (jvmtiEnv *env, jvmtiEvent type, jthread event_thread, va_list args)
+{
+#define ARG(Type,Name) Type Name = (Type) va_arg (args, Type)
+
+#define GET_BOOLEAN_ARG(Name)                  \
+  ARG (int, b);                                        \
+  jboolean Name = (b == 0) ? false : true
+
+#define GET_CHAR_ARG(Name)                     \
+  ARG (int, c);                                        \
+  char Name = static_cast<char> (c)
+
+  switch (type)
+    {
+    case JVMTI_EVENT_VM_INIT:
+      if (env->callbacks.VMInit != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         env->callbacks.VMInit (env, jni_env, event_thread);
+       }
+      break;
+
+    case JVMTI_EVENT_VM_DEATH:
+      if (env->callbacks.VMDeath != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         env->callbacks.VMDeath (env, jni_env);
+       }
+      break;
+
+    case JVMTI_EVENT_THREAD_START:
+      if (env->callbacks.ThreadStart != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         env->callbacks.ThreadStart (env, jni_env, event_thread);
+       }
+      break;
+
+    case JVMTI_EVENT_THREAD_END:
+      if (env->callbacks.ThreadEnd != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         env->callbacks.ThreadEnd (env, jni_env, event_thread);
+       }
+      break;
+
+    case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+      if (env->callbacks.ClassFileLoadHook != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jclass, class_being_redefined);
+         ARG (jobject, loader);
+         ARG (const char *, name);
+         ARG (jobject, protection_domain);
+         ARG (jint, class_data_len);
+         ARG (const unsigned char *, class_data);
+         ARG (jint *, new_class_data_len);
+         ARG (unsigned char **, new_class_data);
+         env->callbacks.ClassFileLoadHook (env, jni_env,
+                                           class_being_redefined, loader,
+                                           name, protection_domain,
+                                           class_data_len, class_data,
+                                           new_class_data_len,
+                                           new_class_data);
+       }
+      break;
+
+    case JVMTI_EVENT_CLASS_LOAD:
+      if (env->callbacks.ClassLoad != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jclass, klass);
+         env->callbacks.ClassLoad (env, jni_env, event_thread, klass);
+       }
+      break;
+
+    case JVMTI_EVENT_CLASS_PREPARE:
+      if (env->callbacks.ClassPrepare != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jclass, klass);
+         env->callbacks.ClassPrepare (env, jni_env, event_thread, klass);
+       }
+      break;
+
+    case JVMTI_EVENT_VM_START:
+      if (env->callbacks.VMStart != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         env->callbacks.VMStart (env, jni_env);
+       }
+      break;
+
+    case JVMTI_EVENT_EXCEPTION:
+      if (env->callbacks.Exception != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         ARG (jobject, exception);
+         ARG (jmethodID, catch_method);
+         ARG (jlocation, catch_location);
+         env->callbacks.Exception (env, jni_env, event_thread, method,
+                                   location, exception, catch_method,
+                                   catch_location);
+       }
+      break;
+
+    case JVMTI_EVENT_EXCEPTION_CATCH:
+      if (env->callbacks.ExceptionCatch != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         ARG (jobject, exception);
+         env->callbacks.ExceptionCatch (env, jni_env, event_thread, method,
+                                        location, exception);
+       }
+      break;
+
+    case JVMTI_EVENT_SINGLE_STEP:
+      if (env->callbacks.SingleStep != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         env->callbacks.SingleStep (env, jni_env, event_thread, method,
+                                    location);
+       }
+      break;
+
+    case JVMTI_EVENT_FRAME_POP:
+      if (env->callbacks.FramePop != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         GET_BOOLEAN_ARG (was_popped_by_exception);
+         env->callbacks.FramePop (env, jni_env, event_thread, method,
+                                  was_popped_by_exception);
+       }
+      break;
+
+    case JVMTI_EVENT_BREAKPOINT:
+      if (env->callbacks.Breakpoint != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         env->callbacks.Breakpoint (env, jni_env, event_thread, method,
+                                    location);
+       }
+      break;
+
+    case JVMTI_EVENT_FIELD_ACCESS:
+      if (env->callbacks.FieldAccess != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         ARG (jclass, field_class);
+         ARG (jobject, object);
+         ARG (jfieldID, field);
+         env->callbacks.FieldAccess (env, jni_env, event_thread, method,
+                                     location, field_class, object, field);
+       }
+      break;
+
+    case JVMTI_EVENT_FIELD_MODIFICATION:
+      if (env->callbacks.FieldModification != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (jlocation, location);
+         ARG (jclass, field_class);
+         ARG (jobject, object);
+         ARG (jfieldID, field);
+         GET_CHAR_ARG (signature_type);
+         ARG (jvalue, new_value);
+         env->callbacks.FieldModification (env, jni_env, event_thread, method,
+                                           location, field_class, object,
+                                           field, signature_type, new_value);
+       }
+      break;
+
+    case JVMTI_EVENT_METHOD_ENTRY:
+      if (env->callbacks.MethodEntry != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         env->callbacks.MethodEntry (env, jni_env, event_thread, method);
+       }
+      break;
+
+    case JVMTI_EVENT_METHOD_EXIT:
+      if (env->callbacks.MethodExit != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         GET_BOOLEAN_ARG (was_popped_by_exception);
+         ARG (jvalue, return_value);
+         env->callbacks.MethodExit (env, jni_env, event_thread, method,
+                                    was_popped_by_exception, return_value);
+       }
+      break;
+
+    case JVMTI_EVENT_NATIVE_METHOD_BIND:
+      if (env->callbacks.NativeMethodBind != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jmethodID, method);
+         ARG (void *, address);
+         ARG (void **, new_address_ptr);
+         env->callbacks.NativeMethodBind (env, jni_env, event_thread, method,
+                                          address, new_address_ptr);
+       }
+      break;
+
+    case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+      if (env->callbacks.CompiledMethodLoad != NULL)
+       {
+         ARG (jmethodID, method);
+         ARG (jint, code_size);
+         ARG (const void *, code_addr);
+         ARG (jint, map_length);
+         ARG (const jvmtiAddrLocationMap *, map);
+         ARG (const void *, compile_info);
+         env->callbacks.CompiledMethodLoad (env, method, code_size, code_addr,
+                                            map_length, map, compile_info);
+       }
+      break;
+
+    case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+      if (env->callbacks.CompiledMethodUnload != NULL)
+       {
+         ARG (jmethodID, method);
+         ARG (const void *, code_addr);
+         env->callbacks.CompiledMethodUnload (env, method, code_addr);
+       }
+      break;
+
+    case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+      if (env->callbacks.DynamicCodeGenerated != NULL)
+       {
+         ARG (const char *, name);
+         ARG (const void *, address);
+         ARG (jint, length);
+         env->callbacks.DynamicCodeGenerated (env, name, address, length);
+       }
+      break;
+
+    case JVMTI_EVENT_DATA_DUMP_REQUEST:
+      if (env->callbacks.DataDumpRequest != NULL)
+       {
+         env->callbacks.DataDumpRequest (env);
+       }
+      break;
+
+    case JVMTI_EVENT_MONITOR_WAIT:
+      if (env->callbacks.MonitorWait != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jobject, object);
+         ARG (jlong, timeout);
+         env->callbacks.MonitorWait (env, jni_env, event_thread, object,
+                                     timeout);
+       }
+      break;
+
+    case JVMTI_EVENT_MONITOR_WAITED:
+      if (env->callbacks.MonitorWaited != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jobject, object);
+         GET_BOOLEAN_ARG (timed_out);
+         env->callbacks.MonitorWaited (env, jni_env, event_thread, object,
+                                       timed_out);
+       }
+      break;
+
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+      if (env->callbacks.MonitorContendedEnter != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jobject, object);
+         env->callbacks.MonitorContendedEnter (env, jni_env, event_thread,
+                                               object);
+       }
+      break;
+
+    case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+      if (env->callbacks.MonitorContendedEntered != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jobject, object);
+         env->callbacks.MonitorContendedEntered (env, jni_env, event_thread,
+                                                 object);
+       }
+      break;
+
+    case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+      if (env->callbacks.GarbageCollectionStart != NULL)
+       {
+         env->callbacks.GarbageCollectionStart (env);
+       }
+      break;
+
+    case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+      if (env->callbacks.GarbageCollectionFinish != NULL)
+       {
+         env->callbacks.GarbageCollectionFinish (env);
+       }
+      break;
+
+    case JVMTI_EVENT_OBJECT_FREE:
+      if (env->callbacks.ObjectFree != NULL)
+       {
+         ARG (jlong, tag);
+         env->callbacks.ObjectFree (env, tag);
+       }
+      break;
+
+    case JVMTI_EVENT_VM_OBJECT_ALLOC:
+      if (env->callbacks.VMObjectAlloc != NULL)
+       {
+         ARG (JNIEnv *, jni_env);
+         ARG (jobject, object);
+         ARG (jclass, object_class);
+         ARG (jlong, size);
+         env->callbacks.VMObjectAlloc (env, jni_env, event_thread,
+                                       object, object_class, size);
+       }
+      break;
+
+    default:
+      fprintf (stderr, "libgcj: post of unknown JVMTI event (%d)\n",
+              (int) type);
+      break;
+    }
+  va_end (args);
+#undef ARG
+#undef GET_BOOLEAN_ARG
+#undef GET_CHAR_ARG
+}
+
+/* Post an event to requesting JVMTI environments
+ *
+ * This function should not be called without consulting the
+ * JVMTI_REQUESTED_EVENT macro first (for speed). It does no real
+ * harm (other than kill speed), since this function will still
+ * only send the event if it was properly requested by an environment.
+ */ 
+void
+_Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...)
+{
+  va_list args;
+  va_start (args, event_thread);
+
+  _envListLock->readLock ()->lock ();
+  struct jvmti_env_list *e;
+  FOREACH_ENVIRONMENT (e)
+    {
+      /* Events are only posted if the event was explicitly enabled,
+        it has a registered event handler, and the event thread
+        matches (either globally or restricted to a specific thread).
+        Here we check all but the event handler, which will be handled
+        by post_event. */
+      if (e->env->enabled[EVENT_INDEX(type)]
+         && (e->env->thread[EVENT_INDEX(type)] == NULL
+             || e->env->thread[EVENT_INDEX(type)] == event_thread))
+       {
+         post_event (e->env, type, event_thread, args);
+       }
+    }
+  _envListLock->readLock ()->unlock ();
+  va_end (args);
+}