OSDN Git Service

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