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 *
_Jv_GetJDWP_JVMTIEnv(void)109 _Jv_GetJDWP_JVMTIEnv (void)
110 {
111   return _jdwp_jvmtiEnv;
112 }
113 
114 void
initialize()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
suspendThread(Thread * thread)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
resumeThread(Thread * thread)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
getSuspendCount(Thread * thread)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
registerEvent(EventRequest * request)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
unregisterEvent(EventRequest * request)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
clearEvents(MAYBE_UNUSED jbyte kind)452 gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind)
453 {
454 }
455 
456 java::util::Collection *
getAllLoadedClasses(void)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::
getClassStatus(jclass klass)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::
getAllClassMethods(jclass klass)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::
getClassMethod(jclass klass,jlong id)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 *
getFrames(Thread * thread,jint start,jint length)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::
getFrame(Thread * thread,jlong frameID)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::
getFrameCount(Thread * thread)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::
getThreadStatus(Thread * thread)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::
getLoadRequests(MAYBE_UNUSED ClassLoader * cl)690 getLoadRequests (MAYBE_UNUSED ClassLoader *cl)
691 {
692   return new ::java::util::ArrayList ();
693 }
694 
695 MethodResult *
696 gnu::classpath::jdwp::VMVirtualMachine::
executeMethod(MAYBE_UNUSED jobject obj,MAYBE_UNUSED Thread * thread,MAYBE_UNUSED jclass clazz,MAYBE_UNUSED VMMethod * method,MAYBE_UNUSED JArray<value::Value * > * values,MAYBE_UNUSED jint options)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::
getSourceFile(jclass clazz)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::
redefineClasses(MAYBE_UNUSED JArray<jclass> * types,MAYBE_UNUSED JArray<jbyteArray> * bytecodes)721 redefineClasses (MAYBE_UNUSED JArray<jclass> *types,
722 		 MAYBE_UNUSED JArray<jbyteArray> *bytecodes)
723 {
724 }
725 
726 void
727 gnu::classpath::jdwp::VMVirtualMachine::
setDefaultStratum(MAYBE_UNUSED jstring stratum)728 setDefaultStratum (MAYBE_UNUSED jstring stratum)
729 {
730 }
731 
732 jstring
733 gnu::classpath::jdwp::VMVirtualMachine::
getSourceDebugExtension(MAYBE_UNUSED jclass klass)734 getSourceDebugExtension (MAYBE_UNUSED jclass klass)
735 {
736   return NULL;
737 }
738 
739 jbyteArray
740 gnu::classpath::jdwp::VMVirtualMachine::
getBytecodes(MAYBE_UNUSED gnu::classpath::jdwp::VMMethod * method)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::
getMonitorInfo(MAYBE_UNUSED jobject obj)748 getMonitorInfo (MAYBE_UNUSED jobject obj)
749 {
750   return NULL;
751 }
752 
753 jobjectArray
754 gnu::classpath::jdwp::VMVirtualMachine::
getOwnedMonitors(MAYBE_UNUSED::java::lang::Thread * thread)755 getOwnedMonitors (MAYBE_UNUSED ::java::lang::Thread *thread)
756 {
757   return NULL;
758 }
759 
760 jobject
761 gnu::classpath::jdwp::VMVirtualMachine::
getCurrentContendedMonitor(MAYBE_UNUSED::java::lang::Thread * thread)762 getCurrentContendedMonitor (MAYBE_UNUSED ::java::lang::Thread *thread)
763 {
764   return NULL;
765 }
766 
767 void
768 gnu::classpath::jdwp::VMVirtualMachine::
popFrames(MAYBE_UNUSED::java::lang::Thread * thread,MAYBE_UNUSED jlong frameId)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
get_linetable(jvmtiEnv * env,jmethodID method,jint * count_ptr,jvmtiLineNumberEntry ** table_ptr)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 *
get_request_step_filter(EventRequest * request)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 *
get_request_location(EventRequest * request)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
handle_single_step(jvmtiEnv * env,struct step_info * sinfo,jthread thread,jmethodID method,jlocation location)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
throw_jvmti_error(jvmtiError err)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
jdwpBreakpointCB(jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread,jmethodID method,jlocation location)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
jdwpClassPrepareCB(MAYBE_UNUSED jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread,jclass klass)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
jdwpExceptionCB(jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread,jmethodID method,jlocation location,jobject exception,jmethodID catch_method,jlocation catch_location)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
jdwpSingleStepCB(jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread,jmethodID method,jlocation location)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
jdwpThreadEndCB(MAYBE_UNUSED jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread)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
jdwpThreadStartCB(MAYBE_UNUSED jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread)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
jdwpVMDeathCB(MAYBE_UNUSED jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env)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
jdwpVMInitCB(MAYBE_UNUSED jvmtiEnv * env,MAYBE_UNUSED JNIEnv * jni_env,jthread thread)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 }
1160