OSDN Git Service

ab11b3ed299bfb916ea6f86460a2274aa1cc19b9
[pf3gnuchains/gcc-fork.git] / libjava / gnu / classpath / jdwp / natVMVirtualMachine.cc
1 // natVMVirtualMachine.cc - native support for VMVirtualMachine
2
3 /* Copyright (C) 2006, 2007 Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
10
11 #include <config.h>
12 #include <gcj/cni.h>
13 #include <java-assert.h>
14 #include <jvm.h>
15 #include <jvmti.h>
16
17 #include <java-interp.h>
18
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>
31
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>
53
54 using namespace java::lang;
55 using namespace gnu::classpath::jdwp::event;
56 using namespace gnu::classpath::jdwp::util;
57
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);
66
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)
71 // JVMTI environment
72 static jvmtiEnv *_jdwp_jvmtiEnv;
73
74 jvmtiEnv *
75 _Jv_GetJDWP_JVMTIEnv (void)
76 {
77   return _jdwp_jvmtiEnv;
78 }
79
80 void
81 gnu::classpath::jdwp::VMVirtualMachine::initialize ()
82 {
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);
86
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);
92 }
93
94 void
95 gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
96 {
97   jint value;
98   Integer *count;
99   {
100     JvSynchronize dummy (_jdwp_suspend_counts);
101     count = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
102     if (count == NULL)
103       {
104         // New -- suspend thread
105         value = 0;
106       }
107     else
108       {
109         // Thread already suspended
110         value = count->intValue ();
111       }
112
113     count = Integer::valueOf (++value);
114     _jdwp_suspend_counts->put (thread, count);
115   }
116
117   if (value == 1)
118     {
119       // Suspend the thread
120       jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread);
121       if (err != JVMTI_ERROR_NONE)
122         {
123           using namespace gnu::gcj::runtime;
124           using namespace gnu::classpath::jdwp::exception;
125           char *reason;
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 ());
132         }
133     }
134 }
135
136 void
137 gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread)
138 {
139   jint value;
140   {
141     JvSynchronize dummy (_jdwp_suspend_counts);
142     Integer *count
143       = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
144     if (count == NULL)
145       {
146         // Thread not suspended: ThreadReference.Resume says to ignore it.
147         return;
148       }
149     else
150       {
151         // Decrement suspend count
152         value = count->intValue () - 1;
153       }
154
155     if (value == 0)
156       {
157         // Thread will be resumed, remove from table
158         _jdwp_suspend_counts->remove (thread);
159       }
160     else
161       {
162         // Thread stays suspended: record new suspend count
163         count = Integer::valueOf (value);
164         _jdwp_suspend_counts->put (thread, count);
165       }
166   }
167
168   if (value == 0)
169     {
170       jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread);
171       if (err != JVMTI_ERROR_NONE)
172         {
173           using namespace gnu::gcj::runtime;
174           using namespace gnu::classpath::jdwp::exception;
175           char *reason;
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 ());
182         }
183     }
184 }
185
186 jint
187 gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread)
188 {
189   jint suspensions = 0;
190   Integer *count
191     = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
192   if (count != NULL)
193     suspensions = count->intValue ();
194   return suspensions;
195 }
196
197 void
198 gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
199 {
200   switch (request->getEventKind ())
201     {
202     case EventRequest::EVENT_SINGLE_STEP:
203       break;
204
205     case EventRequest::EVENT_BREAKPOINT:
206       {
207         using namespace ::gnu::gcj::jvmti;
208         Location *loc = get_request_location (request);
209         if (loc == NULL)
210           {
211             using namespace gnu::classpath::jdwp::exception;
212             throw new InvalidLocationException ();
213           }
214
215         jlong method = loc->getMethod ()->getId ();
216         jlocation index = loc->getIndex ();
217         Breakpoint  *bp = BreakpointManager::getBreakpoint (method, index);
218         if (bp == NULL)
219           {
220             // Breakpoint not in interpreter yet
221             bp = BreakpointManager::newBreakpoint (method, index);
222           }
223         else
224           {
225             // Ignore the duplicate
226           }
227       }
228      break;
229
230     case EventRequest::EVENT_FRAME_POP:
231       break;
232
233     case EventRequest::EVENT_EXCEPTION:
234       break;
235
236     case EventRequest::EVENT_USER_DEFINED:
237       break;
238
239     case EventRequest::EVENT_THREAD_START:
240       break;
241
242     case EventRequest::EVENT_THREAD_END:
243       break;
244
245     case EventRequest::EVENT_CLASS_PREPARE:
246       break;
247
248     case EventRequest::EVENT_CLASS_LOAD:
249       break;
250
251     case EventRequest::EVENT_CLASS_UNLOAD:
252       break;
253
254     case EventRequest::EVENT_FIELD_ACCESS:
255       break;
256
257     case EventRequest::EVENT_FIELD_MODIFY:
258       break;
259
260     case EventRequest::EVENT_METHOD_ENTRY:
261       break;
262
263     case EventRequest::EVENT_METHOD_EXIT:
264       break;
265
266     case EventRequest::EVENT_VM_INIT:
267       break;
268
269     case EventRequest::EVENT_VM_DEATH:
270       break;
271     }
272 }
273
274 void
275 gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
276 {
277   switch (request->getEventKind ())
278     {
279     case EventRequest::EVENT_SINGLE_STEP:
280       break;
281
282     case EventRequest::EVENT_BREAKPOINT:
283       {
284         using namespace gnu::gcj::jvmti;
285         ::java::util::Collection *breakpoints;
286         EventManager *em = EventManager::getDefault ();
287         breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
288
289         // Check for duplicates
290         int matches = 0;
291         Location *the_location = get_request_location (request);
292
293         // This should not be possible: we REQUIRE a Location
294         // to install a breakpoint
295         JvAssert (the_location != NULL);
296
297         ::java::util::Iterator *iter = breakpoints->iterator ();
298         while (iter->hasNext ())
299           {
300             EventRequest *er
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)
305               {
306                 // Short-circuit: already more than one breakpoint
307                 return;
308               }
309           }
310
311         if (matches == 0)
312           {
313             using namespace gnu::classpath::jdwp::exception;
314             jstring msg
315               = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
316             throw new JdwpInternalErrorException (msg);
317           }
318
319         jlong methodId = the_location->getMethod ()->getId ();
320         BreakpointManager::deleteBreakpoint (methodId,
321                                              the_location->getIndex ());
322       }
323       break;
324
325     case EventRequest::EVENT_FRAME_POP:
326       break;
327
328     case EventRequest::EVENT_EXCEPTION:
329       break;
330
331     case EventRequest::EVENT_USER_DEFINED:
332       break;
333
334     case EventRequest::EVENT_THREAD_START:
335       break;
336
337     case EventRequest::EVENT_THREAD_END:
338       break;
339
340     case EventRequest::EVENT_CLASS_PREPARE:
341       break;
342
343     case EventRequest::EVENT_CLASS_LOAD:
344       break;
345
346     case EventRequest::EVENT_CLASS_UNLOAD:
347       break;
348
349     case EventRequest::EVENT_FIELD_ACCESS:
350       break;
351
352     case EventRequest::EVENT_FIELD_MODIFY:
353       break;
354
355     case EventRequest::EVENT_METHOD_ENTRY:
356       break;
357
358     case EventRequest::EVENT_METHOD_EXIT:
359       break;
360
361     case EventRequest::EVENT_VM_INIT:
362       break;
363
364     case EventRequest::EVENT_VM_DEATH:
365       break;
366     }
367 }
368
369 void
370 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
371 {
372 }
373
374 jint
375 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void)
376 {
377   return 0;
378 }
379
380 java::util::Iterator *
381 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
382 {
383   return NULL;
384 }
385
386 jint
387 gnu::classpath::jdwp::VMVirtualMachine::
388 getClassStatus (MAYBE_UNUSED jclass klass)
389 {
390   return 0;
391 }
392
393 JArray<gnu::classpath::jdwp::VMMethod *> *
394 gnu::classpath::jdwp::VMVirtualMachine::
395 getAllClassMethods (jclass klass)
396 {
397   jint count;
398   jmethodID *methods;
399   jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
400   if (err != JVMTI_ERROR_NONE)
401     throw_jvmti_error (err);
402
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)
408     {
409       jlong id = reinterpret_cast<jlong> (methods[i]);
410       rmeth[i] = getClassMethod (klass, id);
411     }
412
413   _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
414   return result;
415 }
416
417 gnu::classpath::jdwp::VMMethod *
418 gnu::classpath::jdwp::VMVirtualMachine::
419 getClassMethod (jclass klass, jlong id)
420 {
421   jmethodID method = reinterpret_cast<jmethodID> (id);
422   _Jv_MethodBase *bmeth = _Jv_FindInterpreterMethod (klass, method);
423   if (bmeth != NULL)
424     return new gnu::classpath::jdwp::VMMethod (klass, id);
425
426   throw new gnu::classpath::jdwp::exception::InvalidMethodException (id);
427 }
428
429 java::util::ArrayList *
430 gnu::classpath::jdwp::VMVirtualMachine::getFrames (MAYBE_UNUSED Thread *thread,
431                                                    MAYBE_UNUSED jint start,
432                                                    MAYBE_UNUSED jint length)
433 {
434   return NULL;
435 }
436
437 gnu::classpath::jdwp::VMFrame *
438 gnu::classpath::jdwp::VMVirtualMachine::
439 getFrame (Thread *thread, jlong frameID)
440 {
441   using namespace gnu::classpath::jdwp::exception;
442   
443   _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
444   jint depth = 0;
445   _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID); 
446   
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)
450     {
451       vm_frame = vm_frame->next;
452       depth++;
453       if (vm_frame == NULL)
454         throw new InvalidFrameException (frameID);
455     }
456   
457   Location *loc = NULL;
458   jvmtiFrameInfo info;
459   jvmtiError jerr;
460   jint num_frames;
461   jclass klass;
462   
463   // Get the info for the frame of interest
464   jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
465    
466   if (jerr != JVMTI_ERROR_NONE)
467     throw_jvmti_error (jerr);
468   
469   jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
470       
471   if (jerr != JVMTI_ERROR_NONE)
472     throw_jvmti_error (jerr);
473
474   VMMethod *meth 
475     = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
476   
477   if (info.location == -1)
478     loc = new Location (meth, 0);
479   else
480     loc = new Location (meth, info.location);
481   
482   return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc); 
483 }
484
485 jint
486 gnu::classpath::jdwp::VMVirtualMachine::
487 getFrameCount (Thread *thread)
488 {
489   jint frame_count;
490   
491   jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
492   
493   if (jerr != JVMTI_ERROR_NONE)
494     throw_jvmti_error (jerr);
495   
496   return frame_count;
497 }
498
499 jint
500 gnu::classpath::jdwp::VMVirtualMachine::
501 getThreadStatus (MAYBE_UNUSED Thread *thread)
502 {
503   return 0;
504 }
505
506 java::util::ArrayList *
507 gnu::classpath::jdwp::VMVirtualMachine::
508 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
509 {
510   return NULL;
511 }
512
513 MethodResult *
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)
519 {
520   return NULL;
521 }
522
523 jstring
524 gnu::classpath::jdwp::VMVirtualMachine::
525 getSourceFile (MAYBE_UNUSED jclass clazz)
526 {
527   return NULL;
528 }
529
530 static Location *
531 get_request_location (EventRequest *request)
532 {
533   Location *loc = NULL;
534   ::java::util::Collection *filters = request->getFilters ();
535   ::java::util::Iterator *iter = filters->iterator ();
536   while (iter->hasNext ())
537     {
538       using namespace gnu::classpath::jdwp::event::filters;
539       IEventFilter *filter = (IEventFilter *) iter->next ();
540       if (filter->getClass () == &LocationOnlyFilter::class$)
541         {
542           LocationOnlyFilter *lof
543             = reinterpret_cast<LocationOnlyFilter *> (filter);
544           loc = lof->getLocation ();
545         }
546     }
547
548   return loc;
549 }
550
551 static void
552 throw_jvmti_error (jvmtiError err)
553 {
554   char *error;
555   jstring msg;
556   if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
557     {
558       msg = JvNewStringLatin1 (error);
559       _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
560     }
561   else
562     msg = JvNewStringLatin1 ("out of memory");
563
564   using namespace gnu::classpath::jdwp::exception;
565   throw new JdwpInternalErrorException (msg);
566 }
567
568 static void JNICALL
569 jdwpClassPrepareCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
570                     jthread thread, jclass klass)
571 {
572   using namespace gnu::classpath::jdwp;
573
574   jint flags = 0;
575   jvmtiError err = env->GetClassStatus (klass, &flags);
576   if (err != JVMTI_ERROR_NONE)
577     throw_jvmti_error (err);
578
579   using namespace gnu::classpath::jdwp::event;
580   jint status = 0;
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;
589
590   event::ClassPrepareEvent *event
591     = new event::ClassPrepareEvent (thread, klass, status);
592   Jdwp::notify (event);
593 }
594
595 static void JNICALL
596 jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
597                  jthread thread)
598 {
599   using namespace gnu::classpath::jdwp::event;
600
601   ThreadEndEvent *e = new ThreadEndEvent (thread);
602   gnu::classpath::jdwp::Jdwp::notify (e);
603 }
604
605 static void JNICALL
606 jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
607                    jthread thread)
608 {
609   using namespace gnu::classpath::jdwp::event;
610
611   ThreadStartEvent *e = new ThreadStartEvent (thread);
612   gnu::classpath::jdwp::Jdwp::notify (e);
613 }
614
615 static void JNICALL
616 jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
617 {
618   using namespace gnu::classpath::jdwp::event;
619   gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
620 }
621
622 static void JNICALL
623 jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
624               jthread thread)
625 {
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));
633
634   // Enable 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);
639
640   // Send JDWP VMInit
641   using namespace gnu::classpath::jdwp::event;
642   gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));
643 }