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>
17 #include <java-interp.h>
19 #include <java/lang/Class.h>
20 #include <java/lang/ClassLoader.h>
21 #include <java/lang/Integer.h>
22 #include <java/lang/String.h>
23 #include <java/lang/StringBuilder.h>
24 #include <java/lang/Thread.h>
25 #include <java/nio/ByteBuffer.h>
26 #include <java/nio/ByteBufferImpl.h>
27 #include <java/util/ArrayList.h>
28 #include <java/util/Collection.h>
29 #include <java/util/Hashtable.h>
30 #include <java/util/Iterator.h>
32 #include <gnu/classpath/jdwp/Jdwp.h>
33 #include <gnu/classpath/jdwp/VMFrame.h>
34 #include <gnu/classpath/jdwp/VMMethod.h>
35 #include <gnu/classpath/jdwp/VMVirtualMachine.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/exception/InvalidFrameException.h>
46 #include <gnu/classpath/jdwp/exception/InvalidLocationException.h>
47 #include <gnu/classpath/jdwp/exception/InvalidMethodException.h>
48 #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
49 #include <gnu/classpath/jdwp/util/Location.h>
50 #include <gnu/classpath/jdwp/util/MethodResult.h>
51 #include <gnu/gcj/jvmti/Breakpoint.h>
52 #include <gnu/gcj/jvmti/BreakpointManager.h>
54 using namespace java::lang;
55 using namespace gnu::classpath::jdwp::event;
56 using namespace gnu::classpath::jdwp::util;
58 // Forward declarations
59 static Location *get_request_location (EventRequest *);
60 static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
61 static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
62 static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread);
63 static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *);
64 static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread);
65 static void throw_jvmti_error (jvmtiError);
67 #define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB
68 #define ENABLE_EVENT(Event,Thread) \
69 _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE, \
70 JVMTI_EVENT_ ## Event, Thread)
72 static jvmtiEnv *_jdwp_jvmtiEnv;
75 _Jv_GetJDWP_JVMTIEnv (void)
77 return _jdwp_jvmtiEnv;
81 gnu::classpath::jdwp::VMVirtualMachine::initialize ()
83 _jdwp_suspend_counts = new ::java::util::Hashtable ();
84 JavaVM *vm = _Jv_GetJavaVM ();
85 vm->GetEnv (reinterpret_cast<void **> (&_jdwp_jvmtiEnv), JVMTI_VERSION_1_0);
87 // Wait for VM_INIT to do more initialization
88 jvmtiEventCallbacks callbacks;
89 DEFINE_CALLBACK (callbacks, VMInit);
90 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
91 ENABLE_EVENT (VM_INIT, NULL);
95 gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
100 JvSynchronize dummy (_jdwp_suspend_counts);
101 count = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
104 // New -- suspend thread
109 // Thread already suspended
110 value = count->intValue ();
113 count = Integer::valueOf (++value);
114 _jdwp_suspend_counts->put (thread, count);
119 // Suspend the thread
120 jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread);
121 if (err != JVMTI_ERROR_NONE)
123 using namespace gnu::gcj::runtime;
124 using namespace gnu::classpath::jdwp::exception;
126 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
127 String *txt = JvNewStringLatin1 ("could not suspend thread: ");
128 StringBuilder *msg = new StringBuilder (txt);
129 msg->append (JvNewStringLatin1 (reason));
130 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
131 throw new JdwpInternalErrorException (msg->toString ());
137 gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread)
141 JvSynchronize dummy (_jdwp_suspend_counts);
143 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
146 // Thread not suspended: ThreadReference.Resume says to ignore it.
151 // Decrement suspend count
152 value = count->intValue () - 1;
157 // Thread will be resumed, remove from table
158 _jdwp_suspend_counts->remove (thread);
162 // Thread stays suspended: record new suspend count
163 count = Integer::valueOf (value);
164 _jdwp_suspend_counts->put (thread, count);
170 jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread);
171 if (err != JVMTI_ERROR_NONE)
173 using namespace gnu::gcj::runtime;
174 using namespace gnu::classpath::jdwp::exception;
176 _jdwp_jvmtiEnv->GetErrorName (err, &reason);
177 String *txt = JvNewStringLatin1 ("could not resume thread: ");
178 StringBuilder *msg = new StringBuilder (txt);
179 msg->append (JvNewStringLatin1 (reason));
180 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
181 throw new JdwpInternalErrorException (msg->toString ());
187 gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread)
189 jint suspensions = 0;
191 = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
193 suspensions = count->intValue ();
198 gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
200 switch (request->getEventKind ())
202 case EventRequest::EVENT_SINGLE_STEP:
205 case EventRequest::EVENT_BREAKPOINT:
207 using namespace ::gnu::gcj::jvmti;
208 Location *loc = get_request_location (request);
211 using namespace gnu::classpath::jdwp::exception;
212 throw new InvalidLocationException ();
215 jlong method = loc->getMethod ()->getId ();
216 jlocation index = loc->getIndex ();
217 Breakpoint *bp = BreakpointManager::getBreakpoint (method, index);
220 // Breakpoint not in interpreter yet
221 bp = BreakpointManager::newBreakpoint (method, index);
225 // Ignore the duplicate
230 case EventRequest::EVENT_FRAME_POP:
233 case EventRequest::EVENT_EXCEPTION:
236 case EventRequest::EVENT_USER_DEFINED:
239 case EventRequest::EVENT_THREAD_START:
242 case EventRequest::EVENT_THREAD_END:
245 case EventRequest::EVENT_CLASS_PREPARE:
248 case EventRequest::EVENT_CLASS_LOAD:
251 case EventRequest::EVENT_CLASS_UNLOAD:
254 case EventRequest::EVENT_FIELD_ACCESS:
257 case EventRequest::EVENT_FIELD_MODIFY:
260 case EventRequest::EVENT_METHOD_ENTRY:
263 case EventRequest::EVENT_METHOD_EXIT:
266 case EventRequest::EVENT_VM_INIT:
269 case EventRequest::EVENT_VM_DEATH:
275 gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
277 switch (request->getEventKind ())
279 case EventRequest::EVENT_SINGLE_STEP:
282 case EventRequest::EVENT_BREAKPOINT:
284 using namespace gnu::gcj::jvmti;
285 ::java::util::Collection *breakpoints;
286 EventManager *em = EventManager::getDefault ();
287 breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
289 // Check for duplicates
291 Location *the_location = get_request_location (request);
293 // This should not be possible: we REQUIRE a Location
294 // to install a breakpoint
295 JvAssert (the_location != NULL);
297 ::java::util::Iterator *iter = breakpoints->iterator ();
298 while (iter->hasNext ())
301 = reinterpret_cast<EventRequest *> (iter->next ());
302 Location *loc = get_request_location (er);
303 JvAssert (loc != NULL);
304 if (loc->equals (the_location) && ++matches == 2)
306 // Short-circuit: already more than one breakpoint
313 using namespace gnu::classpath::jdwp::exception;
315 = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
316 throw new JdwpInternalErrorException (msg);
319 jlong methodId = the_location->getMethod ()->getId ();
320 BreakpointManager::deleteBreakpoint (methodId,
321 the_location->getIndex ());
325 case EventRequest::EVENT_FRAME_POP:
328 case EventRequest::EVENT_EXCEPTION:
331 case EventRequest::EVENT_USER_DEFINED:
334 case EventRequest::EVENT_THREAD_START:
337 case EventRequest::EVENT_THREAD_END:
340 case EventRequest::EVENT_CLASS_PREPARE:
343 case EventRequest::EVENT_CLASS_LOAD:
346 case EventRequest::EVENT_CLASS_UNLOAD:
349 case EventRequest::EVENT_FIELD_ACCESS:
352 case EventRequest::EVENT_FIELD_MODIFY:
355 case EventRequest::EVENT_METHOD_ENTRY:
358 case EventRequest::EVENT_METHOD_EXIT:
361 case EventRequest::EVENT_VM_INIT:
364 case EventRequest::EVENT_VM_DEATH:
370 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
375 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void)
380 java::util::Iterator *
381 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
387 gnu::classpath::jdwp::VMVirtualMachine::
388 getClassStatus (MAYBE_UNUSED jclass klass)
393 JArray<gnu::classpath::jdwp::VMMethod *> *
394 gnu::classpath::jdwp::VMVirtualMachine::
395 getAllClassMethods (jclass klass)
399 jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
400 if (err != JVMTI_ERROR_NONE)
401 throw_jvmti_error (err);
403 JArray<VMMethod *> *result
404 = (JArray<VMMethod *> *) JvNewObjectArray (count,
405 &VMMethod::class$, NULL);
406 VMMethod **rmeth = elements (result);
407 for (int i = 0; i < count; ++i)
409 jlong id = reinterpret_cast<jlong> (methods[i]);
410 rmeth[i] = getClassMethod (klass, id);
413 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
417 gnu::classpath::jdwp::VMMethod *
418 gnu::classpath::jdwp::VMVirtualMachine::
419 getClassMethod (jclass klass, jlong id)
421 jmethodID method = reinterpret_cast<jmethodID> (id);
422 _Jv_MethodBase *bmeth = _Jv_FindInterpreterMethod (klass, method);
424 return new gnu::classpath::jdwp::VMMethod (klass, id);
426 throw new gnu::classpath::jdwp::exception::InvalidMethodException (id);
429 java::util::ArrayList *
430 gnu::classpath::jdwp::VMVirtualMachine::getFrames (MAYBE_UNUSED Thread *thread,
431 MAYBE_UNUSED jint start,
432 MAYBE_UNUSED jint length)
437 gnu::classpath::jdwp::VMFrame *
438 gnu::classpath::jdwp::VMVirtualMachine::
439 getFrame (Thread *thread, jlong frameID)
441 using namespace gnu::classpath::jdwp::exception;
443 _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
445 _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID);
447 // We need to find the stack depth of the frame, so search through the call
448 // stack to find it. This also checks for a valid frameID.
449 while (vm_frame != frame)
451 vm_frame = vm_frame->next;
453 if (vm_frame == NULL)
454 throw new InvalidFrameException (frameID);
457 Location *loc = NULL;
463 // Get the info for the frame of interest
464 jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
466 if (jerr != JVMTI_ERROR_NONE)
467 throw_jvmti_error (jerr);
469 jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
471 if (jerr != JVMTI_ERROR_NONE)
472 throw_jvmti_error (jerr);
475 = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
477 if (info.location == -1)
478 loc = new Location (meth, 0);
480 loc = new Location (meth, info.location);
482 return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc);
486 gnu::classpath::jdwp::VMVirtualMachine::
487 getFrameCount (Thread *thread)
491 jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
493 if (jerr != JVMTI_ERROR_NONE)
494 throw_jvmti_error (jerr);
500 gnu::classpath::jdwp::VMVirtualMachine::
501 getThreadStatus (MAYBE_UNUSED Thread *thread)
506 java::util::ArrayList *
507 gnu::classpath::jdwp::VMVirtualMachine::
508 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
514 gnu::classpath::jdwp::VMVirtualMachine::
515 executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread,
516 MAYBE_UNUSED jclass clazz, MAYBE_UNUSED reflect::Method *method,
517 MAYBE_UNUSED jobjectArray values,
518 MAYBE_UNUSED jboolean nonVirtual)
524 gnu::classpath::jdwp::VMVirtualMachine::
525 getSourceFile (MAYBE_UNUSED jclass clazz)
531 get_request_location (EventRequest *request)
533 Location *loc = NULL;
534 ::java::util::Collection *filters = request->getFilters ();
535 ::java::util::Iterator *iter = filters->iterator ();
536 while (iter->hasNext ())
538 using namespace gnu::classpath::jdwp::event::filters;
539 IEventFilter *filter = (IEventFilter *) iter->next ();
540 if (filter->getClass () == &LocationOnlyFilter::class$)
542 LocationOnlyFilter *lof
543 = reinterpret_cast<LocationOnlyFilter *> (filter);
544 loc = lof->getLocation ();
552 throw_jvmti_error (jvmtiError err)
556 if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
558 msg = JvNewStringLatin1 (error);
559 _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
562 msg = JvNewStringLatin1 ("out of memory");
564 using namespace gnu::classpath::jdwp::exception;
565 throw new JdwpInternalErrorException (msg);
569 jdwpClassPrepareCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
570 jthread thread, jclass klass)
572 using namespace gnu::classpath::jdwp;
575 jvmtiError err = env->GetClassStatus (klass, &flags);
576 if (err != JVMTI_ERROR_NONE)
577 throw_jvmti_error (err);
579 using namespace gnu::classpath::jdwp::event;
581 if (flags & JVMTI_CLASS_STATUS_VERIFIED)
582 status |= ClassPrepareEvent::STATUS_VERIFIED;
583 if (flags & JVMTI_CLASS_STATUS_PREPARED)
584 status |= ClassPrepareEvent::STATUS_PREPARED;
585 if (flags & JVMTI_CLASS_STATUS_ERROR)
586 status |= ClassPrepareEvent::STATUS_ERROR;
587 if (flags & JVMTI_CLASS_STATUS_INITIALIZED)
588 status |= ClassPrepareEvent::STATUS_INITIALIZED;
590 event::ClassPrepareEvent *event
591 = new event::ClassPrepareEvent (thread, klass, status);
592 Jdwp::notify (event);
596 jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
599 using namespace gnu::classpath::jdwp::event;
601 ThreadEndEvent *e = new ThreadEndEvent (thread);
602 gnu::classpath::jdwp::Jdwp::notify (e);
606 jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
609 using namespace gnu::classpath::jdwp::event;
611 ThreadStartEvent *e = new ThreadStartEvent (thread);
612 gnu::classpath::jdwp::Jdwp::notify (e);
616 jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
618 using namespace gnu::classpath::jdwp::event;
619 gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
623 jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
626 // The VM is now initialized, add our callbacks
627 jvmtiEventCallbacks callbacks;
628 DEFINE_CALLBACK (callbacks, ClassPrepare);
629 DEFINE_CALLBACK (callbacks, ThreadEnd);
630 DEFINE_CALLBACK (callbacks, ThreadStart);
631 DEFINE_CALLBACK (callbacks, VMDeath);
632 _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
635 ENABLE_EVENT (CLASS_PREPARE, NULL);
636 ENABLE_EVENT (THREAD_END, NULL);
637 ENABLE_EVENT (THREAD_START, NULL);
638 ENABLE_EVENT (VM_DEATH, NULL);
641 using namespace gnu::classpath::jdwp::event;
642 gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));