* Removed all Id tags.
[cacao.git] / src / native / vm / gnu / VMjdwp.c
1 /* src/native/vm/VMjdwp.c - jvmti->jdwp interface
2
3    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    Contact: cacao@cacaojvm.org
26
27    Author: Martin Platter
28
29    Changes:
30
31 */
32
33 #include "native/jvmti/jvmti.h"
34 #include "native/jvmti/VMjdwp.h"
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 void printjvmtierror(char *desc, jvmtiError err) {
40     char* errdesc;
41         
42         if (err == JVMTI_ERROR_NONE) return;
43         (*jvmtienv)->GetErrorName(jvmtienv,err, &errdesc);
44         fprintf(stderr,"%s: jvmti error %s\n",desc, errdesc);
45         fflush(stderr);
46         (*jvmtienv)->Deallocate(jvmtienv,(unsigned char*)errdesc);
47 }
48
49
50 /* class and method IDs */
51 static jclass Jdwpclass, threadstartclass,threadendclass, classprepareclass,    vmmethodclass, locationclass, breakpointclass;
52 static jmethodID notifymid, threadstartmid,threadendmid, classpreparemid, 
53         vmmethodmid, locationmid, breakpointmid;
54
55 static void notify (JNIEnv* jni_env, jobject event){
56         fprintf(stderr,"VMjdwp notfiy called\n");
57
58         (*jni_env)->CallStaticVoidMethod(jni_env,Jdwpclass,notifymid,event);
59     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
60         fprintf(stderr,"Exception occourred in notify mehtod\n");
61                 (*jni_env)->ExceptionDescribe(jni_env);
62         }
63
64 }
65
66 static void ThreadStart (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
67                          jthread thread){
68         jobject obj;
69
70         obj = (*jni_env)->
71                 NewObject(jni_env, threadstartclass, threadstartmid, thread);
72         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
73         fprintf(stderr,"error calling ThreadStartEvent constructor\n");
74                 (*jni_env)->ExceptionDescribe(jni_env);
75                 return;
76         }
77
78         fprintf(stderr,"VMjdwp:ThreadStart: thread %p\n",thread);
79         fflush(stderr);
80
81         notify (jni_env,obj);
82 }
83
84
85 static void ThreadEnd (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
86                          jthread thread){
87         jobject obj;
88
89
90         obj = (*jni_env)->NewObject(jni_env, threadendclass, threadendmid, thread);
91         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
92         fprintf(stderr,"error calling ThreadEndEvent constructor\n");
93                 (*jni_env)->ExceptionDescribe(jni_env);
94                 return;
95         }
96
97         fprintf(stderr,"VMjdwp:ThreadEnd: thread %p\n",thread);
98         fflush(stderr);
99
100         notify (jni_env,obj);
101 }
102
103
104 static void ClassPrepare (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
105                                                   jthread thread, jclass klass) {
106         jobject obj;
107         int classstatus;
108         jvmtiError e;
109
110         if (JVMTI_ERROR_NONE != 
111                 (e = (*jvmtienv)->GetClassStatus(jvmtienv, klass, &classstatus))) {
112                 printjvmtierror("unable to get class status", e);
113                 return;
114         }
115
116         obj = (*jni_env)->NewObject(jni_env, classprepareclass, classpreparemid, thread, klass, classstatus);
117         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
118         fprintf(stderr,"error calling ClassPrepareEvent constructor\n");
119                 (*jni_env)->ExceptionDescribe(jni_env);
120                 return;
121         }
122
123         fprintf(stderr,"VMjdwp:ClassPrepareEvent: thread %p\n",thread);
124         fflush(stderr);
125
126         notify (jni_env,obj);
127 }
128
129 static void Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread,
130                                            jmethodID method, jlocation location, jobject exception,
131                                            jmethodID catch_method, jlocation catch_location) {
132         /* gnu classpath jdwp has no ExceptionEvent yet */
133         fprintf(stderr,"VMjdwp:Exception: thread %p\n",thread);
134         fflush(stderr);
135         
136 }
137
138 static void Breakpoint (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread,
139                                                 jmethodID method, jlocation location) {
140         jobject vmmethod, loc, ev;
141         jclass mcl;
142         jvmtiError e;
143
144         if (JVMTI_ERROR_NONE != 
145                 (e = (*jvmtienv)->GetMethodDeclaringClass(jvmtienv,
146                                                                            method,
147                                                                            &mcl))){
148                 printjvmtierror("unable to get declaring class", e);
149                 return;
150         }
151
152         vmmethod = (*jni_env)->NewObject(jni_env, vmmethodclass, vmmethodmid, 
153                                                                          mcl, method);
154         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
155         fprintf(stderr,"error calling VMMethod constructor\n");
156                 (*jni_env)->ExceptionDescribe(jni_env);
157                 return;
158         }
159
160         loc = (*jni_env)->NewObject(jni_env, locationclass, locationmid, 
161                                                                          vmmethod, location);
162         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
163         fprintf(stderr,"error calling location constructor\n");
164                 (*jni_env)->ExceptionDescribe(jni_env);
165                 return;
166         }
167         
168         /* XXX todo: get object instance - needs jvmti local variable support */
169         ev = (*jni_env)->NewObject(jni_env, breakpointclass, breakpointmid, 
170                                                                          thread, loc,NULL);
171         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
172         fprintf(stderr,"error calling breakpoint constructor\n");
173                 (*jni_env)->ExceptionDescribe(jni_env);
174                 return;
175         }
176
177         fprintf(stderr,"VMjdwp:Breakpoint: thread %p\n",thread);
178         fflush(stderr);
179
180         notify (jni_env,ev);    
181 }
182
183
184 static void MethodEntry (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
185                                                  jthread thread, jmethodID method) {
186         /* do not report gnu/classpath/jdwp method entries */
187 }
188
189
190 static void VMDeath (jvmtiEnv *jvmti_env,
191                      JNIEnv* jni_env) {
192   fprintf(stderr,"JVMTI-Event: IMPLEMENT ME!!!");
193 }
194
195
196 /* setup_jdwp_thread **********************************************************
197
198    Helper function to start JDWP listening thread
199
200 *******************************************************************************/
201
202 static void setup_jdwp_thread(JNIEnv* jni_env) {
203         jobject o;
204         jmethodID m;
205         jstring  s;
206
207         /* new gnu.classpath.jdwp.Jdwp() */
208         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"<init>","()V");
209     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
210         fprintf(stderr,"could not get Jdwp constructor\n");
211                 (*jni_env)->ExceptionDescribe(jni_env);
212                 exit(1); 
213         }
214         
215         o = (*jni_env)->NewObject(jni_env, Jdwpclass, m);
216     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
217         fprintf(stderr,"error calling Jdwp constructor\n");
218                 (*jni_env)->ExceptionDescribe(jni_env);
219                 exit(1); 
220         }
221
222         jdwpthread = (jthread)o;
223         
224         
225         /* configure(jdwpoptions) */
226         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"configure",
227                                                                 "(Ljava/lang/String;)V");
228     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
229         fprintf(stderr,"could not get Jdwp configure method\n");
230                 (*jni_env)->ExceptionDescribe(jni_env);
231                 exit(1); 
232         }
233
234         
235         s = (*jni_env)->NewStringUTF(jni_env,jdwpoptions);
236     if (s == NULL) {
237         fprintf(stderr,"could not get new java string from jdwp options\n");
238                 exit(1); 
239         }
240
241         free(jdwpoptions);
242         
243         (*jni_env)->CallVoidMethod(jni_env,o,m,s);
244     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
245         fprintf(stderr,"Exception occourred in Jdwp configure\n");
246                 (*jni_env)->ExceptionDescribe(jni_env);
247                 exit(1); 
248         }
249
250         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"_doInitialization","()V");
251     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
252         fprintf(stderr,"could not get Jdwp _doInitialization method\n");
253                 (*jni_env)->ExceptionDescribe(jni_env);
254                 exit(1); 
255         }
256
257
258         (*jni_env)->CallVoidMethod(jni_env,o,m);
259     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
260         fprintf(stderr,"Exception occourred in Jdwp _doInitialization\n");
261                 (*jni_env)->ExceptionDescribe(jni_env);
262                 exit(1); 
263         }
264 }
265
266 #define FINDCLASSWITHEXCEPTION(CLASS,SIGNATURE) \
267         CLASS = (*jni_env)->FindClass(jni_env, SIGNATURE);     \
268         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {  \
269                 fprintf(stderr,"could not find %s\n", SIGNATURE);  \
270                 (*jni_env)->ExceptionDescribe(jni_env);            \
271                 exit(1);                                           \
272         }
273 #define GETMIDWITHEXCEPTION(CLASS, CLASSNAME, MID, METHODNAME, METHODSIG) \
274         FINDCLASSWITHEXCEPTION(CLASS, CLASSNAME);                             \
275         MID = (*jni_env)->GetMethodID(jni_env, CLASS, METHODNAME, METHODSIG); \
276     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {                 \
277         fprintf(stderr,"could not get %s %s\n",CLASSNAME, METHODNAME);    \
278                 (*jni_env)->ExceptionDescribe(jni_env);                           \
279                 exit(1);                                                          \
280         }
281
282
283 static void fillidcache(JNIEnv* jni_env) {
284         FINDCLASSWITHEXCEPTION(Jdwpclass, "gnu/classpath/jdwp/Jdwp");
285         
286         notifymid = (*jni_env)->
287          GetStaticMethodID(jni_env,Jdwpclass,
288                                            "notify","(Lgnu/classpath/jdwp/event/Event;)V");
289         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
290                 fprintf(stderr,"could not get notify method\n");
291                 (*jni_env)->ExceptionDescribe(jni_env);
292                 exit(1); 
293         }
294
295         GETMIDWITHEXCEPTION(threadstartclass, 
296                                                 "gnu/classpath/jdwp/event/ThreadStartEvent", 
297                                                 threadstartmid, "<init>", "(Ljava/lang/Thread;)V");
298
299
300         GETMIDWITHEXCEPTION(threadendclass, 
301                                                 "gnu/classpath/jdwp/event/ThreadEndEvent", 
302                                                 threadendmid, "<init>", "(Ljava/lang/Thread;)V");
303
304
305         GETMIDWITHEXCEPTION(classprepareclass, 
306                                                 "gnu/classpath/jdwp/event/ClassPrepareEvent", 
307                                                 classpreparemid, "<init>", 
308                                                 "(Ljava/lang/Thread;Ljava/lang/Class;I)V");
309
310
311         GETMIDWITHEXCEPTION(vmmethodclass, "gnu/classpath/jdwp/VMMethod",
312                                                 vmmethodmid, "<init>", "(Ljava/lang/Class;J)V");
313
314         GETMIDWITHEXCEPTION(locationclass, "gnu/classpath/jdwp/util/Location",
315                                                 locationmid, "<init>", 
316                                                 "(Lgnu/classpath/jdwp/VMMethod;J)V");
317
318
319         GETMIDWITHEXCEPTION(
320                 breakpointclass, 
321                 "gnu/classpath/jdwp/event/BreakpointEvent", 
322                 breakpointmid, "<init>", 
323                 "(Ljava/lang/Thread;Lgnu/classpath/jdwp/util/Location;Ljava/lang/Object;)V");
324
325 }
326
327 static void VMInit (jvmtiEnv *jvmti_env, 
328                     JNIEnv* jni_env,
329                     jthread thread) {
330         jclass cl;
331         jmethodID m;
332         jobject eventobj;
333         jvmtiError err;
334
335         fprintf(stderr,"JDWP VMInit\n");
336
337         /* get needed jmethodIDs and jclasses for callbacks */
338         fillidcache(jni_env);
339
340         /* startup gnu classpath jdwp thread */
341         setup_jdwp_thread(jni_env);
342
343         fprintf(stderr,"JDWP listening thread started\n");
344
345         /* send VmInitEvent */
346     cl = (*jni_env)->FindClass(jni_env, 
347                                                                           "gnu/classpath/jdwp/event/VmInitEvent");
348     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
349         fprintf(stderr,"could not find class VMInitEvent\n");
350                 (*jni_env)->ExceptionDescribe(jni_env);
351                 exit(1); 
352         }
353
354         m = (*jni_env)->GetMethodID(jni_env,cl,"<init>",
355                                                                 "(Ljava/lang/Thread;)V");
356     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
357         fprintf(stderr,"could not get VmInitEvent constructor\n");
358                 (*jni_env)->ExceptionDescribe(jni_env);
359                 exit(1); 
360         }
361
362         eventobj = (*jni_env)->NewObject(jni_env, cl, m, thread);
363     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
364         fprintf(stderr,"error calling VmInitEvent constructor\n");
365                 (*jni_env)->ExceptionDescribe(jni_env);
366                 exit(1); 
367         }
368
369
370         notify (jni_env,eventobj);
371
372         if (suspend) {
373                 fprintf(stderr,"suspend initial thread\n");
374                 err = (*jvmti_env)->SuspendThread(jvmti_env,thread);
375                 printjvmtierror("error suspending initial thread",err);
376         }
377 }
378
379 static void usage() {   
380         puts("usage jdwp:[help]|(<option>=<value>),*");
381         puts("   transport=[dt_socket|...]");
382         puts("   address=<hostname:port>");
383         puts("   server=[y|n]");
384         puts("   suspend=[y|n]");
385 }
386
387 static bool processoptions(char *options) {
388         int i,len;
389         
390         if (strncmp(options,"help",4) == 0) {
391                 usage();
392                 return false;
393         }
394
395         suspend = true;         /* default value */
396
397
398         /* copy options for later use in java jdwp listen thread configure */
399         jdwpoptions = malloc(sizeof(char)*strlen(options));
400         strncpy(jdwpoptions, options, sizeof(char)*strlen(options));
401
402         len = strlen(options);
403         
404         i=0;
405         while (i<len) {
406                 if (strncmp("suspend=",&options[i],8)==0) {
407                         if (8>=strlen(&options[i])) {
408                                 if ((options[i+8]== 'y') || (options[i+8]== 'n')) {
409                                         suspend = options[i+8]== 'y';
410                                 } else {
411                                         printf("jdwp error argument: %s\n",options);
412                                         usage();
413                                         return -1;
414                                 }
415                         }
416                 } else {
417                         /* these options will be handled by jdwp java configure */
418                         if ((strncmp("transport=",options,10)==0) ||
419                                 (strncmp("server=",options,7)==0)) {
420                         } else {
421                                 printf("jdwp unkown argument: %s\n",options);
422                                 usage();
423                                 return false;
424                         }
425                 }
426                 while ((options[i]!=',')&&(i<len)) i++;
427                 i++;
428         }
429         return true;    
430 }
431
432
433 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { 
434         jint rc;
435         jvmtiCapabilities cap;
436         jvmtiError e;
437
438
439         fprintf(stderr,"jdwp Agent_OnLoad options: %s\n",options);
440         if (!processoptions(options)) return -1;
441         
442         rc = (*vm)->GetEnv(vm, (void**)&jvmtienv, JVMTI_VERSION_1_0);
443         if (rc != JNI_OK) {         
444                 fprintf(stderr, "jdwp: Unable to get jvmtiEnv error=%d\n", rc);
445                 return -1;              
446         }
447         
448         /* set eventcallbacks */
449         if (JVMTI_ERROR_NONE != 
450                 (e = (*jvmtienv)->SetEventCallbacks(jvmtienv,
451                                                                            &jvmti_jdwp_EventCallbacks,
452                                                                            sizeof(jvmtiEventCallbacks)))){
453                 printjvmtierror("jdwp: unable to setup event callbacks", e);
454                 return -1;
455         }
456
457         e = (*jvmtienv)->GetPotentialCapabilities(jvmtienv, &cap);
458         printjvmtierror("jdwp: unable to get potential capabilities", e);
459         if (e == JVMTI_ERROR_NONE) 
460                 e = (*jvmtienv)->AddCapabilities(jvmtienv, &cap);
461         if (e != JVMTI_ERROR_NONE) {
462                 printjvmtierror("jdwp: error adding jvmti capabilities", e);
463                 return -1;
464         }
465         
466         /* only enable needed events. VMVirtualMachine.registerEvent will  
467            be used to enable other events by need */
468         if (JVMTI_ERROR_NONE != (e = (*jvmtienv)->
469                                                          SetEventNotificationMode(jvmtienv, JVMTI_ENABLE,
470                                                                                                           JVMTI_EVENT_VM_INIT, 
471                                                                                                           NULL))) {
472                 printjvmtierror("jdwp unable to enable vm init callback",e);
473                 return -1;
474         }
475
476         return 0;
477 }
478         
479
480 jvmtiEventCallbacks jvmti_jdwp_EventCallbacks = {
481     &VMInit,
482     &VMDeath,
483     &ThreadStart,
484     &ThreadEnd,
485     NULL, /* &ClassFileLoadHook, */
486     NULL, /* &ClassLoad, */
487     &ClassPrepare,
488     NULL, /* &VMStart */
489     &Exception,
490     NULL, /* &ExceptionCatch, */
491     NULL, /* &SingleStep, */
492     NULL, /* &FramePop, */
493     &Breakpoint,
494     NULL, /* &FieldAccess, */
495     NULL, /* &FieldModification, */
496     &MethodEntry,
497     NULL, /* &MethodExit, */
498     NULL, /* &NativeMethodBind, */
499     NULL, /* &CompiledMethodLoad, */
500     NULL, /* &CompiledMethodUnload, */
501     NULL, /* &DynamicCodeGenerated, */
502     NULL, /* &DataDumpRequest, */
503     NULL,
504     NULL, /* &MonitorWait, */
505     NULL, /* &MonitorWaited, */
506     NULL, /* &MonitorContendedEnter, */
507     NULL, /* &MonitorContendedEntered, */
508     NULL,
509     NULL,
510     NULL,
511     NULL,
512     NULL, /* &GarbageCollectionStart, */
513     NULL, /* &GarbageCollectionFinish, */
514     NULL, /* &ObjectFree, */
515     NULL, /* &VMObjectAlloc, */
516 };
517
518
519 /*
520  * These are local overrides for various environment variables in Emacs.
521  * Please do not remove this and leave it at the end of the file, where
522  * Emacs will automagically detect them.
523  * ---------------------------------------------------------------------
524  * Local variables:
525  * mode: c
526  * indent-tabs-mode: t
527  * c-basic-offset: 4
528  * tab-width: 4
529  * End:
530  */