aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc')
-rw-r--r--libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc875
1 files changed, 832 insertions, 43 deletions
diff --git a/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc
index 351bb23ccb2..db33781378e 100644
--- a/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc
+++ b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc
@@ -10,6 +10,8 @@ details. */
#include <config.h>
#include <gcj/cni.h>
+#include <java-assert.h>
+#include <java-interp.h>
#include <jvm.h>
#include <jvmti.h>
@@ -19,40 +21,111 @@ details. */
#include <java/lang/String.h>
#include <java/lang/StringBuilder.h>
#include <java/lang/Thread.h>
+#include <java/lang/Throwable.h>
#include <java/nio/ByteBuffer.h>
+#include <java/nio/ByteBufferImpl.h>
#include <java/util/ArrayList.h>
+#include <java/util/Collection.h>
#include <java/util/Hashtable.h>
#include <java/util/Iterator.h>
#include <gnu/classpath/jdwp/Jdwp.h>
+#include <gnu/classpath/jdwp/JdwpConstants$StepDepth.h>
+#include <gnu/classpath/jdwp/JdwpConstants$StepSize.h>
+#include <gnu/classpath/jdwp/JdwpConstants$ThreadStatus.h>
#include <gnu/classpath/jdwp/VMFrame.h>
#include <gnu/classpath/jdwp/VMMethod.h>
#include <gnu/classpath/jdwp/VMVirtualMachine.h>
+#include <gnu/classpath/jdwp/event/BreakpointEvent.h>
+#include <gnu/classpath/jdwp/event/ClassPrepareEvent.h>
+#include <gnu/classpath/jdwp/event/ExceptionEvent.h>
+#include <gnu/classpath/jdwp/event/EventManager.h>
#include <gnu/classpath/jdwp/event/EventRequest.h>
+#include <gnu/classpath/jdwp/event/SingleStepEvent.h>
+#include <gnu/classpath/jdwp/event/ThreadEndEvent.h>
+#include <gnu/classpath/jdwp/event/ThreadStartEvent.h>
+#include <gnu/classpath/jdwp/event/VmDeathEvent.h>
#include <gnu/classpath/jdwp/event/VmInitEvent.h>
+#include <gnu/classpath/jdwp/event/filters/IEventFilter.h>
+#include <gnu/classpath/jdwp/event/filters/LocationOnlyFilter.h>
+#include <gnu/classpath/jdwp/event/filters/StepFilter.h>
+#include <gnu/classpath/jdwp/exception/AbsentInformationException.h>
+#include <gnu/classpath/jdwp/exception/InvalidFrameException.h>
+#include <gnu/classpath/jdwp/exception/InvalidLocationException.h>
+#include <gnu/classpath/jdwp/exception/InvalidMethodException.h>
#include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
+#include <gnu/classpath/jdwp/id/ThreadId.h>
+#include <gnu/classpath/jdwp/util/Location.h>
#include <gnu/classpath/jdwp/util/MethodResult.h>
+#include <gnu/gcj/jvmti/Breakpoint.h>
+#include <gnu/gcj/jvmti/BreakpointManager.h>
using namespace java::lang;
using namespace gnu::classpath::jdwp::event;
using namespace gnu::classpath::jdwp::util;
+// Stepping information
+struct step_info
+{
+ jint size; // See gnu.classpath.jdwp.JdwpConstants.StepSize
+ jint depth; // See gnu.classpath.jdwp.JdwpConstants.StepDepth
+ int stack_depth; // stack depth at start of stepping
+ jmethodID method; // method in which we are stepping
+};
+
// Forward declarations
-static void jdwpVMInitCB (jvmtiEnv *env, JNIEnv *jni_env, jthread thread);
+static jvmtiError get_linetable (jvmtiEnv *, jmethodID, jint *,
+ jvmtiLineNumberEntry **);
+static Location *get_request_location (EventRequest *);
+static gnu::classpath::jdwp::event::filters::StepFilter *
+get_request_step_filter (EventRequest *);
+static void handle_single_step (jvmtiEnv *, struct step_info *, jthread,
+ jmethodID, jlocation);
+static void JNICALL jdwpBreakpointCB (jvmtiEnv *, JNIEnv *, jthread,
+ jmethodID, jlocation);
+static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
+static void JNICALL jdwpExceptionCB (jvmtiEnv *, JNIEnv *jni_env, jthread,
+ jmethodID, jlocation, jobject,
+ jmethodID, jlocation);
+static void JNICALL jdwpSingleStepCB (jvmtiEnv *, JNIEnv *, jthread,
+ jmethodID, jlocation);
+static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
+static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread);
+static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *);
+static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread);
+static void throw_jvmti_error (jvmtiError);
#define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB
+#define DISABLE_EVENT(Event,Thread) \
+ _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_DISABLE, \
+ JVMTI_EVENT_ ## Event, Thread)
#define ENABLE_EVENT(Event,Thread) \
_jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE, \
JVMTI_EVENT_ ## Event, Thread)
// JVMTI environment
static jvmtiEnv *_jdwp_jvmtiEnv;
+jvmtiEnv *
+_Jv_GetJDWP_JVMTIEnv (void)
+{
+ return _jdwp_jvmtiEnv;
+}
+
void
gnu::classpath::jdwp::VMVirtualMachine::initialize ()
{
_jdwp_suspend_counts = new ::java::util::Hashtable ();
+ _stepping_threads = new ::java::util::Hashtable ();
+ _event_list = new ::java::util::ArrayList ();
+
JavaVM *vm = _Jv_GetJavaVM ();
- vm->GetEnv (reinterpret_cast<void **> (&_jdwp_jvmtiEnv), JVMTI_VERSION_1_0);
+ union
+ {
+ void *ptr;
+ jvmtiEnv *env;
+ } foo;
+ vm->GetEnv (&(foo.ptr), JVMTI_VERSION_1_0);
+ _jdwp_jvmtiEnv = foo.env;
// Wait for VM_INIT to do more initialization
jvmtiEventCallbacks callbacks;
@@ -62,7 +135,7 @@ gnu::classpath::jdwp::VMVirtualMachine::initialize ()
}
void
-gnu::classpath::jdwp::VMVirtualMachine ::suspendThread (Thread *thread)
+gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
{
jint value;
Integer *count;
@@ -170,9 +243,57 @@ gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
switch (request->getEventKind ())
{
case EventRequest::EVENT_SINGLE_STEP:
+ {
+ Thread *thread;
+ filters::StepFilter *filter = get_request_step_filter (request);
+ if (filter == NULL)
+ {
+ // No filter specified: report every step in every
+ // thread.
+ thread = NULL;
+ }
+ else
+ {
+ // Add stepping information to list of stepping threads
+ thread = filter->getThread ()->getThread ();
+ _Jv_InterpFrame *frame
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ struct step_info *sinfo
+ = (struct step_info *) JvAllocBytes (sizeof (struct step_info));
+ sinfo->size = filter->getSize ();
+ sinfo->depth = filter->getDepth ();
+ sinfo->stack_depth = frame->depth ();
+ sinfo->method = frame->self->get_method ();
+ _stepping_threads->put (thread, (jobject) sinfo);
+ }
+
+ ENABLE_EVENT (SINGLE_STEP, thread);
+ }
break;
case EventRequest::EVENT_BREAKPOINT:
+ {
+ using namespace ::gnu::gcj::jvmti;
+ Location *loc = get_request_location (request);
+ if (loc == NULL)
+ {
+ using namespace gnu::classpath::jdwp::exception;
+ throw new InvalidLocationException ();
+ }
+
+ jlong method = loc->getMethod ()->getId ();
+ jlocation index = loc->getIndex ();
+ Breakpoint *bp = BreakpointManager::getBreakpoint (method, index);
+ if (bp == NULL)
+ {
+ // Breakpoint not in interpreter yet
+ bp = BreakpointManager::newBreakpoint (method, index);
+ }
+ else
+ {
+ // Ignore the duplicate
+ }
+ }
break;
case EventRequest::EVENT_FRAME_POP:
@@ -213,7 +334,7 @@ gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
case EventRequest::EVENT_VM_INIT:
break;
-
+
case EventRequest::EVENT_VM_DEATH:
break;
}
@@ -225,9 +346,62 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
switch (request->getEventKind ())
{
case EventRequest::EVENT_SINGLE_STEP:
+ {
+ Thread *thread;
+ filters::StepFilter *filter = get_request_step_filter (request);
+ if (filter == NULL)
+ thread = NULL;
+ else
+ {
+ thread = filter->getThread ()->getThread ();
+ _stepping_threads->remove (thread);
+ }
+
+ DISABLE_EVENT (SINGLE_STEP, thread);
+ }
break;
case EventRequest::EVENT_BREAKPOINT:
+ {
+ using namespace gnu::gcj::jvmti;
+ ::java::util::Collection *breakpoints;
+ EventManager *em = EventManager::getDefault ();
+ breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
+
+ // Check for duplicates
+ int matches = 0;
+ Location *the_location = get_request_location (request);
+
+ // This should not be possible: we REQUIRE a Location
+ // to install a breakpoint
+ JvAssert (the_location != NULL);
+
+ ::java::util::Iterator *iter = breakpoints->iterator ();
+ while (iter->hasNext ())
+ {
+ EventRequest *er
+ = reinterpret_cast<EventRequest *> (iter->next ());
+ Location *loc = get_request_location (er);
+ JvAssert (loc != NULL);
+ if (loc->equals (the_location) && ++matches == 2)
+ {
+ // Short-circuit: already more than one breakpoint
+ return;
+ }
+ }
+
+ if (matches == 0)
+ {
+ using namespace gnu::classpath::jdwp::exception;
+ jstring msg
+ = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
+ throw new JdwpInternalErrorException (msg);
+ }
+
+ jlong methodId = the_location->getMethod ()->getId ();
+ BreakpointManager::deleteBreakpoint (methodId,
+ the_location->getIndex ());
+ }
break;
case EventRequest::EVENT_FRAME_POP:
@@ -244,7 +418,7 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
case EventRequest::EVENT_THREAD_END:
break;
-
+
case EventRequest::EVENT_CLASS_PREPARE:
break;
@@ -268,103 +442,718 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
case EventRequest::EVENT_VM_INIT:
break;
-
+
case EventRequest::EVENT_VM_DEATH:
break;
}
}
void
-gnu::classpath::jdwp::VMVirtualMachine::clearEvents (jbyte kind)
+gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
{
}
-jint
-gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void)
-{
- return 0;
-}
-
-java::util::Iterator *
+java::util::Collection *
gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
{
- return NULL;
+ using namespace ::java::util;
+ return (Collection *) new ArrayList ();
}
jint
-gnu::classpath::jdwp::VMVirtualMachine::getClassStatus (jclass klass)
+gnu::classpath::jdwp::VMVirtualMachine::
+getClassStatus (jclass klass)
{
- return 0;
+ jint flags = 0;
+ jvmtiError err = _jdwp_jvmtiEnv->GetClassStatus (klass, &flags);
+ if (err != JVMTI_ERROR_NONE)
+ throw_jvmti_error (err);
+
+ using namespace gnu::classpath::jdwp::event;
+ jint status = 0;
+ if (flags & JVMTI_CLASS_STATUS_VERIFIED)
+ status |= ClassPrepareEvent::STATUS_VERIFIED;
+ if (flags & JVMTI_CLASS_STATUS_PREPARED)
+ status |= ClassPrepareEvent::STATUS_PREPARED;
+ if (flags & JVMTI_CLASS_STATUS_ERROR)
+ status |= ClassPrepareEvent::STATUS_ERROR;
+ if (flags & JVMTI_CLASS_STATUS_INITIALIZED)
+ status |= ClassPrepareEvent::STATUS_INITIALIZED;
+
+ return status;
}
JArray<gnu::classpath::jdwp::VMMethod *> *
-gnu::classpath::jdwp::VMVirtualMachine::getAllClassMethods (jclass klass)
+gnu::classpath::jdwp::VMVirtualMachine::
+getAllClassMethods (jclass klass)
{
- return NULL;
+ jint count;
+ jmethodID *methods;
+ jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
+ if (err != JVMTI_ERROR_NONE)
+ throw_jvmti_error (err);
+
+ JArray<VMMethod *> *result
+ = (JArray<VMMethod *> *) JvNewObjectArray (count,
+ &VMMethod::class$, NULL);
+ VMMethod **rmeth = elements (result);
+ for (int i = 0; i < count; ++i)
+ {
+ jlong id = reinterpret_cast<jlong> (methods[i]);
+ rmeth[i] = getClassMethod (klass, id);
+ }
+
+ _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
+ return result;
}
gnu::classpath::jdwp::VMMethod *
-gnu::classpath::jdwp::VMVirtualMachine::getClassMethod (jclass klass, jlong id)
+gnu::classpath::jdwp::VMVirtualMachine::
+getClassMethod (jclass klass, jlong id)
{
- return NULL;
+ jint count;
+ jmethodID *methods;
+ jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
+ if (err != JVMTI_ERROR_NONE)
+ throw_jvmti_error (err);
+
+ jmethodID meth_id = reinterpret_cast<jmethodID> (id);
+
+ using namespace gnu::classpath::jdwp;
+
+ // Check if this method is defined for the given class and if so return a
+ // VMMethod representing it.
+ for (int i = 0; i < count; i++)
+ {
+ if (methods[i] == meth_id)
+ return new VMMethod (klass, reinterpret_cast<jlong> (meth_id));
+ }
+
+ throw new exception::InvalidMethodException (id);
}
java::util::ArrayList *
-gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread,
- jint start,
- jint length)
+gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, jint start,
+ jint length)
{
- return NULL;
+ jint frame_count = getFrameCount (thread);
+ ::java::util::ArrayList *frame_list;
+
+ // Calculate the max number of frames to be returned.
+ jint num_frames = frame_count - start;
+
+ // Check if num_frames is valid.
+ if (num_frames < 0)
+ num_frames = 0;
+
+ // Check if there are more than length frames left after start.
+ // If length ios -1 return all remaining frames.
+ if (length != -1 && num_frames > length)
+ num_frames = length;
+
+ frame_list = new ::java::util::ArrayList (num_frames);
+
+ _Jv_Frame *vm_frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+
+ // Take start frames off the top of the stack
+ while (vm_frame != NULL && start > 0)
+ {
+ start--;
+ vm_frame = vm_frame->next;
+ }
+
+ // Use as a counter for the number of frames returned.
+ num_frames = 0;
+
+ while (vm_frame != NULL && (num_frames < length || length == -1))
+ {
+ jlong frameId = reinterpret_cast<jlong> (vm_frame);
+
+ VMFrame *frame = getFrame (thread, frameId);
+ frame_list->add (frame);
+ vm_frame = vm_frame->next;
+ num_frames++;
+ }
+
+ return frame_list;
}
gnu::classpath::jdwp::VMFrame *
-gnu::classpath::jdwp::VMVirtualMachine::getFrame (Thread *thread,
- ::java::nio::ByteBuffer *bb)
+gnu::classpath::jdwp::VMVirtualMachine::
+getFrame (Thread *thread, jlong frameID)
{
- return NULL;
+ using namespace gnu::classpath::jdwp::exception;
+
+ _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
+ jint depth = 0;
+ _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID);
+
+ // We need to find the stack depth of the frame, so search through the call
+ // stack to find it. This also checks for a valid frameID.
+ while (vm_frame != frame)
+ {
+ vm_frame = vm_frame->next;
+ depth++;
+ if (vm_frame == NULL)
+ throw new InvalidFrameException (frameID);
+ }
+
+ Location *loc = NULL;
+ jvmtiFrameInfo info;
+ jvmtiError jerr;
+ jint num_frames;
+ jclass klass;
+
+ // Get the info for the frame of interest
+ jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
+
+ if (jerr != JVMTI_ERROR_NONE)
+ throw_jvmti_error (jerr);
+
+ jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
+
+ if (jerr != JVMTI_ERROR_NONE)
+ throw_jvmti_error (jerr);
+
+ VMMethod *meth
+ = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
+
+ jobject this_obj;
+
+ if (info.location == -1)
+ {
+ loc = new Location (meth, 0);
+ this_obj = NULL;
+ }
+ else
+ {
+ loc = new Location (meth, info.location);
+ _Jv_InterpFrame *iframe = reinterpret_cast<_Jv_InterpFrame *> (vm_frame);
+ this_obj = iframe->get_this_ptr ();
+ }
+
+ return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc,
+ this_obj);
}
jint
-gnu::classpath::jdwp::VMVirtualMachine::getFrameCount (Thread *thread)
+gnu::classpath::jdwp::VMVirtualMachine::
+getFrameCount (Thread *thread)
{
- return 0;
+ jint frame_count;
+
+ jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
+
+ if (jerr != JVMTI_ERROR_NONE)
+ throw_jvmti_error (jerr);
+
+ return frame_count;
}
jint
-gnu::classpath::jdwp::VMVirtualMachine::getThreadStatus (Thread *thread)
+gnu::classpath::jdwp::VMVirtualMachine::
+getThreadStatus (Thread *thread)
{
- return 0;
+ jint thr_state, status;
+
+ jvmtiError jerr = _jdwp_jvmtiEnv->GetThreadState (thread, &thr_state);
+ if (jerr != JVMTI_ERROR_NONE)
+ throw_jvmti_error (jerr);
+
+ if (thr_state & JVMTI_THREAD_STATE_SLEEPING)
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::SLEEPING;
+ else if (thr_state & JVMTI_THREAD_STATE_RUNNABLE)
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING;
+ else if (thr_state & JVMTI_THREAD_STATE_WAITING)
+ {
+ if (thr_state & (JVMTI_THREAD_STATE_IN_OBJECT_WAIT
+ | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER))
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::MONITOR;
+ else
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::WAIT;
+ }
+ else
+ {
+ // The thread is not SLEEPING, MONITOR, or WAIT. It may, however, be
+ // alive but not yet started.
+ if (!(thr_state & (JVMTI_THREAD_STATE_ALIVE
+ | JVMTI_THREAD_STATE_TERMINATED)))
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING;
+ status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::ZOMBIE;
+ }
+
+ return status;
}
java::util::ArrayList *
-gnu::classpath::jdwp::VMVirtualMachine::getLoadRequests (ClassLoader *cl)
+gnu::classpath::jdwp::VMVirtualMachine::
+getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
{
- return NULL;
+ return new ::java::util::ArrayList ();
}
MethodResult *
-gnu::classpath::jdwp::VMVirtualMachine::executeMethod (jobject obj,
- Thread *thread,
- jclass clazz,
- reflect::Method *method,
- jobjectArray values,
- jboolean nonVirtual)
+gnu::classpath::jdwp::VMVirtualMachine::
+executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread,
+ MAYBE_UNUSED jclass clazz, MAYBE_UNUSED reflect::Method *method,
+ MAYBE_UNUSED jobjectArray values,
+ MAYBE_UNUSED jboolean nonVirtual)
{
return NULL;
}
jstring
-gnu::classpath::jdwp::VMVirtualMachine::getSourceFile (jclass clazz)
+gnu::classpath::jdwp::VMVirtualMachine::
+getSourceFile (jclass clazz)
+{
+ jstring file = _Jv_GetInterpClassSourceFile (clazz);
+
+ // Check if the source file was found.
+ if (file == NULL)
+ throw new exception::AbsentInformationException (
+ _Jv_NewStringUTF("Source file not found"));
+
+ return file;
+}
+
+void
+gnu::classpath::jdwp::VMVirtualMachine::
+redefineClasses (MAYBE_UNUSED JArray<jclass> *types,
+ MAYBE_UNUSED JArray<jbyteArray> *bytecodes)
+{
+}
+
+void
+gnu::classpath::jdwp::VMVirtualMachine::
+setDefaultStratum (MAYBE_UNUSED jstring stratum)
+{
+}
+
+jstring
+gnu::classpath::jdwp::VMVirtualMachine::
+getSourceDebugExtension (MAYBE_UNUSED jclass klass)
{
return NULL;
}
+jbyteArray
+gnu::classpath::jdwp::VMVirtualMachine::
+getBytecodes (MAYBE_UNUSED gnu::classpath::jdwp::VMMethod *method)
+{
+ return NULL;
+}
+
+gnu::classpath::jdwp::util::MonitorInfo *
+gnu::classpath::jdwp::VMVirtualMachine::
+getMonitorInfo (MAYBE_UNUSED jobject obj)
+{
+ return NULL;
+}
+
+jobjectArray
+gnu::classpath::jdwp::VMVirtualMachine::
+getOwnedMonitors (MAYBE_UNUSED ::java::lang::Thread *thread)
+{
+ return NULL;
+}
+
+jobject
+gnu::classpath::jdwp::VMVirtualMachine::
+getCurrentContendedMonitor (MAYBE_UNUSED ::java::lang::Thread *thread)
+{
+ return NULL;
+}
+
+void
+gnu::classpath::jdwp::VMVirtualMachine::
+popFrames (MAYBE_UNUSED ::java::lang::Thread *thread,
+ MAYBE_UNUSED jlong frameId)
+{
+}
+
+// A simple caching function used while single-stepping
+static jvmtiError
+get_linetable (jvmtiEnv *env, jmethodID method, jint *count_ptr,
+ jvmtiLineNumberEntry **table_ptr)
+{
+ static jint last_count = 0;
+ static jvmtiLineNumberEntry *last_table = NULL;
+ static jmethodID last_method = 0;
+
+ if (method == last_method)
+ {
+ *count_ptr = last_count;
+ *table_ptr = last_table;
+ return JVMTI_ERROR_NONE;
+ }
+
+ jvmtiError err;
+ jint count;
+ jvmtiLineNumberEntry *table;
+ err = env->GetLineNumberTable (method, &count, &table);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ // Keep last table in cache
+ return err;
+ }
+
+ env->Deallocate ((unsigned char *) last_table);
+ last_table = *table_ptr = table;
+ last_count = *count_ptr = count;
+ return JVMTI_ERROR_NONE;
+}
+
+static gnu::classpath::jdwp::event::filters::StepFilter *
+get_request_step_filter (EventRequest *request)
+{
+ ::java::util::Collection *filters = request->getFilters ();
+ ::java::util::Iterator *iter = filters->iterator ();
+ filters::StepFilter *filter = NULL;
+ while (iter->hasNext ())
+ {
+ using namespace gnu::classpath::jdwp::event::filters;
+ IEventFilter *next = (IEventFilter *) iter->next ();
+ if (next->getClass () == &StepFilter::class$)
+ {
+ filter = reinterpret_cast<StepFilter *> (next);
+ break;
+ }
+ }
+
+ return filter;
+}
+
+static Location *
+get_request_location (EventRequest *request)
+{
+ Location *loc = NULL;
+ ::java::util::Collection *filters = request->getFilters ();
+ ::java::util::Iterator *iter = filters->iterator ();
+ while (iter->hasNext ())
+ {
+ using namespace gnu::classpath::jdwp::event::filters;
+ IEventFilter *filter = (IEventFilter *) iter->next ();
+ if (filter->getClass () == &LocationOnlyFilter::class$)
+ {
+ LocationOnlyFilter *lof
+ = reinterpret_cast<LocationOnlyFilter *> (filter);
+ loc = lof->getLocation ();
+ }
+ }
+
+ return loc;
+}
+
static void
+handle_single_step (jvmtiEnv *env, struct step_info *sinfo, jthread thread,
+ jmethodID method, jlocation location)
+{
+ using namespace gnu::classpath::jdwp;
+
+ if (sinfo == NULL || sinfo->size == JdwpConstants$StepSize::MIN)
+ {
+ // Stop now
+ goto send_notification;
+ }
+ else
+ {
+ // Check if we're on a new source line
+ /* This is a little inefficient when we're stepping OVER,
+ but this must be done when stepping INTO. */
+ jint count;
+ jvmtiLineNumberEntry *table;
+ if (get_linetable (env, method, &count, &table) == JVMTI_ERROR_NONE)
+ {
+ jint i;
+ for (i = 0; i < count; ++i)
+ {
+ if (table[i].start_location == location)
+ {
+ // This is the start of a new line -- stop
+ goto send_notification;
+ }
+ }
+
+ // Not at a new source line -- just keep stepping
+ return;
+ }
+ else
+ {
+ /* Something went wrong: either "absent information"
+ or "out of memory" ("invalid method id" and "native
+ method" aren't possible -- those are validated before
+ single stepping is enabled).
+
+ Do what gdb does: just keep going. */
+ return;
+ }
+ }
+
+ send_notification:
+ jclass klass;
+ jvmtiError err = env->GetMethodDeclaringClass (method, &klass);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ fprintf (stderr, "libgcj: internal error: could not find class for method while single stepping -- continuing\n");
+ return;
+ }
+
+ VMMethod *vmmethod = new VMMethod (klass, reinterpret_cast<jlong> (method));
+ Location *loc = new Location (vmmethod, location);
+ JvAssert (thread->frame.frame_type == frame_interpreter);
+ _Jv_InterpFrame *iframe
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ jobject instance = iframe->get_this_ptr ();
+ event::SingleStepEvent *event
+ = new event::SingleStepEvent (thread, loc, instance);
+
+ // We only want to send the notification (and consequently
+ // suspend) if we are not about to execute a breakpoint.
+ _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (iframe->self);
+ if (im->breakpoint_at (location))
+ {
+ // Next insn is a breakpoint -- record event and
+ // wait for the JVMTI breakpoint notification to
+ // enforce a suspension policy.
+ VMVirtualMachine::_event_list->add (event);
+ }
+ else
+ {
+ // Next insn is not a breakpoint, so send notification
+ // and enforce the suspend policy.
+ Jdwp::notify (event);
+ }
+}
+
+static void
+throw_jvmti_error (jvmtiError err)
+{
+ char *error;
+ jstring msg;
+ if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
+ {
+ msg = JvNewStringLatin1 (error);
+ _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
+ }
+ else
+ msg = JvNewStringLatin1 ("out of memory");
+
+ using namespace gnu::classpath::jdwp::exception;
+ throw new JdwpInternalErrorException (msg);
+}
+
+static void JNICALL
+jdwpBreakpointCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
+ jthread thread, jmethodID method, jlocation location)
+{
+ jclass klass;
+ jvmtiError err;
+ err = env->GetMethodDeclaringClass (method, &klass);
+ JvAssert (err == JVMTI_ERROR_NONE);
+
+ using namespace gnu::classpath::jdwp;
+ using namespace gnu::classpath::jdwp::event;
+
+ jlong methodId = reinterpret_cast<jlong> (method);
+ VMMethod *meth = VMVirtualMachine::getClassMethod (klass, methodId);
+ Location *loc = new Location (meth, location);
+ JvAssert (thread->frame.frame_type == frame_interpreter);
+ _Jv_InterpFrame *iframe
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ jobject instance = iframe->get_this_ptr ();
+ BreakpointEvent *event = new BreakpointEvent (thread, loc, instance);
+
+ VMVirtualMachine::_event_list->add (event);
+ JArray<Event *> *events
+ = ((JArray<Event *> *)
+ JvNewObjectArray (VMVirtualMachine::_event_list->size (),
+ &Event::class$, NULL));
+ VMVirtualMachine::_event_list->toArray ((jobjectArray) events);
+ VMVirtualMachine::_event_list->clear ();
+ Jdwp::notify (events);
+}
+
+static void JNICALL
+jdwpClassPrepareCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
+ jthread thread, jclass klass)
+{
+ using namespace gnu::classpath::jdwp;
+
+ jint status = VMVirtualMachine::getClassStatus (klass);
+ event::ClassPrepareEvent *event
+ = new event::ClassPrepareEvent (thread, klass, status);
+ Jdwp::notify (event);
+}
+
+static void JNICALL
+jdwpExceptionCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread,
+ jmethodID method, jlocation location, jobject exception,
+ jmethodID catch_method, jlocation catch_location)
+{
+ using namespace gnu::classpath::jdwp;
+ jclass throw_klass;
+ jvmtiError err = env->GetMethodDeclaringClass (method, &throw_klass);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ fprintf (stderr, "libgcj: internal error: could not find class for ");
+ fprintf (stderr, "method throwing exception -- continuing\n");
+ return;
+ }
+
+ VMMethod *vmmethod = new VMMethod (throw_klass,
+ reinterpret_cast<jlong> (method));
+ Location *throw_loc = new Location (vmmethod, location);
+ Location *catch_loc = NULL;
+ if (catch_method == 0)
+ catch_loc = Location::getEmptyLocation ();
+ else
+ {
+ jclass catch_klass;
+ err = env->GetMethodDeclaringClass (catch_method, &catch_klass);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ fprintf (stderr,
+ "libgcj: internal error: could not find class for ");
+ fprintf (stderr,
+ "method catching exception -- ignoring\n");
+ }
+ else
+ {
+ vmmethod = new VMMethod (catch_klass,
+ reinterpret_cast<jlong> (catch_method));
+ catch_loc = new Location (vmmethod, catch_location);
+ }
+ }
+
+ _Jv_InterpFrame *iframe
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ jobject instance = (iframe == NULL) ? NULL : iframe->get_this_ptr ();
+ Throwable *throwable = reinterpret_cast<Throwable *> (exception);
+ event::ExceptionEvent *e = new ExceptionEvent (throwable, thread,
+ throw_loc, catch_loc,
+ throw_klass, instance);
+ Jdwp::notify (e);
+}
+
+static void JNICALL
+jdwpSingleStepCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread,
+ jmethodID method, jlocation location)
+{
+ jobject si =
+ gnu::classpath::jdwp::VMVirtualMachine::_stepping_threads->get (thread);
+ struct step_info *sinfo = reinterpret_cast<struct step_info *> (si);
+
+ if (sinfo == NULL)
+ {
+ // no step filter for this thread - simply report it
+ handle_single_step (env, NULL, thread, method, location);
+ }
+ else
+ {
+ // A step filter exists for this thread
+ using namespace gnu::classpath::jdwp;
+
+ _Jv_InterpFrame *frame
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+
+ switch (sinfo->depth)
+ {
+ case JdwpConstants$StepDepth::INTO:
+ /* This is the easy case. We ignore the method and
+ simply stop at either the next insn, or the next source
+ line. */
+ handle_single_step (env, sinfo, thread, method, location);
+ break;
+
+ case JdwpConstants$StepDepth::OVER:
+ /* This is also a pretty easy case. We just make sure that
+ the methods are the same and that we are at the same
+ stack depth, but we should also stop on the next
+ insn/line if the stack depth is LESS THAN it was when
+ we started stepping. */
+ if (method == sinfo->method)
+ {
+ // Still in the same method -- must be at same stack depth
+ // to avoid confusion with recursive methods.
+ if (frame->depth () == sinfo->stack_depth)
+ handle_single_step (env, sinfo, thread, method, location);
+ }
+ else if (frame->depth () < sinfo->stack_depth)
+ {
+ // The method in which we were stepping was popped off
+ // the stack. We simply need to stop at the next insn/line.
+ handle_single_step (env, sinfo, thread, method, location);
+ }
+ break;
+
+ case JdwpConstants$StepDepth::OUT:
+ // All we need to do is check the stack depth
+ if (sinfo->stack_depth > frame->depth ())
+ handle_single_step (env, sinfo, thread, method, location);
+ break;
+
+ default:
+ /* This should not happen. The JDWP back-end should have
+ validated the StepFilter. */
+ fprintf (stderr,
+ "libgcj: unknown step depth while single stepping\n");
+ return;
+ }
+ }
+}
+
+static void JNICALL
+jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
+ jthread thread)
+{
+ using namespace gnu::classpath::jdwp::event;
+
+ ThreadEndEvent *e = new ThreadEndEvent (thread);
+ gnu::classpath::jdwp::Jdwp::notify (e);
+}
+
+static void JNICALL
+jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
+ jthread thread)
+{
+ using namespace gnu::classpath::jdwp::event;
+
+ ThreadStartEvent *e = new ThreadStartEvent (thread);
+ gnu::classpath::jdwp::Jdwp::notify (e);
+}
+
+static void JNICALL
+jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
+{
+ using namespace gnu::classpath::jdwp::event;
+ gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
+}
+
+static void JNICALL
jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
jthread thread)
{
+ // The VM is now initialized, add our callbacks
+ jvmtiEventCallbacks callbacks;
+ DEFINE_CALLBACK (callbacks, Breakpoint);
+ DEFINE_CALLBACK (callbacks, ClassPrepare);
+ DEFINE_CALLBACK (callbacks, Exception);
+ DEFINE_CALLBACK (callbacks, SingleStep);
+ DEFINE_CALLBACK (callbacks, ThreadEnd);
+ DEFINE_CALLBACK (callbacks, ThreadStart);
+ DEFINE_CALLBACK (callbacks, VMDeath);
+ _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
+
+ // Enable callbacks
+ ENABLE_EVENT (BREAKPOINT, NULL);
+ ENABLE_EVENT (CLASS_PREPARE, NULL);
+ ENABLE_EVENT (EXCEPTION, NULL);
+ // SingleStep is enabled only when needed
+ ENABLE_EVENT (THREAD_END, NULL);
+ ENABLE_EVENT (THREAD_START, NULL);
+ ENABLE_EVENT (VM_DEATH, NULL);
+
// Send JDWP VMInit
using namespace gnu::classpath::jdwp::event;
- Thread *init_thread = reinterpret_cast<Thread *> (thread);
- gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (init_thread));
+ gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));
}