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