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-interp.h>
20 #include <java/lang/Class.h>
21 #include <java/lang/ClassLoader.h>
22 #include <java/lang/Integer.h>
23 #include <java/lang/String.h>
24 #include <java/lang/StringBuilder.h>
25 #include <java/lang/Thread.h>
26 #include <java/nio/ByteBuffer.h>
27 #include <java/nio/ByteBufferImpl.h>
28 #include <java/util/ArrayList.h>
29 #include <java/util/Collection.h>
30 #include <java/util/Hashtable.h>
31 #include <java/util/Iterator.h>
33 #include <gnu/classpath/jdwp/Jdwp.h>
34 #include <gnu/classpath/jdwp/VMFrame.h>
35 #include <gnu/classpath/jdwp/VMMethod.h>
36 #include <gnu/classpath/jdwp/VMVirtualMachine.h>
37 #include <gnu/classpath/jdwp/event/ClassPrepareEvent.h>
38 #include <gnu/classpath/jdwp/event/EventManager.h>
39 #include <gnu/classpath/jdwp/event/EventRequest.h>
40 #include <gnu/classpath/jdwp/event/ThreadEndEvent.h>
41 #include <gnu/classpath/jdwp/event/ThreadStartEvent.h>
42 #include <gnu/classpath/jdwp/event/VmDeathEvent.h>
43 #include <gnu/classpath/jdwp/event/VmInitEvent.h>
44 #include <gnu/classpath/jdwp/event/filters/IEventFilter.h>
45 #include <gnu/classpath/jdwp/event/filters/LocationOnlyFilter.h>
46 #include <gnu/classpath/jdwp/event/filters/StepFilter.h>
47 #include <gnu/classpath/jdwp/exception/InvalidFrameException.h>
48 #include <gnu/classpath/jdwp/exception/InvalidLocationException.h>
49 #include <gnu/classpath/jdwp/exception/InvalidMethodException.h>
50 #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
51 #include <gnu/classpath/jdwp/id/ThreadId.h>
52 #include <gnu/classpath/jdwp/util/Location.h>
53 #include <gnu/classpath/jdwp/util/MethodResult.h>
54 #include <gnu/gcj/jvmti/Breakpoint.h>
55 #include <gnu/gcj/jvmti/BreakpointManager.h>
57 using namespace java::lang;
58 using namespace gnu::classpath::jdwp::event;
59 using namespace gnu::classpath::jdwp::util;
61 // Stepping information
64 jint size; // See gnu.classpath.jdwp.JdwpConstants.StepSize
65 jint depth; // See gnu.classpath.jdwp.JdwpConstants.StepDepth
66 int stack_depth; // stack depth at start of stepping
67 jmethodID method; // method in which we are stepping
70 // Forward declarations
71 static Location *get_request_location (EventRequest *);
72 static gnu::classpath::jdwp::event::filters::StepFilter *
73 get_request_step_filter (EventRequest *);
74 static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
75 static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
76 static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread);
77 static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *);
78 static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread);
79 static void throw_jvmti_error (jvmtiError);
81 #define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB
82 #define DISABLE_EVENT(Event,Thread) \
83 _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_DISABLE, \
84 JVMTI_EVENT_ ## Event, Thread)
85 #define ENABLE_EVENT(Event,Thread) \
86 _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE, \
87 JVMTI_EVENT_ ## Event, Thread)
89 static jvmtiEnv *_jdwp_jvmtiEnv;
92 _Jv_GetJDWP_JVMTIEnv (void)
94 return _jdwp_jvmtiEnv;
98 gnu::classpath::jdwp::VMVirtualMachine::initialize ()
100 _jdwp_suspend_counts = new ::java::util::Hashtable ();
101 _stepping_threads = new ::java::util::Hashtable ();
103 JavaVM *vm = _Jv_GetJavaVM ();
104 vm->GetEnv (reinterpret_cast<void **> (&_jdwp_jvmtiEnv), JVMTI_VERSION_1_0);
106 // Wait for VM_INIT to do more initialization
107 jvmtiEventCallbacks callbacks;
108 DEFINE_CALLBACK (callbacks, VMInit);
109 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
110 ENABLE_EVENT (VM_INIT, NULL);
114 gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
119 JvSynchronize dummy (_jdwp_suspend_counts);
120 count = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
123 // New -- suspend thread
128 // Thread already suspended
129 value = count->intValue ();
132 count = Integer::valueOf (++value);
133 _jdwp_suspend_counts->put (thread, count);
138 // Suspend the thread
139 jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread);
140 if (err != JVMTI_ERROR_NONE)
142 using namespace gnu::gcj::runtime;
143 using namespace gnu::classpath::jdwp::exception;
145 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
146 String *txt = JvNewStringLatin1 ("could not suspend thread: ");
147 StringBuilder *msg = new StringBuilder (txt);
148 msg->append (JvNewStringLatin1 (reason));
149 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
150 throw new JdwpInternalErrorException (msg->toString ());
156 gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread)
160 JvSynchronize dummy (_jdwp_suspend_counts);
162 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
165 // Thread not suspended: ThreadReference.Resume says to ignore it.
170 // Decrement suspend count
171 value = count->intValue () - 1;
176 // Thread will be resumed, remove from table
177 _jdwp_suspend_counts->remove (thread);
181 // Thread stays suspended: record new suspend count
182 count = Integer::valueOf (value);
183 _jdwp_suspend_counts->put (thread, count);
189 jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread);
190 if (err != JVMTI_ERROR_NONE)
192 using namespace gnu::gcj::runtime;
193 using namespace gnu::classpath::jdwp::exception;
195 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
196 String *txt = JvNewStringLatin1 ("could not resume thread: ");
197 StringBuilder *msg = new StringBuilder (txt);
198 msg->append (JvNewStringLatin1 (reason));
199 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
200 throw new JdwpInternalErrorException (msg->toString ());
206 gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread)
208 jint suspensions = 0;
210 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
212 suspensions = count->intValue ();
217 gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
219 switch (request->getEventKind ())
221 case EventRequest::EVENT_SINGLE_STEP:
224 filters::StepFilter *filter = get_request_step_filter (request);
227 // No filter specified: report every step in every
233 // Add stepping information to list of stepping threads
234 thread = filter->getThread ()->getThread ();
235 _Jv_InterpFrame *frame
236 = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
237 struct step_info *sinfo
238 = (struct step_info *) JvAllocBytes (sizeof (struct step_info));
239 sinfo->size = filter->getSize ();
240 sinfo->depth = filter->getDepth ();
241 sinfo->stack_depth = frame->depth ();
242 sinfo->method = frame->self->get_method ();
243 _stepping_threads->put (thread, (jobject) sinfo);
246 ENABLE_EVENT (SINGLE_STEP, thread);
250 case EventRequest::EVENT_BREAKPOINT:
252 using namespace ::gnu::gcj::jvmti;
253 Location *loc = get_request_location (request);
256 using namespace gnu::classpath::jdwp::exception;
257 throw new InvalidLocationException ();
260 jlong method = loc->getMethod ()->getId ();
261 jlocation index = loc->getIndex ();
262 Breakpoint *bp = BreakpointManager::getBreakpoint (method, index);
265 // Breakpoint not in interpreter yet
266 bp = BreakpointManager::newBreakpoint (method, index);
270 // Ignore the duplicate
275 case EventRequest::EVENT_FRAME_POP:
278 case EventRequest::EVENT_EXCEPTION:
281 case EventRequest::EVENT_USER_DEFINED:
284 case EventRequest::EVENT_THREAD_START:
287 case EventRequest::EVENT_THREAD_END:
290 case EventRequest::EVENT_CLASS_PREPARE:
293 case EventRequest::EVENT_CLASS_LOAD:
296 case EventRequest::EVENT_CLASS_UNLOAD:
299 case EventRequest::EVENT_FIELD_ACCESS:
302 case EventRequest::EVENT_FIELD_MODIFY:
305 case EventRequest::EVENT_METHOD_ENTRY:
308 case EventRequest::EVENT_METHOD_EXIT:
311 case EventRequest::EVENT_VM_INIT:
314 case EventRequest::EVENT_VM_DEATH:
320 gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
322 switch (request->getEventKind ())
324 case EventRequest::EVENT_SINGLE_STEP:
327 filters::StepFilter *filter = get_request_step_filter (request);
332 thread = filter->getThread ()->getThread ();
333 _stepping_threads->remove (thread);
336 DISABLE_EVENT (SINGLE_STEP, thread);
340 case EventRequest::EVENT_BREAKPOINT:
342 using namespace gnu::gcj::jvmti;
343 ::java::util::Collection *breakpoints;
344 EventManager *em = EventManager::getDefault ();
345 breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
347 // Check for duplicates
349 Location *the_location = get_request_location (request);
351 // This should not be possible: we REQUIRE a Location
352 // to install a breakpoint
353 JvAssert (the_location != NULL);
355 ::java::util::Iterator *iter = breakpoints->iterator ();
356 while (iter->hasNext ())
359 = reinterpret_cast<EventRequest *> (iter->next ());
360 Location *loc = get_request_location (er);
361 JvAssert (loc != NULL);
362 if (loc->equals (the_location) && ++matches == 2)
364 // Short-circuit: already more than one breakpoint
371 using namespace gnu::classpath::jdwp::exception;
373 = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
374 throw new JdwpInternalErrorException (msg);
377 jlong methodId = the_location->getMethod ()->getId ();
378 BreakpointManager::deleteBreakpoint (methodId,
379 the_location->getIndex ());
383 case EventRequest::EVENT_FRAME_POP:
386 case EventRequest::EVENT_EXCEPTION:
389 case EventRequest::EVENT_USER_DEFINED:
392 case EventRequest::EVENT_THREAD_START:
395 case EventRequest::EVENT_THREAD_END:
398 case EventRequest::EVENT_CLASS_PREPARE:
401 case EventRequest::EVENT_CLASS_LOAD:
404 case EventRequest::EVENT_CLASS_UNLOAD:
407 case EventRequest::EVENT_FIELD_ACCESS:
410 case EventRequest::EVENT_FIELD_MODIFY:
413 case EventRequest::EVENT_METHOD_ENTRY:
416 case EventRequest::EVENT_METHOD_EXIT:
419 case EventRequest::EVENT_VM_INIT:
422 case EventRequest::EVENT_VM_DEATH:
428 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
433 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void)
438 java::util::Iterator *
439 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
445 gnu::classpath::jdwp::VMVirtualMachine::
446 getClassStatus (MAYBE_UNUSED jclass klass)
451 JArray<gnu::classpath::jdwp::VMMethod *> *
452 gnu::classpath::jdwp::VMVirtualMachine::
453 getAllClassMethods (jclass klass)
457 jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
458 if (err != JVMTI_ERROR_NONE)
459 throw_jvmti_error (err);
461 JArray<VMMethod *> *result
462 = (JArray<VMMethod *> *) JvNewObjectArray (count,
463 &VMMethod::class$, NULL);
464 VMMethod **rmeth = elements (result);
465 for (int i = 0; i < count; ++i)
467 jlong id = reinterpret_cast<jlong> (methods[i]);
468 rmeth[i] = getClassMethod (klass, id);
471 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
475 gnu::classpath::jdwp::VMMethod *
476 gnu::classpath::jdwp::VMVirtualMachine::
477 getClassMethod (jclass klass, jlong id)
479 jmethodID method = reinterpret_cast<jmethodID> (id);
480 _Jv_MethodBase *bmeth = _Jv_FindInterpreterMethod (klass, method);
482 return new gnu::classpath::jdwp::VMMethod (klass, id);
484 throw new gnu::classpath::jdwp::exception::InvalidMethodException (id);
487 java::util::ArrayList *
488 gnu::classpath::jdwp::VMVirtualMachine::getFrames (MAYBE_UNUSED Thread *thread,
489 MAYBE_UNUSED jint start,
490 MAYBE_UNUSED jint length)
495 gnu::classpath::jdwp::VMFrame *
496 gnu::classpath::jdwp::VMVirtualMachine::
497 getFrame (Thread *thread, jlong frameID)
499 using namespace gnu::classpath::jdwp::exception;
501 _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
503 _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID);
505 // We need to find the stack depth of the frame, so search through the call
506 // stack to find it. This also checks for a valid frameID.
507 while (vm_frame != frame)
509 vm_frame = vm_frame->next;
511 if (vm_frame == NULL)
512 throw new InvalidFrameException (frameID);
515 Location *loc = NULL;
521 // Get the info for the frame of interest
522 jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
524 if (jerr != JVMTI_ERROR_NONE)
525 throw_jvmti_error (jerr);
527 jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
529 if (jerr != JVMTI_ERROR_NONE)
530 throw_jvmti_error (jerr);
533 = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
535 if (info.location == -1)
536 loc = new Location (meth, 0);
538 loc = new Location (meth, info.location);
540 return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc);
544 gnu::classpath::jdwp::VMVirtualMachine::
545 getFrameCount (Thread *thread)
549 jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
551 if (jerr != JVMTI_ERROR_NONE)
552 throw_jvmti_error (jerr);
558 gnu::classpath::jdwp::VMVirtualMachine::
559 getThreadStatus (MAYBE_UNUSED Thread *thread)
564 java::util::ArrayList *
565 gnu::classpath::jdwp::VMVirtualMachine::
566 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
572 gnu::classpath::jdwp::VMVirtualMachine::
573 executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread,
574 MAYBE_UNUSED jclass clazz, MAYBE_UNUSED reflect::Method *method,
575 MAYBE_UNUSED jobjectArray values,
576 MAYBE_UNUSED jboolean nonVirtual)
582 gnu::classpath::jdwp::VMVirtualMachine::
583 getSourceFile (MAYBE_UNUSED jclass clazz)
588 static gnu::classpath::jdwp::event::filters::StepFilter *
589 get_request_step_filter (EventRequest *request)
591 ::java::util::Collection *filters = request->getFilters ();
592 ::java::util::Iterator *iter = filters->iterator ();
593 filters::StepFilter *filter = NULL;
594 while (iter->hasNext ())
596 using namespace gnu::classpath::jdwp::event::filters;
597 IEventFilter *next = (IEventFilter *) iter->next ();
598 if (next->getClass () == &StepFilter::class$)
600 filter = reinterpret_cast<StepFilter *> (next);
609 get_request_location (EventRequest *request)
611 Location *loc = NULL;
612 ::java::util::Collection *filters = request->getFilters ();
613 ::java::util::Iterator *iter = filters->iterator ();
614 while (iter->hasNext ())
616 using namespace gnu::classpath::jdwp::event::filters;
617 IEventFilter *filter = (IEventFilter *) iter->next ();
618 if (filter->getClass () == &LocationOnlyFilter::class$)
620 LocationOnlyFilter *lof
621 = reinterpret_cast<LocationOnlyFilter *> (filter);
622 loc = lof->getLocation ();
630 throw_jvmti_error (jvmtiError err)
634 if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
636 msg = JvNewStringLatin1 (error);
637 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
640 msg = JvNewStringLatin1 ("out of memory");
642 using namespace gnu::classpath::jdwp::exception;
643 throw new JdwpInternalErrorException (msg);
647 jdwpClassPrepareCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
648 jthread thread, jclass klass)
650 using namespace gnu::classpath::jdwp;
653 jvmtiError err = env->GetClassStatus (klass, &flags);
654 if (err != JVMTI_ERROR_NONE)
655 throw_jvmti_error (err);
657 using namespace gnu::classpath::jdwp::event;
659 if (flags & JVMTI_CLASS_STATUS_VERIFIED)
660 status |= ClassPrepareEvent::STATUS_VERIFIED;
661 if (flags & JVMTI_CLASS_STATUS_PREPARED)
662 status |= ClassPrepareEvent::STATUS_PREPARED;
663 if (flags & JVMTI_CLASS_STATUS_ERROR)
664 status |= ClassPrepareEvent::STATUS_ERROR;
665 if (flags & JVMTI_CLASS_STATUS_INITIALIZED)
666 status |= ClassPrepareEvent::STATUS_INITIALIZED;
668 event::ClassPrepareEvent *event
669 = new event::ClassPrepareEvent (thread, klass, status);
670 Jdwp::notify (event);
674 jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
677 using namespace gnu::classpath::jdwp::event;
679 ThreadEndEvent *e = new ThreadEndEvent (thread);
680 gnu::classpath::jdwp::Jdwp::notify (e);
684 jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
687 using namespace gnu::classpath::jdwp::event;
689 ThreadStartEvent *e = new ThreadStartEvent (thread);
690 gnu::classpath::jdwp::Jdwp::notify (e);
694 jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
696 using namespace gnu::classpath::jdwp::event;
697 gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
701 jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
704 // The VM is now initialized, add our callbacks
705 jvmtiEventCallbacks callbacks;
706 DEFINE_CALLBACK (callbacks, ClassPrepare);
707 DEFINE_CALLBACK (callbacks, ThreadEnd);
708 DEFINE_CALLBACK (callbacks, ThreadStart);
709 DEFINE_CALLBACK (callbacks, VMDeath);
710 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
713 ENABLE_EVENT (CLASS_PREPARE, NULL);
714 ENABLE_EVENT (THREAD_END, NULL);
715 ENABLE_EVENT (THREAD_START, NULL);
716 ENABLE_EVENT (VM_DEATH, NULL);
719 using namespace gnu::classpath::jdwp::event;
720 gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));