1 // natVMVirtualMachine.cc - native support for VMVirtualMachine
3 /* Copyright (C) 2006, 2007 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
13 #include <java-assert.h>
14 #include <java-interp.h>
18 #include <java/lang/Class.h>
19 #include <java/lang/ClassLoader.h>
20 #include <java/lang/Integer.h>
21 #include <java/lang/String.h>
22 #include <java/lang/StringBuilder.h>
23 #include <java/lang/Thread.h>
24 #include <java/nio/ByteBuffer.h>
25 #include <java/nio/ByteBufferImpl.h>
26 #include <java/util/ArrayList.h>
27 #include <java/util/Collection.h>
28 #include <java/util/Hashtable.h>
29 #include <java/util/Iterator.h>
31 #include <gnu/classpath/jdwp/Jdwp.h>
32 #include <gnu/classpath/jdwp/VMFrame.h>
33 #include <gnu/classpath/jdwp/VMMethod.h>
34 #include <gnu/classpath/jdwp/VMVirtualMachine.h>
35 #include <gnu/classpath/jdwp/event/BreakpointEvent.h>
36 #include <gnu/classpath/jdwp/event/ClassPrepareEvent.h>
37 #include <gnu/classpath/jdwp/event/EventManager.h>
38 #include <gnu/classpath/jdwp/event/EventRequest.h>
39 #include <gnu/classpath/jdwp/event/ThreadEndEvent.h>
40 #include <gnu/classpath/jdwp/event/ThreadStartEvent.h>
41 #include <gnu/classpath/jdwp/event/VmDeathEvent.h>
42 #include <gnu/classpath/jdwp/event/VmInitEvent.h>
43 #include <gnu/classpath/jdwp/event/filters/IEventFilter.h>
44 #include <gnu/classpath/jdwp/event/filters/LocationOnlyFilter.h>
45 #include <gnu/classpath/jdwp/event/filters/StepFilter.h>
46 #include <gnu/classpath/jdwp/exception/InvalidFrameException.h>
47 #include <gnu/classpath/jdwp/exception/InvalidLocationException.h>
48 #include <gnu/classpath/jdwp/exception/InvalidMethodException.h>
49 #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
50 #include <gnu/classpath/jdwp/id/ThreadId.h>
51 #include <gnu/classpath/jdwp/util/Location.h>
52 #include <gnu/classpath/jdwp/util/MethodResult.h>
53 #include <gnu/gcj/jvmti/Breakpoint.h>
54 #include <gnu/gcj/jvmti/BreakpointManager.h>
56 using namespace java::lang;
57 using namespace gnu::classpath::jdwp::event;
58 using namespace gnu::classpath::jdwp::util;
60 // Stepping information
63 jint size; // See gnu.classpath.jdwp.JdwpConstants.StepSize
64 jint depth; // See gnu.classpath.jdwp.JdwpConstants.StepDepth
65 int stack_depth; // stack depth at start of stepping
66 jmethodID method; // method in which we are stepping
69 // Forward declarations
70 static Location *get_request_location (EventRequest *);
71 static gnu::classpath::jdwp::event::filters::StepFilter *
72 get_request_step_filter (EventRequest *);
73 static void JNICALL jdwpBreakpointCB (jvmtiEnv *, JNIEnv *, jthread,
74 jmethodID, jlocation);
75 static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
76 static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
77 static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread);
78 static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *);
79 static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread);
80 static void throw_jvmti_error (jvmtiError);
82 #define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB
83 #define DISABLE_EVENT(Event,Thread) \
84 _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_DISABLE, \
85 JVMTI_EVENT_ ## Event, Thread)
86 #define ENABLE_EVENT(Event,Thread) \
87 _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE, \
88 JVMTI_EVENT_ ## Event, Thread)
90 static jvmtiEnv *_jdwp_jvmtiEnv;
93 _Jv_GetJDWP_JVMTIEnv (void)
95 return _jdwp_jvmtiEnv;
99 gnu::classpath::jdwp::VMVirtualMachine::initialize ()
101 _jdwp_suspend_counts = new ::java::util::Hashtable ();
102 _stepping_threads = new ::java::util::Hashtable ();
104 JavaVM *vm = _Jv_GetJavaVM ();
105 vm->GetEnv (reinterpret_cast<void **> (&_jdwp_jvmtiEnv), JVMTI_VERSION_1_0);
107 // Wait for VM_INIT to do more initialization
108 jvmtiEventCallbacks callbacks;
109 DEFINE_CALLBACK (callbacks, VMInit);
110 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
111 ENABLE_EVENT (VM_INIT, NULL);
115 gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
120 JvSynchronize dummy (_jdwp_suspend_counts);
121 count = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
124 // New -- suspend thread
129 // Thread already suspended
130 value = count->intValue ();
133 count = Integer::valueOf (++value);
134 _jdwp_suspend_counts->put (thread, count);
139 // Suspend the thread
140 jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread);
141 if (err != JVMTI_ERROR_NONE)
143 using namespace gnu::gcj::runtime;
144 using namespace gnu::classpath::jdwp::exception;
146 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
147 String *txt = JvNewStringLatin1 ("could not suspend thread: ");
148 StringBuilder *msg = new StringBuilder (txt);
149 msg->append (JvNewStringLatin1 (reason));
150 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
151 throw new JdwpInternalErrorException (msg->toString ());
157 gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread)
161 JvSynchronize dummy (_jdwp_suspend_counts);
163 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
166 // Thread not suspended: ThreadReference.Resume says to ignore it.
171 // Decrement suspend count
172 value = count->intValue () - 1;
177 // Thread will be resumed, remove from table
178 _jdwp_suspend_counts->remove (thread);
182 // Thread stays suspended: record new suspend count
183 count = Integer::valueOf (value);
184 _jdwp_suspend_counts->put (thread, count);
190 jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread);
191 if (err != JVMTI_ERROR_NONE)
193 using namespace gnu::gcj::runtime;
194 using namespace gnu::classpath::jdwp::exception;
196 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
197 String *txt = JvNewStringLatin1 ("could not resume thread: ");
198 StringBuilder *msg = new StringBuilder (txt);
199 msg->append (JvNewStringLatin1 (reason));
200 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
201 throw new JdwpInternalErrorException (msg->toString ());
207 gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread)
209 jint suspensions = 0;
211 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
213 suspensions = count->intValue ();
218 gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
220 switch (request->getEventKind ())
222 case EventRequest::EVENT_SINGLE_STEP:
225 filters::StepFilter *filter = get_request_step_filter (request);
228 // No filter specified: report every step in every
234 // Add stepping information to list of stepping threads
235 thread = filter->getThread ()->getThread ();
236 _Jv_InterpFrame *frame
237 = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
238 struct step_info *sinfo
239 = (struct step_info *) JvAllocBytes (sizeof (struct step_info));
240 sinfo->size = filter->getSize ();
241 sinfo->depth = filter->getDepth ();
242 sinfo->stack_depth = frame->depth ();
243 sinfo->method = frame->self->get_method ();
244 _stepping_threads->put (thread, (jobject) sinfo);
247 ENABLE_EVENT (SINGLE_STEP, thread);
251 case EventRequest::EVENT_BREAKPOINT:
253 using namespace ::gnu::gcj::jvmti;
254 Location *loc = get_request_location (request);
257 using namespace gnu::classpath::jdwp::exception;
258 throw new InvalidLocationException ();
261 jlong method = loc->getMethod ()->getId ();
262 jlocation index = loc->getIndex ();
263 Breakpoint *bp = BreakpointManager::getBreakpoint (method, index);
266 // Breakpoint not in interpreter yet
267 bp = BreakpointManager::newBreakpoint (method, index);
271 // Ignore the duplicate
276 case EventRequest::EVENT_FRAME_POP:
279 case EventRequest::EVENT_EXCEPTION:
282 case EventRequest::EVENT_USER_DEFINED:
285 case EventRequest::EVENT_THREAD_START:
288 case EventRequest::EVENT_THREAD_END:
291 case EventRequest::EVENT_CLASS_PREPARE:
294 case EventRequest::EVENT_CLASS_LOAD:
297 case EventRequest::EVENT_CLASS_UNLOAD:
300 case EventRequest::EVENT_FIELD_ACCESS:
303 case EventRequest::EVENT_FIELD_MODIFY:
306 case EventRequest::EVENT_METHOD_ENTRY:
309 case EventRequest::EVENT_METHOD_EXIT:
312 case EventRequest::EVENT_VM_INIT:
315 case EventRequest::EVENT_VM_DEATH:
321 gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
323 switch (request->getEventKind ())
325 case EventRequest::EVENT_SINGLE_STEP:
328 filters::StepFilter *filter = get_request_step_filter (request);
333 thread = filter->getThread ()->getThread ();
334 _stepping_threads->remove (thread);
337 DISABLE_EVENT (SINGLE_STEP, thread);
341 case EventRequest::EVENT_BREAKPOINT:
343 using namespace gnu::gcj::jvmti;
344 ::java::util::Collection *breakpoints;
345 EventManager *em = EventManager::getDefault ();
346 breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
348 // Check for duplicates
350 Location *the_location = get_request_location (request);
352 // This should not be possible: we REQUIRE a Location
353 // to install a breakpoint
354 JvAssert (the_location != NULL);
356 ::java::util::Iterator *iter = breakpoints->iterator ();
357 while (iter->hasNext ())
360 = reinterpret_cast<EventRequest *> (iter->next ());
361 Location *loc = get_request_location (er);
362 JvAssert (loc != NULL);
363 if (loc->equals (the_location) && ++matches == 2)
365 // Short-circuit: already more than one breakpoint
372 using namespace gnu::classpath::jdwp::exception;
374 = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
375 throw new JdwpInternalErrorException (msg);
378 jlong methodId = the_location->getMethod ()->getId ();
379 BreakpointManager::deleteBreakpoint (methodId,
380 the_location->getIndex ());
384 case EventRequest::EVENT_FRAME_POP:
387 case EventRequest::EVENT_EXCEPTION:
390 case EventRequest::EVENT_USER_DEFINED:
393 case EventRequest::EVENT_THREAD_START:
396 case EventRequest::EVENT_THREAD_END:
399 case EventRequest::EVENT_CLASS_PREPARE:
402 case EventRequest::EVENT_CLASS_LOAD:
405 case EventRequest::EVENT_CLASS_UNLOAD:
408 case EventRequest::EVENT_FIELD_ACCESS:
411 case EventRequest::EVENT_FIELD_MODIFY:
414 case EventRequest::EVENT_METHOD_ENTRY:
417 case EventRequest::EVENT_METHOD_EXIT:
420 case EventRequest::EVENT_VM_INIT:
423 case EventRequest::EVENT_VM_DEATH:
429 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
434 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void)
439 java::util::Iterator *
440 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
446 gnu::classpath::jdwp::VMVirtualMachine::
447 getClassStatus (MAYBE_UNUSED jclass klass)
452 JArray<gnu::classpath::jdwp::VMMethod *> *
453 gnu::classpath::jdwp::VMVirtualMachine::
454 getAllClassMethods (jclass klass)
458 jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
459 if (err != JVMTI_ERROR_NONE)
460 throw_jvmti_error (err);
462 JArray<VMMethod *> *result
463 = (JArray<VMMethod *> *) JvNewObjectArray (count,
464 &VMMethod::class$, NULL);
465 VMMethod **rmeth = elements (result);
466 for (int i = 0; i < count; ++i)
468 jlong id = reinterpret_cast<jlong> (methods[i]);
469 rmeth[i] = getClassMethod (klass, id);
472 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
476 gnu::classpath::jdwp::VMMethod *
477 gnu::classpath::jdwp::VMVirtualMachine::
478 getClassMethod (jclass klass, jlong id)
480 jmethodID method = reinterpret_cast<jmethodID> (id);
481 _Jv_MethodBase *bmeth = _Jv_FindInterpreterMethod (klass, method);
483 return new gnu::classpath::jdwp::VMMethod (klass, id);
485 throw new gnu::classpath::jdwp::exception::InvalidMethodException (id);
488 java::util::ArrayList *
489 gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, jint start,
492 jint frame_count = getFrameCount (thread);
493 ::java::util::ArrayList *frame_list;
495 // Calculate the max number of frames to be returned.
496 jint num_frames = frame_count - start;
498 // Check if num_frames is valid.
502 // Check if there are more than length frames left after start.
503 // If length ios -1 return all remaining frames.
504 if (length != -1 && num_frames > length)
507 frame_list = new ::java::util::ArrayList (num_frames);
509 _Jv_Frame *vm_frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
511 // Take start frames off the top of the stack
512 while (vm_frame != NULL && start > 0)
515 vm_frame = vm_frame->next;
518 // Use as a counter for the number of frames returned.
521 while (vm_frame != NULL && (num_frames < length || length == -1))
523 jlong frameId = reinterpret_cast<jlong> (vm_frame);
525 VMFrame *frame = getFrame (thread, frameId);
526 frame_list->add (frame);
527 vm_frame = vm_frame->next;
534 gnu::classpath::jdwp::VMFrame *
535 gnu::classpath::jdwp::VMVirtualMachine::
536 getFrame (Thread *thread, jlong frameID)
538 using namespace gnu::classpath::jdwp::exception;
540 _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
542 _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID);
544 // We need to find the stack depth of the frame, so search through the call
545 // stack to find it. This also checks for a valid frameID.
546 while (vm_frame != frame)
548 vm_frame = vm_frame->next;
550 if (vm_frame == NULL)
551 throw new InvalidFrameException (frameID);
554 Location *loc = NULL;
560 // Get the info for the frame of interest
561 jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
563 if (jerr != JVMTI_ERROR_NONE)
564 throw_jvmti_error (jerr);
566 jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
568 if (jerr != JVMTI_ERROR_NONE)
569 throw_jvmti_error (jerr);
572 = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
574 if (info.location == -1)
575 loc = new Location (meth, 0);
577 loc = new Location (meth, info.location);
579 return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc);
583 gnu::classpath::jdwp::VMVirtualMachine::
584 getFrameCount (Thread *thread)
588 jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
590 if (jerr != JVMTI_ERROR_NONE)
591 throw_jvmti_error (jerr);
597 gnu::classpath::jdwp::VMVirtualMachine::
598 getThreadStatus (MAYBE_UNUSED Thread *thread)
603 java::util::ArrayList *
604 gnu::classpath::jdwp::VMVirtualMachine::
605 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
611 gnu::classpath::jdwp::VMVirtualMachine::
612 executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread,
613 MAYBE_UNUSED jclass clazz, MAYBE_UNUSED reflect::Method *method,
614 MAYBE_UNUSED jobjectArray values,
615 MAYBE_UNUSED jboolean nonVirtual)
621 gnu::classpath::jdwp::VMVirtualMachine::
622 getSourceFile (MAYBE_UNUSED jclass clazz)
627 static gnu::classpath::jdwp::event::filters::StepFilter *
628 get_request_step_filter (EventRequest *request)
630 ::java::util::Collection *filters = request->getFilters ();
631 ::java::util::Iterator *iter = filters->iterator ();
632 filters::StepFilter *filter = NULL;
633 while (iter->hasNext ())
635 using namespace gnu::classpath::jdwp::event::filters;
636 IEventFilter *next = (IEventFilter *) iter->next ();
637 if (next->getClass () == &StepFilter::class$)
639 filter = reinterpret_cast<StepFilter *> (next);
648 get_request_location (EventRequest *request)
650 Location *loc = NULL;
651 ::java::util::Collection *filters = request->getFilters ();
652 ::java::util::Iterator *iter = filters->iterator ();
653 while (iter->hasNext ())
655 using namespace gnu::classpath::jdwp::event::filters;
656 IEventFilter *filter = (IEventFilter *) iter->next ();
657 if (filter->getClass () == &LocationOnlyFilter::class$)
659 LocationOnlyFilter *lof
660 = reinterpret_cast<LocationOnlyFilter *> (filter);
661 loc = lof->getLocation ();
669 throw_jvmti_error (jvmtiError err)
673 if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
675 msg = JvNewStringLatin1 (error);
676 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
679 msg = JvNewStringLatin1 ("out of memory");
681 using namespace gnu::classpath::jdwp::exception;
682 throw new JdwpInternalErrorException (msg);
686 jdwpBreakpointCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
687 jthread thread, jmethodID method, jlocation location)
691 err = env->GetMethodDeclaringClass (method, &klass);
692 JvAssert (err == JVMTI_ERROR_NONE);
694 using namespace gnu::classpath::jdwp;
696 jlong methodId = reinterpret_cast<jlong> (method);
697 VMMethod *meth = VMVirtualMachine::getClassMethod (klass, methodId);
698 Location *loc = new Location (meth, location);
699 JvAssert (thread->frame.frame_type == frame_interpreter);
700 _Jv_InterpFrame *iframe
701 = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
702 jobject instance = iframe->get_this_ptr ();
703 event::BreakpointEvent *event
704 = new event::BreakpointEvent (thread, loc, instance);
705 Jdwp::notify (event);
709 jdwpClassPrepareCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
710 jthread thread, jclass klass)
712 using namespace gnu::classpath::jdwp;
715 jvmtiError err = env->GetClassStatus (klass, &flags);
716 if (err != JVMTI_ERROR_NONE)
717 throw_jvmti_error (err);
719 using namespace gnu::classpath::jdwp::event;
721 if (flags & JVMTI_CLASS_STATUS_VERIFIED)
722 status |= ClassPrepareEvent::STATUS_VERIFIED;
723 if (flags & JVMTI_CLASS_STATUS_PREPARED)
724 status |= ClassPrepareEvent::STATUS_PREPARED;
725 if (flags & JVMTI_CLASS_STATUS_ERROR)
726 status |= ClassPrepareEvent::STATUS_ERROR;
727 if (flags & JVMTI_CLASS_STATUS_INITIALIZED)
728 status |= ClassPrepareEvent::STATUS_INITIALIZED;
730 event::ClassPrepareEvent *event
731 = new event::ClassPrepareEvent (thread, klass, status);
732 Jdwp::notify (event);
736 jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
739 using namespace gnu::classpath::jdwp::event;
741 ThreadEndEvent *e = new ThreadEndEvent (thread);
742 gnu::classpath::jdwp::Jdwp::notify (e);
746 jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
749 using namespace gnu::classpath::jdwp::event;
751 ThreadStartEvent *e = new ThreadStartEvent (thread);
752 gnu::classpath::jdwp::Jdwp::notify (e);
756 jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
758 using namespace gnu::classpath::jdwp::event;
759 gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
763 jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
766 // The VM is now initialized, add our callbacks
767 jvmtiEventCallbacks callbacks;
768 DEFINE_CALLBACK (callbacks, Breakpoint);
769 DEFINE_CALLBACK (callbacks, ClassPrepare);
770 DEFINE_CALLBACK (callbacks, ThreadEnd);
771 DEFINE_CALLBACK (callbacks, ThreadStart);
772 DEFINE_CALLBACK (callbacks, VMDeath);
773 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
776 ENABLE_EVENT (BREAKPOINT, NULL);
777 ENABLE_EVENT (CLASS_PREPARE, NULL);
778 ENABLE_EVENT (THREAD_END, NULL);
779 ENABLE_EVENT (THREAD_START, NULL);
780 ENABLE_EVENT (VM_DEATH, NULL);
783 using namespace gnu::classpath::jdwp::event;
784 gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));