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