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