5e4d746b7c90cf119b08f027fb1fad1b1412eefc
[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    $Id: VMjdwp.c 6213 2006-12-18 17:36:06Z twisti $
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         /* XXX todo: get object instance - needs jvmti local variable support */
172         ev = (*jni_env)->NewObject(jni_env, breakpointclass, breakpointmid, 
173                                                                          thread, loc,NULL);
174         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
175         fprintf(stderr,"error calling breakpoint constructor\n");
176                 (*jni_env)->ExceptionDescribe(jni_env);
177                 return;
178         }
179
180         fprintf(stderr,"VMjdwp:Breakpoint: thread %p\n",thread);
181         fflush(stderr);
182
183         notify (jni_env,ev);    
184 }
185
186
187 static void MethodEntry (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
188                                                  jthread thread, jmethodID method) {
189         /* do not report gnu/classpath/jdwp method entries */
190 }
191
192
193 static void VMDeath (jvmtiEnv *jvmti_env,
194                      JNIEnv* jni_env) {
195   fprintf(stderr,"JVMTI-Event: IMPLEMENT ME!!!");
196 }
197
198
199 /* setup_jdwp_thread **********************************************************
200
201    Helper function to start JDWP listening thread
202
203 *******************************************************************************/
204
205 static void setup_jdwp_thread(JNIEnv* jni_env) {
206         jobject o;
207         jmethodID m;
208         jstring  s;
209
210         /* new gnu.classpath.jdwp.Jdwp() */
211         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"<init>","()V");
212     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
213         fprintf(stderr,"could not get Jdwp constructor\n");
214                 (*jni_env)->ExceptionDescribe(jni_env);
215                 exit(1); 
216         }
217         
218         o = (*jni_env)->NewObject(jni_env, Jdwpclass, m);
219     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
220         fprintf(stderr,"error calling Jdwp constructor\n");
221                 (*jni_env)->ExceptionDescribe(jni_env);
222                 exit(1); 
223         }
224
225         jdwpthread = (jthread)o;
226         
227         
228         /* configure(jdwpoptions) */
229         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"configure",
230                                                                 "(Ljava/lang/String;)V");
231     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
232         fprintf(stderr,"could not get Jdwp configure method\n");
233                 (*jni_env)->ExceptionDescribe(jni_env);
234                 exit(1); 
235         }
236
237         
238         s = (*jni_env)->NewStringUTF(jni_env,jdwpoptions);
239     if (s == NULL) {
240         fprintf(stderr,"could not get new java string from jdwp options\n");
241                 exit(1); 
242         }
243
244         free(jdwpoptions);
245         
246         (*jni_env)->CallVoidMethod(jni_env,o,m,s);
247     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
248         fprintf(stderr,"Exception occourred in Jdwp configure\n");
249                 (*jni_env)->ExceptionDescribe(jni_env);
250                 exit(1); 
251         }
252
253         m = (*jni_env)->GetMethodID(jni_env,Jdwpclass,"_doInitialization","()V");
254     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
255         fprintf(stderr,"could not get Jdwp _doInitialization method\n");
256                 (*jni_env)->ExceptionDescribe(jni_env);
257                 exit(1); 
258         }
259
260
261         (*jni_env)->CallVoidMethod(jni_env,o,m);
262     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
263         fprintf(stderr,"Exception occourred in Jdwp _doInitialization\n");
264                 (*jni_env)->ExceptionDescribe(jni_env);
265                 exit(1); 
266         }
267 }
268
269 #define FINDCLASSWITHEXCEPTION(CLASS,SIGNATURE) \
270         CLASS = (*jni_env)->FindClass(jni_env, SIGNATURE);     \
271         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {  \
272                 fprintf(stderr,"could not find %s\n", SIGNATURE);  \
273                 (*jni_env)->ExceptionDescribe(jni_env);            \
274                 exit(1);                                           \
275         }
276 #define GETMIDWITHEXCEPTION(CLASS, CLASSNAME, MID, METHODNAME, METHODSIG) \
277         FINDCLASSWITHEXCEPTION(CLASS, CLASSNAME);                             \
278         MID = (*jni_env)->GetMethodID(jni_env, CLASS, METHODNAME, METHODSIG); \
279     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {                 \
280         fprintf(stderr,"could not get %s %s\n",CLASSNAME, METHODNAME);    \
281                 (*jni_env)->ExceptionDescribe(jni_env);                           \
282                 exit(1);                                                          \
283         }
284
285
286 static void fillidcache(JNIEnv* jni_env) {
287         FINDCLASSWITHEXCEPTION(Jdwpclass, "gnu/classpath/jdwp/Jdwp");
288         
289         notifymid = (*jni_env)->
290          GetStaticMethodID(jni_env,Jdwpclass,
291                                            "notify","(Lgnu/classpath/jdwp/event/Event;)V");
292         if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
293                 fprintf(stderr,"could not get notify method\n");
294                 (*jni_env)->ExceptionDescribe(jni_env);
295                 exit(1); 
296         }
297
298         GETMIDWITHEXCEPTION(threadstartclass, 
299                                                 "gnu/classpath/jdwp/event/ThreadStartEvent", 
300                                                 threadstartmid, "<init>", "(Ljava/lang/Thread;)V");
301
302
303         GETMIDWITHEXCEPTION(threadendclass, 
304                                                 "gnu/classpath/jdwp/event/ThreadEndEvent", 
305                                                 threadendmid, "<init>", "(Ljava/lang/Thread;)V");
306
307
308         GETMIDWITHEXCEPTION(classprepareclass, 
309                                                 "gnu/classpath/jdwp/event/ClassPrepareEvent", 
310                                                 classpreparemid, "<init>", 
311                                                 "(Ljava/lang/Thread;Ljava/lang/Class;I)V");
312
313
314         GETMIDWITHEXCEPTION(vmmethodclass, "gnu/classpath/jdwp/VMMethod",
315                                                 vmmethodmid, "<init>", "(Ljava/lang/Class;J)V");
316
317         GETMIDWITHEXCEPTION(locationclass, "gnu/classpath/jdwp/util/Location",
318                                                 locationmid, "<init>", 
319                                                 "(Lgnu/classpath/jdwp/VMMethod;J)V");
320
321
322         GETMIDWITHEXCEPTION(
323                 breakpointclass, 
324                 "gnu/classpath/jdwp/event/BreakpointEvent", 
325                 breakpointmid, "<init>", 
326                 "(Ljava/lang/Thread;Lgnu/classpath/jdwp/util/Location;Ljava/lang/Object;)V");
327
328 }
329
330 static void VMInit (jvmtiEnv *jvmti_env, 
331                     JNIEnv* jni_env,
332                     jthread thread) {
333         jclass cl;
334         jmethodID m;
335         jobject eventobj;
336         jvmtiError err;
337
338         fprintf(stderr,"JDWP VMInit\n");
339
340         /* get needed jmethodIDs and jclasses for callbacks */
341         fillidcache(jni_env);
342
343         /* startup gnu classpath jdwp thread */
344         setup_jdwp_thread(jni_env);
345
346         fprintf(stderr,"JDWP listening thread started\n");
347
348         /* send VmInitEvent */
349     cl = (*jni_env)->FindClass(jni_env, 
350                                                                           "gnu/classpath/jdwp/event/VmInitEvent");
351     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
352         fprintf(stderr,"could not find class VMInitEvent\n");
353                 (*jni_env)->ExceptionDescribe(jni_env);
354                 exit(1); 
355         }
356
357         m = (*jni_env)->GetMethodID(jni_env,cl,"<init>",
358                                                                 "(Ljava/lang/Thread;)V");
359     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
360         fprintf(stderr,"could not get VmInitEvent constructor\n");
361                 (*jni_env)->ExceptionDescribe(jni_env);
362                 exit(1); 
363         }
364
365         eventobj = (*jni_env)->NewObject(jni_env, cl, m, thread);
366     if ((*jni_env)->ExceptionOccurred(jni_env) != NULL) {
367         fprintf(stderr,"error calling VmInitEvent constructor\n");
368                 (*jni_env)->ExceptionDescribe(jni_env);
369                 exit(1); 
370         }
371
372
373         notify (jni_env,eventobj);
374
375         if (suspend) {
376                 fprintf(stderr,"suspend initial thread\n");
377                 err = (*jvmti_env)->SuspendThread(jvmti_env,thread);
378                 printjvmtierror("error suspending initial thread",err);
379         }
380 }
381
382 static void usage() {   
383         puts("usage jdwp:[help]|(<option>=<value>),*");
384         puts("   transport=[dt_socket|...]");
385         puts("   address=<hostname:port>");
386         puts("   server=[y|n]");
387         puts("   suspend=[y|n]");
388 }
389
390 static bool processoptions(char *options) {
391         int i,len;
392         
393         if (strncmp(options,"help",4) == 0) {
394                 usage();
395                 return false;
396         }
397
398         suspend = true;         /* default value */
399
400
401         /* copy options for later use in java jdwp listen thread configure */
402         jdwpoptions = malloc(sizeof(char)*strlen(options));
403         strncpy(jdwpoptions, options, sizeof(char)*strlen(options));
404
405         len = strlen(options);
406         
407         i=0;
408         while (i<len) {
409                 if (strncmp("suspend=",&options[i],8)==0) {
410                         if (8>=strlen(&options[i])) {
411                                 if ((options[i+8]== 'y') || (options[i+8]== 'n')) {
412                                         suspend = options[i+8]== 'y';
413                                 } else {
414                                         printf("jdwp error argument: %s\n",options);
415                                         usage();
416                                         return -1;
417                                 }
418                         }
419                 } else {
420                         /* these options will be handled by jdwp java configure */
421                         if ((strncmp("transport=",options,10)==0) ||
422                                 (strncmp("server=",options,7)==0)) {
423                         } else {
424                                 printf("jdwp unkown argument: %s\n",options);
425                                 usage();
426                                 return false;
427                         }
428                 }
429                 while ((options[i]!=',')&&(i<len)) i++;
430                 i++;
431         }
432         return true;    
433 }
434
435
436 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { 
437         jint rc;
438         jvmtiCapabilities cap;
439         jvmtiError e;
440
441
442         fprintf(stderr,"jdwp Agent_OnLoad options: %s\n",options);
443         if (!processoptions(options)) return -1;
444         
445         rc = (*vm)->GetEnv(vm, (void**)&jvmtienv, JVMTI_VERSION_1_0);
446         if (rc != JNI_OK) {         
447                 fprintf(stderr, "jdwp: Unable to get jvmtiEnv error=%d\n", rc);
448                 return -1;              
449         }
450         
451         /* set eventcallbacks */
452         if (JVMTI_ERROR_NONE != 
453                 (e = (*jvmtienv)->SetEventCallbacks(jvmtienv,
454                                                                            &jvmti_jdwp_EventCallbacks,
455                                                                            sizeof(jvmtiEventCallbacks)))){
456                 printjvmtierror("jdwp: unable to setup event callbacks", e);
457                 return -1;
458         }
459
460         e = (*jvmtienv)->GetPotentialCapabilities(jvmtienv, &cap);
461         printjvmtierror("jdwp: unable to get potential capabilities", e);
462         if (e == JVMTI_ERROR_NONE) 
463                 e = (*jvmtienv)->AddCapabilities(jvmtienv, &cap);
464         if (e != JVMTI_ERROR_NONE) {
465                 printjvmtierror("jdwp: error adding jvmti capabilities", e);
466                 return -1;
467         }
468         
469         /* only enable needed events. VMVirtualMachine.registerEvent will  
470            be used to enable other events by need */
471         if (JVMTI_ERROR_NONE != (e = (*jvmtienv)->
472                                                          SetEventNotificationMode(jvmtienv, JVMTI_ENABLE,
473                                                                                                           JVMTI_EVENT_VM_INIT, 
474                                                                                                           NULL))) {
475                 printjvmtierror("jdwp unable to enable vm init callback",e);
476                 return -1;
477         }
478
479         return 0;
480 }
481         
482
483 jvmtiEventCallbacks jvmti_jdwp_EventCallbacks = {
484     &VMInit,
485     &VMDeath,
486     &ThreadStart,
487     &ThreadEnd,
488     NULL, /* &ClassFileLoadHook, */
489     NULL, /* &ClassLoad, */
490     &ClassPrepare,
491     NULL, /* &VMStart */
492     &Exception,
493     NULL, /* &ExceptionCatch, */
494     NULL, /* &SingleStep, */
495     NULL, /* &FramePop, */
496     &Breakpoint,
497     NULL, /* &FieldAccess, */
498     NULL, /* &FieldModification, */
499     &MethodEntry,
500     NULL, /* &MethodExit, */
501     NULL, /* &NativeMethodBind, */
502     NULL, /* &CompiledMethodLoad, */
503     NULL, /* &CompiledMethodUnload, */
504     NULL, /* &DynamicCodeGenerated, */
505     NULL, /* &DataDumpRequest, */
506     NULL,
507     NULL, /* &MonitorWait, */
508     NULL, /* &MonitorWaited, */
509     NULL, /* &MonitorContendedEnter, */
510     NULL, /* &MonitorContendedEntered, */
511     NULL,
512     NULL,
513     NULL,
514     NULL,
515     NULL, /* &GarbageCollectionStart, */
516     NULL, /* &GarbageCollectionFinish, */
517     NULL, /* &ObjectFree, */
518     NULL, /* &VMObjectAlloc, */
519 };
520
521
522 /*
523  * These are local overrides for various environment variables in Emacs.
524  * Please do not remove this and leave it at the end of the file, where
525  * Emacs will automagically detect them.
526  * ---------------------------------------------------------------------
527  * Local variables:
528  * mode: c
529  * indent-tabs-mode: t
530  * c-basic-offset: 4
531  * tab-width: 4
532  * End:
533  */