From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc | 1159 +++++++++++++++++++++ 1 file changed, 1159 insertions(+) create mode 100644 libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc (limited to 'libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc') diff --git a/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc new file mode 100644 index 000000000..7bd300b79 --- /dev/null +++ b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc @@ -0,0 +1,1159 @@ +// natVMVirtualMachine.cc - native support for VMVirtualMachine + +/* Copyright (C) 2006, 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 (); + 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; + DEFINE_CALLBACK (callbacks, VMInit); + _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks)); + ENABLE_EVENT (VM_INIT, NULL); +} + +void +gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread) +{ + jint value; + Integer *count; + { + JvSynchronize dummy (_jdwp_suspend_counts); + count = reinterpret_cast (_jdwp_suspend_counts->get (thread)); + if (count == NULL) + { + // New -- suspend thread + value = 0; + } + else + { + // Thread already suspended + value = count->intValue (); + } + + count = Integer::valueOf (++value); + _jdwp_suspend_counts->put (thread, count); + } + + if (value == 1) + { + // Suspend the thread + jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread); + if (err != JVMTI_ERROR_NONE) + { + using namespace gnu::gcj::runtime; + using namespace gnu::classpath::jdwp::exception; + char *reason; + _jdwp_jvmtiEnv->GetErrorName (err, &reason); + String *txt = JvNewStringLatin1 ("could not suspend thread: "); + StringBuilder *msg = new StringBuilder (txt); + msg->append (JvNewStringLatin1 (reason)); + _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason); + throw new JdwpInternalErrorException (msg->toString ()); + } + } +} + +void +gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread) +{ + jint value; + { + JvSynchronize dummy (_jdwp_suspend_counts); + Integer *count + = reinterpret_cast (_jdwp_suspend_counts->get (thread)); + if (count == NULL) + { + // Thread not suspended: ThreadReference.Resume says to ignore it. + return; + } + else + { + // Decrement suspend count + value = count->intValue () - 1; + } + + if (value == 0) + { + // Thread will be resumed, remove from table + _jdwp_suspend_counts->remove (thread); + } + else + { + // Thread stays suspended: record new suspend count + count = Integer::valueOf (value); + _jdwp_suspend_counts->put (thread, count); + } + } + + if (value == 0) + { + jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread); + if (err != JVMTI_ERROR_NONE) + { + using namespace gnu::gcj::runtime; + using namespace gnu::classpath::jdwp::exception; + char *reason; + _jdwp_jvmtiEnv->GetErrorName (err, &reason); + String *txt = JvNewStringLatin1 ("could not resume thread: "); + StringBuilder *msg = new StringBuilder (txt); + msg->append (JvNewStringLatin1 (reason)); + _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason); + throw new JdwpInternalErrorException (msg->toString ()); + } + } +} + +jint +gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread) +{ + jint suspensions = 0; + Integer *count + = reinterpret_cast (_jdwp_suspend_counts->get (thread)); + if (count != NULL) + suspensions = count->intValue (); + return suspensions; +} + +void +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: + break; + + case EventRequest::EVENT_EXCEPTION: + break; + + case EventRequest::EVENT_USER_DEFINED: + break; + + case EventRequest::EVENT_THREAD_START: + break; + + case EventRequest::EVENT_THREAD_END: + break; + + case EventRequest::EVENT_CLASS_PREPARE: + break; + + case EventRequest::EVENT_CLASS_LOAD: + break; + + case EventRequest::EVENT_CLASS_UNLOAD: + break; + + case EventRequest::EVENT_FIELD_ACCESS: + break; + + case EventRequest::EVENT_FIELD_MODIFY: + break; + + case EventRequest::EVENT_METHOD_ENTRY: + break; + + case EventRequest::EVENT_METHOD_EXIT: + break; + + case EventRequest::EVENT_VM_INIT: + break; + + case EventRequest::EVENT_VM_DEATH: + break; + } +} + +void +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 (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: + break; + + case EventRequest::EVENT_EXCEPTION: + break; + + case EventRequest::EVENT_USER_DEFINED: + break; + + case EventRequest::EVENT_THREAD_START: + break; + + case EventRequest::EVENT_THREAD_END: + break; + + case EventRequest::EVENT_CLASS_PREPARE: + break; + + case EventRequest::EVENT_CLASS_LOAD: + break; + + case EventRequest::EVENT_CLASS_UNLOAD: + break; + + case EventRequest::EVENT_FIELD_ACCESS: + break; + + case EventRequest::EVENT_FIELD_MODIFY: + break; + + case EventRequest::EVENT_METHOD_ENTRY: + break; + + case EventRequest::EVENT_METHOD_EXIT: + break; + + case EventRequest::EVENT_VM_INIT: + break; + + case EventRequest::EVENT_VM_DEATH: + break; + } +} + +void +gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind) +{ +} + +java::util::Collection * +gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void) +{ + using namespace ::java::util; + return (Collection *) new ArrayList (); +} + +jint +gnu::classpath::jdwp::VMVirtualMachine:: +getClassStatus (jclass klass) +{ + 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::VMVirtualMachine:: +getAllClassMethods (jclass klass) +{ + jint count; + jmethodID *methods; + jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods); + if (err != JVMTI_ERROR_NONE) + throw_jvmti_error (err); + + JArray *result + = (JArray *) JvNewObjectArray (count, + &VMMethod::class$, NULL); + VMMethod **rmeth = elements (result); + for (int i = 0; i < count; ++i) + { + jlong id = reinterpret_cast (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) +{ + 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 (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 (meth_id)); + } + + throw new exception::InvalidMethodException (id); +} + +java::util::ArrayList * +gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, jint start, + jint length) +{ + 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 (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, jlong frameID) +{ + 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 (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 (vm_frame), loc, + this_obj); +} + +jint +gnu::classpath::jdwp::VMVirtualMachine:: +getFrameCount (Thread *thread) +{ + 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) +{ + 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 (MAYBE_UNUSED ClassLoader *cl) +{ + return new ::java::util::ArrayList (); +} + +MethodResult * +gnu::classpath::jdwp::VMVirtualMachine:: +executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread, + MAYBE_UNUSED jclass clazz, MAYBE_UNUSED VMMethod *method, + MAYBE_UNUSED JArray *values, + MAYBE_UNUSED jint options) +{ + return NULL; +} + +jstring +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 *types, + MAYBE_UNUSED JArray *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 (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 (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 (method)); + Location *loc = new Location (vmmethod, location); + _Jv_InterpFrame *iframe + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + JvAssert (iframe->frame_type == frame_interpreter); + 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 (method); + VMMethod *meth = VMVirtualMachine::getClassMethod (klass, methodId); + Location *loc = new Location (meth, location); + _Jv_InterpFrame *iframe + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + JvAssert (iframe->frame_type == frame_interpreter); + jobject instance = iframe->get_this_ptr (); + BreakpointEvent *event = new BreakpointEvent (thread, loc, instance); + + VMVirtualMachine::_event_list->add (event); + JArray *events + = ((JArray *) + 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 (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 (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 (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 (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; + gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread)); +} -- cgit v1.2.3