OSDN Git Service

* gnu/classpath/jdwp/natVMVirtualMachine.cc (handle_single_step): Use
[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 <java-interp.h>
15 #include <jvm.h>
16 #include <jvmti.h>
17
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/lang/Throwable.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/JdwpConstants$StepDepth.h>
34 #include <gnu/classpath/jdwp/JdwpConstants$StepSize.h>
35 #include <gnu/classpath/jdwp/JdwpConstants$ThreadStatus.h>
36 #include <gnu/classpath/jdwp/VMFrame.h>
37 #include <gnu/classpath/jdwp/VMMethod.h>
38 #include <gnu/classpath/jdwp/VMVirtualMachine.h>
39 #include <gnu/classpath/jdwp/event/BreakpointEvent.h>
40 #include <gnu/classpath/jdwp/event/ClassPrepareEvent.h>
41 #include <gnu/classpath/jdwp/event/ExceptionEvent.h>
42 #include <gnu/classpath/jdwp/event/EventManager.h>
43 #include <gnu/classpath/jdwp/event/EventRequest.h>
44 #include <gnu/classpath/jdwp/event/SingleStepEvent.h>
45 #include <gnu/classpath/jdwp/event/ThreadEndEvent.h>
46 #include <gnu/classpath/jdwp/event/ThreadStartEvent.h>
47 #include <gnu/classpath/jdwp/event/VmDeathEvent.h>
48 #include <gnu/classpath/jdwp/event/VmInitEvent.h>
49 #include <gnu/classpath/jdwp/event/filters/IEventFilter.h>
50 #include <gnu/classpath/jdwp/event/filters/LocationOnlyFilter.h>
51 #include <gnu/classpath/jdwp/event/filters/StepFilter.h>
52 #include <gnu/classpath/jdwp/exception/AbsentInformationException.h>
53 #include <gnu/classpath/jdwp/exception/InvalidFrameException.h>
54 #include <gnu/classpath/jdwp/exception/InvalidLocationException.h>
55 #include <gnu/classpath/jdwp/exception/InvalidMethodException.h>
56 #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h>
57 #include <gnu/classpath/jdwp/id/ThreadId.h>
58 #include <gnu/classpath/jdwp/util/Location.h>
59 #include <gnu/classpath/jdwp/util/MethodResult.h>
60 #include <gnu/gcj/jvmti/Breakpoint.h>
61 #include <gnu/gcj/jvmti/BreakpointManager.h>
62
63 using namespace java::lang;
64 using namespace gnu::classpath::jdwp::event;
65 using namespace gnu::classpath::jdwp::util;
66
67 // Stepping information
68 struct step_info
69 {
70   jint size;   // See gnu.classpath.jdwp.JdwpConstants.StepSize
71   jint depth;  // See gnu.classpath.jdwp.JdwpConstants.StepDepth
72   int stack_depth;  // stack depth at start of stepping
73   jmethodID method; // method in which we are stepping
74 };
75
76 // Forward declarations
77 static jvmtiError get_linetable (jvmtiEnv *, jmethodID, jint *,
78                                  jvmtiLineNumberEntry **);
79 static Location *get_request_location (EventRequest *);
80 static gnu::classpath::jdwp::event::filters::StepFilter *
81 get_request_step_filter (EventRequest *);
82 static void handle_single_step (jvmtiEnv *, struct step_info *, jthread,
83                                 jmethodID, jlocation);
84 static void JNICALL jdwpBreakpointCB (jvmtiEnv *, JNIEnv *, jthread,
85                                       jmethodID, jlocation);
86 static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
87 static void JNICALL jdwpExceptionCB (jvmtiEnv *, JNIEnv *jni_env, jthread,
88                                      jmethodID, jlocation, jobject,
89                                      jmethodID, jlocation);
90 static void JNICALL jdwpSingleStepCB (jvmtiEnv *, JNIEnv *, jthread,
91                                       jmethodID, jlocation);
92 static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
93 static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread);
94 static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *);
95 static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread);
96 static void throw_jvmti_error (jvmtiError);
97
98 #define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB
99 #define DISABLE_EVENT(Event,Thread)                                     \
100   _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_DISABLE,              \
101                                             JVMTI_EVENT_ ## Event, Thread)
102 #define ENABLE_EVENT(Event,Thread)                                      \
103   _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE,               \
104                                             JVMTI_EVENT_ ## Event, Thread)
105 // JVMTI environment
106 static jvmtiEnv *_jdwp_jvmtiEnv;
107
108 jvmtiEnv *
109 _Jv_GetJDWP_JVMTIEnv (void)
110 {
111   return _jdwp_jvmtiEnv;
112 }
113
114 void
115 gnu::classpath::jdwp::VMVirtualMachine::initialize ()
116 {
117   _jdwp_suspend_counts = new ::java::util::Hashtable ();
118   _stepping_threads = new ::java::util::Hashtable ();
119   _event_list = new ::java::util::ArrayList ();
120
121   JavaVM *vm = _Jv_GetJavaVM ();
122   union
123   {
124     void *ptr;
125     jvmtiEnv *env;
126   } foo;
127   vm->GetEnv (&(foo.ptr), JVMTI_VERSION_1_0);
128   _jdwp_jvmtiEnv = foo.env;
129
130   // Wait for VM_INIT to do more initialization
131   jvmtiEventCallbacks callbacks;
132   DEFINE_CALLBACK (callbacks, VMInit);
133   _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
134   ENABLE_EVENT (VM_INIT, NULL);
135 }
136
137 void
138 gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread)
139 {
140   jint value;
141   Integer *count;
142   {
143     JvSynchronize dummy (_jdwp_suspend_counts);
144     count = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
145     if (count == NULL)
146       {
147         // New -- suspend thread
148         value = 0;
149       }
150     else
151       {
152         // Thread already suspended
153         value = count->intValue ();
154       }
155
156     count = Integer::valueOf (++value);
157     _jdwp_suspend_counts->put (thread, count);
158   }
159
160   if (value == 1)
161     {
162       // Suspend the thread
163       jvmtiError err = _jdwp_jvmtiEnv->SuspendThread (thread);
164       if (err != JVMTI_ERROR_NONE)
165         {
166           using namespace gnu::gcj::runtime;
167           using namespace gnu::classpath::jdwp::exception;
168           char *reason;
169           _jdwp_jvmtiEnv->GetErrorName (err, &reason);
170           String *txt = JvNewStringLatin1 ("could not suspend thread: ");
171           StringBuilder *msg = new StringBuilder (txt);
172           msg->append (JvNewStringLatin1 (reason));
173           _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
174           throw new JdwpInternalErrorException (msg->toString ());
175         }
176     }
177 }
178
179 void
180 gnu::classpath::jdwp::VMVirtualMachine::resumeThread (Thread *thread)
181 {
182   jint value;
183   {
184     JvSynchronize dummy (_jdwp_suspend_counts);
185     Integer *count
186       = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
187     if (count == NULL)
188       {
189         // Thread not suspended: ThreadReference.Resume says to ignore it.
190         return;
191       }
192     else
193       {
194         // Decrement suspend count
195         value = count->intValue () - 1;
196       }
197
198     if (value == 0)
199       {
200         // Thread will be resumed, remove from table
201         _jdwp_suspend_counts->remove (thread);
202       }
203     else
204       {
205         // Thread stays suspended: record new suspend count
206         count = Integer::valueOf (value);
207         _jdwp_suspend_counts->put (thread, count);
208       }
209   }
210
211   if (value == 0)
212     {
213       jvmtiError err = _jdwp_jvmtiEnv->ResumeThread (thread);
214       if (err != JVMTI_ERROR_NONE)
215         {
216           using namespace gnu::gcj::runtime;
217           using namespace gnu::classpath::jdwp::exception;
218           char *reason;
219           _jdwp_jvmtiEnv->GetErrorName (err, &reason);
220           String *txt = JvNewStringLatin1 ("could not resume thread: ");
221           StringBuilder *msg = new StringBuilder (txt);
222           msg->append (JvNewStringLatin1 (reason));
223           _jdwp_jvmtiEnv->Deallocate ((unsigned char *) reason);
224           throw new JdwpInternalErrorException (msg->toString ());
225         }
226     }
227 }
228
229 jint
230 gnu::classpath::jdwp::VMVirtualMachine::getSuspendCount (Thread *thread)
231 {
232   jint suspensions = 0;
233   Integer *count
234     = reinterpret_cast<Integer *> (_jdwp_suspend_counts->get (thread));
235   if (count != NULL)
236     suspensions = count->intValue ();
237   return suspensions;
238 }
239
240 void
241 gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request)
242 {
243   switch (request->getEventKind ())
244     {
245     case EventRequest::EVENT_SINGLE_STEP:
246       {
247         Thread *thread;
248         filters::StepFilter *filter = get_request_step_filter (request);
249         if (filter == NULL)
250           {
251             // No filter specified: report every step in every
252             // thread.
253             thread = NULL;
254           }
255         else
256           {
257             // Add stepping information to list of stepping threads
258             thread = filter->getThread ()->getThread ();
259             _Jv_InterpFrame *frame
260               = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
261             struct step_info *sinfo
262               = (struct step_info *) JvAllocBytes (sizeof (struct step_info));
263             sinfo->size = filter->getSize ();
264             sinfo->depth = filter->getDepth ();
265             sinfo->stack_depth = frame->depth ();
266             sinfo->method = frame->self->get_method ();
267             _stepping_threads->put (thread, (jobject) sinfo);
268           }
269
270         ENABLE_EVENT (SINGLE_STEP, thread);
271       }
272       break;
273
274     case EventRequest::EVENT_BREAKPOINT:
275       {
276         using namespace ::gnu::gcj::jvmti;
277         Location *loc = get_request_location (request);
278         if (loc == NULL)
279           {
280             using namespace gnu::classpath::jdwp::exception;
281             throw new InvalidLocationException ();
282           }
283
284         jlong method = loc->getMethod ()->getId ();
285         jlocation index = loc->getIndex ();
286         Breakpoint  *bp = BreakpointManager::getBreakpoint (method, index);
287         if (bp == NULL)
288           {
289             // Breakpoint not in interpreter yet
290             bp = BreakpointManager::newBreakpoint (method, index);
291           }
292         else
293           {
294             // Ignore the duplicate
295           }
296       }
297       break;
298
299     case EventRequest::EVENT_FRAME_POP:
300       break;
301
302     case EventRequest::EVENT_EXCEPTION:
303       break;
304
305     case EventRequest::EVENT_USER_DEFINED:
306       break;
307
308     case EventRequest::EVENT_THREAD_START:
309       break;
310
311     case EventRequest::EVENT_THREAD_END:
312       break;
313
314     case EventRequest::EVENT_CLASS_PREPARE:
315       break;
316
317     case EventRequest::EVENT_CLASS_LOAD:
318       break;
319
320     case EventRequest::EVENT_CLASS_UNLOAD:
321       break;
322
323     case EventRequest::EVENT_FIELD_ACCESS:
324       break;
325
326     case EventRequest::EVENT_FIELD_MODIFY:
327       break;
328
329     case EventRequest::EVENT_METHOD_ENTRY:
330       break;
331
332     case EventRequest::EVENT_METHOD_EXIT:
333       break;
334
335     case EventRequest::EVENT_VM_INIT:
336       break;
337
338     case EventRequest::EVENT_VM_DEATH:
339       break;
340     }
341 }
342
343 void
344 gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request)
345 {
346   switch (request->getEventKind ())
347     {
348     case EventRequest::EVENT_SINGLE_STEP:
349       {
350         Thread *thread;
351         filters::StepFilter *filter = get_request_step_filter (request);
352         if (filter == NULL)
353           thread = NULL;
354         else
355           {
356             thread = filter->getThread ()->getThread ();
357             _stepping_threads->remove (thread);
358           }
359
360         DISABLE_EVENT (SINGLE_STEP, thread);
361       }
362       break;
363
364     case EventRequest::EVENT_BREAKPOINT:
365       {
366         using namespace gnu::gcj::jvmti;
367         ::java::util::Collection *breakpoints;
368         EventManager *em = EventManager::getDefault ();
369         breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT);
370
371         // Check for duplicates
372         int matches = 0;
373         Location *the_location = get_request_location (request);
374
375         // This should not be possible: we REQUIRE a Location
376         // to install a breakpoint
377         JvAssert (the_location != NULL);
378
379         ::java::util::Iterator *iter = breakpoints->iterator ();
380         while (iter->hasNext ())
381           {
382             EventRequest *er
383               = reinterpret_cast<EventRequest *> (iter->next ());
384             Location *loc = get_request_location (er);
385             JvAssert (loc != NULL);
386             if (loc->equals (the_location) && ++matches == 2)
387               {
388                 // Short-circuit: already more than one breakpoint
389                 return;
390               }
391           }
392
393         if (matches == 0)
394           {
395             using namespace gnu::classpath::jdwp::exception;
396             jstring msg
397               = JvNewStringLatin1 ("attempt to remove unknown breakpoint");
398             throw new JdwpInternalErrorException (msg);
399           }
400
401         jlong methodId = the_location->getMethod ()->getId ();
402         BreakpointManager::deleteBreakpoint (methodId,
403                                              the_location->getIndex ());
404       }
405       break;
406
407     case EventRequest::EVENT_FRAME_POP:
408       break;
409
410     case EventRequest::EVENT_EXCEPTION:
411       break;
412
413     case EventRequest::EVENT_USER_DEFINED:
414       break;
415
416     case EventRequest::EVENT_THREAD_START:
417       break;
418
419     case EventRequest::EVENT_THREAD_END:
420       break;
421
422     case EventRequest::EVENT_CLASS_PREPARE:
423       break;
424
425     case EventRequest::EVENT_CLASS_LOAD:
426       break;
427
428     case EventRequest::EVENT_CLASS_UNLOAD:
429       break;
430
431     case EventRequest::EVENT_FIELD_ACCESS:
432       break;
433
434     case EventRequest::EVENT_FIELD_MODIFY:
435       break;
436
437     case EventRequest::EVENT_METHOD_ENTRY:
438       break;
439
440     case EventRequest::EVENT_METHOD_EXIT:
441       break;
442
443     case EventRequest::EVENT_VM_INIT:
444       break;
445
446     case EventRequest::EVENT_VM_DEATH:
447       break;
448     }
449 }
450
451 void
452 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
453 {
454 }
455
456 java::util::Collection *
457 gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void)
458 {
459   using namespace ::java::util;
460   return (Collection *) new ArrayList ();
461 }
462
463 jint
464 gnu::classpath::jdwp::VMVirtualMachine::
465 getClassStatus (jclass klass)
466 {
467   jint flags = 0;
468   jvmtiError err = _jdwp_jvmtiEnv->GetClassStatus (klass, &flags);
469   if (err != JVMTI_ERROR_NONE)
470     throw_jvmti_error (err);
471
472   using namespace gnu::classpath::jdwp::event;
473   jint status = 0;
474   if (flags & JVMTI_CLASS_STATUS_VERIFIED)
475     status |= ClassPrepareEvent::STATUS_VERIFIED;
476   if (flags & JVMTI_CLASS_STATUS_PREPARED)
477     status |= ClassPrepareEvent::STATUS_PREPARED;
478   if (flags & JVMTI_CLASS_STATUS_ERROR)
479     status |= ClassPrepareEvent::STATUS_ERROR;
480   if (flags & JVMTI_CLASS_STATUS_INITIALIZED)
481     status |= ClassPrepareEvent::STATUS_INITIALIZED;
482
483   return status;
484 }
485
486 JArray<gnu::classpath::jdwp::VMMethod *> *
487 gnu::classpath::jdwp::VMVirtualMachine::
488 getAllClassMethods (jclass klass)
489 {
490   jint count;
491   jmethodID *methods;
492   jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
493   if (err != JVMTI_ERROR_NONE)
494     throw_jvmti_error (err);
495
496   JArray<VMMethod *> *result
497     = (JArray<VMMethod *> *) JvNewObjectArray (count,
498                                                &VMMethod::class$, NULL);
499   VMMethod **rmeth = elements (result);
500   for (int i = 0; i < count; ++i)
501     {
502       jlong id = reinterpret_cast<jlong> (methods[i]);
503       rmeth[i] = getClassMethod (klass, id);
504     }
505
506   _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods);
507   return result;
508 }
509
510 gnu::classpath::jdwp::VMMethod *
511 gnu::classpath::jdwp::VMVirtualMachine::
512 getClassMethod (jclass klass, jlong id)
513 {
514   jint count;
515   jmethodID *methods;
516   jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods);
517   if (err != JVMTI_ERROR_NONE)
518     throw_jvmti_error (err);
519
520   jmethodID meth_id = reinterpret_cast<jmethodID> (id);
521
522   using namespace gnu::classpath::jdwp;
523
524   // Check if this method is defined for the given class and if so return a
525   // VMMethod representing it.
526   for (int i = 0; i < count; i++)
527     {
528       if (methods[i] == meth_id)
529         return new VMMethod (klass, reinterpret_cast<jlong> (meth_id));
530     }
531
532   throw new exception::InvalidMethodException (id);
533 }
534
535 java::util::ArrayList *
536 gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, jint start,
537                                                    jint length)
538 {
539   jint frame_count = getFrameCount (thread);
540   ::java::util::ArrayList *frame_list;
541   
542   // Calculate the max number of frames to be returned.
543   jint num_frames = frame_count - start;
544   
545   // Check if num_frames is valid.
546   if (num_frames < 0)
547     num_frames = 0;
548   
549   // Check if there are more than length frames left after start.
550   // If length ios -1 return all remaining frames.
551   if (length != -1 && num_frames > length)
552     num_frames = length;
553      
554   frame_list = new ::java::util::ArrayList (num_frames);
555   
556   _Jv_Frame *vm_frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
557   
558   // Take start frames off the top of the stack
559   while (vm_frame != NULL && start > 0)
560     {
561       start--;
562       vm_frame = vm_frame->next;
563     }
564   
565   // Use as a counter for the number of frames returned.
566   num_frames = 0;
567   
568   while (vm_frame != NULL && (num_frames < length || length == -1))
569     {  
570       jlong frameId = reinterpret_cast<jlong> (vm_frame);
571       
572       VMFrame *frame = getFrame (thread, frameId);
573       frame_list->add (frame);
574       vm_frame = vm_frame->next;
575       num_frames++;
576     }
577   
578   return frame_list;
579 }
580
581 gnu::classpath::jdwp::VMFrame *
582 gnu::classpath::jdwp::VMVirtualMachine::
583 getFrame (Thread *thread, jlong frameID)
584 {
585   using namespace gnu::classpath::jdwp::exception;
586   
587   _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame;
588   jint depth = 0;
589   _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID); 
590   
591   // We need to find the stack depth of the frame, so search through the call
592   // stack to find it.  This also checks for a valid frameID.
593   while (vm_frame != frame)
594     {
595       vm_frame = vm_frame->next;
596       depth++;
597       if (vm_frame == NULL)
598         throw new InvalidFrameException (frameID);
599     }
600   
601   Location *loc = NULL;
602   jvmtiFrameInfo info;
603   jvmtiError jerr;
604   jint num_frames;
605   jclass klass;
606   
607   // Get the info for the frame of interest
608   jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames);
609    
610   if (jerr != JVMTI_ERROR_NONE)
611     throw_jvmti_error (jerr);
612   
613   jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass);
614       
615   if (jerr != JVMTI_ERROR_NONE)
616     throw_jvmti_error (jerr);
617
618   VMMethod *meth 
619     = getClassMethod (klass, reinterpret_cast<jlong> (info.method));
620   
621   jobject this_obj;
622   
623   if (info.location == -1)
624     {
625       loc = new Location (meth, 0);
626       this_obj = NULL;
627     }
628   else
629     {
630       loc = new Location (meth, info.location);
631       _Jv_InterpFrame *iframe = reinterpret_cast<_Jv_InterpFrame *> (vm_frame);
632       this_obj = iframe->get_this_ptr ();
633     }
634   
635   return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc,
636                       this_obj); 
637 }
638
639 jint
640 gnu::classpath::jdwp::VMVirtualMachine::
641 getFrameCount (Thread *thread)
642 {
643   jint frame_count;
644   
645   jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count);
646   
647   if (jerr != JVMTI_ERROR_NONE)
648     throw_jvmti_error (jerr);
649   
650   return frame_count;
651 }
652
653 jint
654 gnu::classpath::jdwp::VMVirtualMachine::
655 getThreadStatus (Thread *thread)
656 {
657   jint thr_state, status;
658   
659   jvmtiError jerr = _jdwp_jvmtiEnv->GetThreadState (thread, &thr_state);
660   if (jerr != JVMTI_ERROR_NONE)
661     throw_jvmti_error (jerr);
662   
663   if (thr_state & JVMTI_THREAD_STATE_SLEEPING)
664     status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::SLEEPING;
665   else if (thr_state & JVMTI_THREAD_STATE_RUNNABLE)
666     status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING;
667   else if (thr_state & JVMTI_THREAD_STATE_WAITING)
668     {
669       if (thr_state & (JVMTI_THREAD_STATE_IN_OBJECT_WAIT
670                        | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER))
671         status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::MONITOR;
672       else
673         status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::WAIT;
674     }
675   else
676     {
677       // The thread is not SLEEPING, MONITOR, or WAIT.  It may, however, be
678       // alive but not yet started.
679       if (!(thr_state & (JVMTI_THREAD_STATE_ALIVE 
680                          | JVMTI_THREAD_STATE_TERMINATED)))
681         status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING;
682       status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::ZOMBIE;     
683     }   
684
685   return status;
686 }
687
688 java::util::ArrayList *
689 gnu::classpath::jdwp::VMVirtualMachine::
690 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
691 {
692   return new ::java::util::ArrayList ();
693 }
694
695 MethodResult *
696 gnu::classpath::jdwp::VMVirtualMachine::
697 executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread,
698                MAYBE_UNUSED jclass clazz, MAYBE_UNUSED VMMethod *method,
699                MAYBE_UNUSED JArray<value::Value *> *values,
700                MAYBE_UNUSED jint options)
701 {
702   return NULL;
703 }
704
705 jstring
706 gnu::classpath::jdwp::VMVirtualMachine::
707 getSourceFile (jclass clazz)
708 {
709   jstring file = _Jv_GetInterpClassSourceFile (clazz);
710   
711   // Check if the source file was found.
712   if (file == NULL)
713     throw new exception::AbsentInformationException (
714                            _Jv_NewStringUTF("Source file not found"));
715   
716   return file;
717 }
718
719 void
720 gnu::classpath::jdwp::VMVirtualMachine::
721 redefineClasses (MAYBE_UNUSED JArray<jclass> *types,
722                  MAYBE_UNUSED JArray<jbyteArray> *bytecodes)
723 {
724 }
725
726 void
727 gnu::classpath::jdwp::VMVirtualMachine::
728 setDefaultStratum (MAYBE_UNUSED jstring stratum)
729 {
730 }
731
732 jstring
733 gnu::classpath::jdwp::VMVirtualMachine::
734 getSourceDebugExtension (MAYBE_UNUSED jclass klass)
735 {
736   return NULL;
737 }
738
739 jbyteArray
740 gnu::classpath::jdwp::VMVirtualMachine::
741 getBytecodes (MAYBE_UNUSED gnu::classpath::jdwp::VMMethod *method)
742 {
743   return NULL;
744 }
745
746 gnu::classpath::jdwp::util::MonitorInfo *
747 gnu::classpath::jdwp::VMVirtualMachine::
748 getMonitorInfo (MAYBE_UNUSED jobject obj)
749 {
750   return NULL;
751 }
752
753 jobjectArray
754 gnu::classpath::jdwp::VMVirtualMachine::
755 getOwnedMonitors (MAYBE_UNUSED ::java::lang::Thread *thread)
756 {
757   return NULL;
758 }
759
760 jobject
761 gnu::classpath::jdwp::VMVirtualMachine::
762 getCurrentContendedMonitor (MAYBE_UNUSED ::java::lang::Thread *thread)
763 {
764   return NULL;
765 }
766
767 void
768 gnu::classpath::jdwp::VMVirtualMachine::
769 popFrames (MAYBE_UNUSED ::java::lang::Thread *thread,
770            MAYBE_UNUSED jlong frameId)
771 {
772 }
773
774 // A simple caching function used while single-stepping
775 static jvmtiError
776 get_linetable (jvmtiEnv *env, jmethodID method, jint *count_ptr,
777                jvmtiLineNumberEntry **table_ptr)
778 {
779   static jint last_count = 0;
780   static jvmtiLineNumberEntry *last_table = NULL;
781   static jmethodID last_method = 0;
782
783   if (method == last_method)
784     {
785       *count_ptr = last_count;
786       *table_ptr = last_table;
787       return JVMTI_ERROR_NONE;
788     }
789
790   jvmtiError err;
791   jint count;
792   jvmtiLineNumberEntry *table;
793   err = env->GetLineNumberTable (method, &count, &table);
794   if (err != JVMTI_ERROR_NONE)
795     {
796       // Keep last table in cache
797       return err;
798     }
799
800   env->Deallocate ((unsigned char *) last_table);
801   last_table = *table_ptr = table;
802   last_count = *count_ptr = count;
803   return JVMTI_ERROR_NONE;
804 }
805
806 static gnu::classpath::jdwp::event::filters::StepFilter *
807 get_request_step_filter (EventRequest *request)
808 {
809   ::java::util::Collection *filters = request->getFilters ();
810   ::java::util::Iterator *iter = filters->iterator ();
811   filters::StepFilter *filter = NULL;
812   while (iter->hasNext ())
813     {
814       using namespace gnu::classpath::jdwp::event::filters;
815       IEventFilter *next = (IEventFilter *) iter->next ();
816       if (next->getClass () == &StepFilter::class$)
817         {
818           filter = reinterpret_cast<StepFilter *> (next);
819           break;
820         }
821     }
822
823   return filter;
824 }
825
826 static Location *
827 get_request_location (EventRequest *request)
828 {
829   Location *loc = NULL;
830   ::java::util::Collection *filters = request->getFilters ();
831   ::java::util::Iterator *iter = filters->iterator ();
832   while (iter->hasNext ())
833     {
834       using namespace gnu::classpath::jdwp::event::filters;
835       IEventFilter *filter = (IEventFilter *) iter->next ();
836       if (filter->getClass () == &LocationOnlyFilter::class$)
837         {
838           LocationOnlyFilter *lof
839             = reinterpret_cast<LocationOnlyFilter *> (filter);
840           loc = lof->getLocation ();
841         }
842     }
843
844   return loc;
845 }
846
847 static void
848 handle_single_step (jvmtiEnv *env, struct step_info *sinfo, jthread thread,
849                     jmethodID method, jlocation location)
850 {
851   using namespace gnu::classpath::jdwp;
852
853   if (sinfo == NULL || sinfo->size == JdwpConstants$StepSize::MIN)
854     {
855       // Stop now
856       goto send_notification;
857     }
858   else
859     {
860       // Check if we're on a new source line
861       /* This is a little inefficient when we're stepping OVER,
862          but this must be done when stepping INTO. */
863       jint count;
864       jvmtiLineNumberEntry *table;
865       if (get_linetable (env, method, &count, &table) == JVMTI_ERROR_NONE)
866         {
867           jint i;
868           for (i = 0; i < count; ++i)
869             {
870               if (table[i].start_location == location)
871                 {
872                   // This is the start of a new line -- stop
873                   goto send_notification;
874                 }
875             }
876
877           // Not at a new source line -- just keep stepping
878           return;
879         }
880       else
881         {
882           /* Something went wrong: either "absent information"
883              or "out of memory" ("invalid method id" and "native
884              method" aren't possible -- those are validated before
885              single stepping is enabled).
886
887              Do what gdb does: just keep going. */
888           return;
889         }
890     }
891
892  send_notification:
893   jclass klass;
894   jvmtiError err = env->GetMethodDeclaringClass (method, &klass);
895   if (err != JVMTI_ERROR_NONE)
896     {
897       fprintf (stderr, "libgcj: internal error: could not find class for method while single stepping -- continuing\n");
898       return;
899     }
900
901   VMMethod *vmmethod = new VMMethod (klass, reinterpret_cast<jlong> (method));
902   Location *loc = new Location (vmmethod, location);
903   _Jv_InterpFrame *iframe
904     = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);  
905   JvAssert (iframe->frame_type == frame_interpreter);
906   jobject instance = iframe->get_this_ptr ();
907   event::SingleStepEvent *event
908     = new event::SingleStepEvent (thread, loc, instance);
909
910   // We only want to send the notification (and consequently
911   // suspend) if we are not about to execute a breakpoint.
912   _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (iframe->self);
913   if (im->breakpoint_at (location))
914     {
915       // Next insn is a breakpoint -- record event and
916       // wait for the JVMTI breakpoint notification to
917       // enforce a suspension policy.
918       VMVirtualMachine::_event_list->add (event);
919     }
920   else
921     {
922       // Next insn is not a breakpoint, so send notification
923       // and enforce the suspend policy.
924       Jdwp::notify (event);
925     }
926 }
927
928 static void
929 throw_jvmti_error (jvmtiError err)
930 {
931   char *error;
932   jstring msg;
933   if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE)
934     {
935       msg = JvNewStringLatin1 (error);
936       _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error);
937     }
938   else
939     msg = JvNewStringLatin1 ("out of memory");
940
941   using namespace gnu::classpath::jdwp::exception;
942   throw new JdwpInternalErrorException (msg);
943 }
944
945 static void JNICALL
946 jdwpBreakpointCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
947                   jthread thread, jmethodID method, jlocation location)
948 {
949   jclass klass;
950   jvmtiError err;
951   err = env->GetMethodDeclaringClass (method, &klass);
952   JvAssert (err == JVMTI_ERROR_NONE);
953
954   using namespace gnu::classpath::jdwp;
955   using namespace gnu::classpath::jdwp::event;
956
957   jlong methodId = reinterpret_cast<jlong> (method);
958   VMMethod *meth = VMVirtualMachine::getClassMethod (klass, methodId);
959   Location *loc = new Location (meth, location);
960   _Jv_InterpFrame *iframe
961     = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
962   JvAssert (iframe->frame_type == frame_interpreter);
963   jobject instance = iframe->get_this_ptr ();
964   BreakpointEvent *event = new BreakpointEvent (thread, loc, instance);
965   
966   VMVirtualMachine::_event_list->add (event);
967   JArray<Event *> *events
968     = ((JArray<Event *> *)
969        JvNewObjectArray (VMVirtualMachine::_event_list->size (),
970                          &Event::class$, NULL));
971   VMVirtualMachine::_event_list->toArray ((jobjectArray) events);
972   VMVirtualMachine::_event_list->clear ();
973   Jdwp::notify (events);
974 }
975
976 static void JNICALL
977 jdwpClassPrepareCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
978                     jthread thread, jclass klass)
979 {
980   using namespace gnu::classpath::jdwp;
981
982   jint status = VMVirtualMachine::getClassStatus (klass);
983   event::ClassPrepareEvent *event
984     = new event::ClassPrepareEvent (thread, klass, status);
985   Jdwp::notify (event);
986 }
987
988 static void JNICALL
989 jdwpExceptionCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread,
990                  jmethodID method, jlocation location, jobject exception,
991                  jmethodID catch_method, jlocation catch_location)
992 {
993   using namespace gnu::classpath::jdwp;
994   jclass throw_klass;
995   jvmtiError err = env->GetMethodDeclaringClass (method, &throw_klass);
996   if (err != JVMTI_ERROR_NONE)
997     {
998       fprintf (stderr, "libgcj: internal error: could not find class for ");
999       fprintf (stderr, "method throwing exception -- continuing\n");
1000       return;
1001     }
1002
1003   VMMethod *vmmethod = new VMMethod (throw_klass,
1004                                      reinterpret_cast<jlong> (method));
1005   Location *throw_loc = new Location (vmmethod, location);  
1006   Location *catch_loc = NULL;
1007   if (catch_method == 0)
1008     catch_loc = Location::getEmptyLocation ();
1009   else
1010     {
1011       jclass catch_klass;
1012       err = env->GetMethodDeclaringClass (catch_method, &catch_klass);
1013       if (err != JVMTI_ERROR_NONE)
1014         {
1015           fprintf (stderr,
1016                    "libgcj: internal error: could not find class for ");
1017           fprintf (stderr,
1018                    "method catching exception -- ignoring\n");
1019         }
1020       else
1021         {
1022           vmmethod = new VMMethod (catch_klass,
1023                                    reinterpret_cast<jlong> (catch_method));
1024           catch_loc = new Location (vmmethod, catch_location);
1025         }
1026     }
1027
1028   _Jv_InterpFrame *iframe
1029     = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
1030   jobject instance = (iframe == NULL) ? NULL : iframe->get_this_ptr ();
1031   Throwable *throwable = reinterpret_cast<Throwable *> (exception);
1032   event::ExceptionEvent *e = new ExceptionEvent (throwable, thread,
1033                                                  throw_loc, catch_loc,
1034                                                  throw_klass, instance);
1035   Jdwp::notify (e);
1036 }
1037
1038 static void JNICALL
1039 jdwpSingleStepCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread,
1040                   jmethodID method, jlocation location)
1041 {
1042   jobject si =
1043     gnu::classpath::jdwp::VMVirtualMachine::_stepping_threads->get (thread);
1044   struct step_info *sinfo = reinterpret_cast<struct step_info *> (si);
1045
1046   if (sinfo == NULL)
1047     {
1048       // no step filter for this thread - simply report it
1049       handle_single_step (env, NULL, thread, method, location);
1050     }
1051   else
1052     {
1053       // A step filter exists for this thread
1054       using namespace gnu::classpath::jdwp;
1055
1056       _Jv_InterpFrame *frame
1057         = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
1058
1059       switch (sinfo->depth)
1060         {
1061         case JdwpConstants$StepDepth::INTO:
1062           /* This is the easy case. We ignore the method and
1063              simply stop at either the next insn, or the next source
1064              line. */
1065           handle_single_step (env, sinfo, thread, method, location);
1066           break;
1067
1068         case JdwpConstants$StepDepth::OVER:
1069           /* This is also a pretty easy case. We just make sure that
1070              the methods are the same and that we are at the same
1071              stack depth, but we should also stop on the next
1072              insn/line if the stack depth is LESS THAN it was when
1073              we started stepping. */
1074           if (method == sinfo->method)
1075             {
1076               // Still in the same method -- must be at same stack depth
1077               // to avoid confusion with recursive methods.
1078               if (frame->depth () == sinfo->stack_depth)
1079                 handle_single_step (env, sinfo, thread, method, location);
1080             }
1081           else if (frame->depth () < sinfo->stack_depth)
1082             {
1083               // The method in which we were stepping was popped off
1084               // the stack. We simply need to stop at the next insn/line.
1085               handle_single_step (env, sinfo, thread, method, location);
1086             }
1087           break;
1088
1089         case JdwpConstants$StepDepth::OUT:
1090           // All we need to do is check the stack depth
1091           if (sinfo->stack_depth > frame->depth ())
1092             handle_single_step (env, sinfo, thread, method, location);
1093           break;
1094
1095         default:
1096           /* This should not happen. The JDWP back-end should have
1097              validated the StepFilter. */
1098           fprintf (stderr,
1099                    "libgcj: unknown step depth while single stepping\n");
1100           return;
1101         }
1102     }
1103 }
1104
1105 static void JNICALL
1106 jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
1107                  jthread thread)
1108 {
1109   using namespace gnu::classpath::jdwp::event;
1110
1111   ThreadEndEvent *e = new ThreadEndEvent (thread);
1112   gnu::classpath::jdwp::Jdwp::notify (e);
1113 }
1114
1115 static void JNICALL
1116 jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
1117                    jthread thread)
1118 {
1119   using namespace gnu::classpath::jdwp::event;
1120
1121   ThreadStartEvent *e = new ThreadStartEvent (thread);
1122   gnu::classpath::jdwp::Jdwp::notify (e);
1123 }
1124
1125 static void JNICALL
1126 jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env)
1127 {
1128   using namespace gnu::classpath::jdwp::event;
1129   gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ());
1130 }
1131
1132 static void JNICALL
1133 jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env,
1134               jthread thread)
1135 {
1136   // The VM is now initialized, add our callbacks
1137   jvmtiEventCallbacks callbacks;
1138   DEFINE_CALLBACK (callbacks, Breakpoint);
1139   DEFINE_CALLBACK (callbacks, ClassPrepare);
1140   DEFINE_CALLBACK (callbacks, Exception);
1141   DEFINE_CALLBACK (callbacks, SingleStep);
1142   DEFINE_CALLBACK (callbacks, ThreadEnd);
1143   DEFINE_CALLBACK (callbacks, ThreadStart);
1144   DEFINE_CALLBACK (callbacks, VMDeath);
1145   _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks));
1146
1147   // Enable callbacks
1148   ENABLE_EVENT (BREAKPOINT, NULL);
1149   ENABLE_EVENT (CLASS_PREPARE, NULL);
1150   ENABLE_EVENT (EXCEPTION, NULL);
1151   // SingleStep is enabled only when needed
1152   ENABLE_EVENT (THREAD_END, NULL);
1153   ENABLE_EVENT (THREAD_START, NULL);
1154   ENABLE_EVENT (VM_DEATH, NULL);
1155
1156   // Send JDWP VMInit
1157   using namespace gnu::classpath::jdwp::event;
1158   gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread));
1159 }