* classLoaderCollector: code indent and removed unused code
[cacao.git] / src / vm / jit / stacktrace.c
1 /* src/vm/jit/stacktrace.c
2
3    Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
4    R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
5    C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
6    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., 59 Temple Place - Suite 330, Boston, MA
23    02111-1307, USA.
24
25    Contact: cacao@complang.tuwien.ac.at
26
27    Authors: Joseph Wenninger
28
29    Changes: Christian Thalinger
30
31    $Id: stacktrace.c 2681 2005-06-14 17:14:44Z twisti $
32
33 */
34
35
36 #include <assert.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "asmoffsets.h"
41 #include "mm/boehm.h"
42 #include "native/native.h"
43 #include "vm/global.h"                   /* required here for native includes */
44 #include "native/include/java_lang_ClassLoader.h"
45 #include "toolbox/logging.h"
46 #include "vm/builtin.h"
47 #include "vm/class.h"
48 #include "vm/tables.h"
49 #include "vm/jit/codegen.inc.h"
50 #include "vm/loader.h"
51 #include "vm/stringlocal.h"
52 #include "vm/exceptions.h"
53
54 #undef JWDEBUG
55 #undef JWDEBUG2
56 #undef JWDEBUG3
57
58 /*JoWenn: simplify collectors (trace doesn't contain internal methods)*/
59
60 /* the line number is only u2, but to avoid alignment problems it is made the same size as a native
61         pointer. In the structures where this is used, values of -1 or -2 have a special meainging, so
62         if java bytecode is ever extended to support more than 65535 lines/file, this could will have to
63         be changed.*/
64
65 #if defined(_ALPHA_) || defined(__X86_64__)
66         #define LineNumber u8
67 #else
68         #define LineNumber u4
69 #endif
70
71 typedef struct lineNumberTableEntry {
72 /* The special value of -1 means that a inlined function starts, a value of -2 means that an inlined function ends*/
73         LineNumber lineNr;
74         u1 *pc;
75 } lineNumberTableEntry;
76
77 typedef struct lineNumberTableEntryInlineBegin {
78 /*this should have the same layout and size as the lineNumberTableEntry*/
79         LineNumber lineNrOuter;
80         methodinfo *method;
81 } lineNumberTableEntryInlineBegin;
82
83
84 typedef void(*CacaoStackTraceCollector)(void **,stackTraceBuffer*);
85
86 #define BLOCK_INITIALSIZE 40
87 #define BLOCK_SIZEINCREMENT 40
88
89 static void addEntry(stackTraceBuffer* buffer,methodinfo*method ,LineNumber line) {
90         if (buffer->size>buffer->full) {
91                 stacktraceelement *tmp=&(buffer->start[buffer->full]);
92                 tmp->method=method;
93                 tmp->linenumber=line;
94                 buffer->full = buffer->full + 1;
95 #if (defined(JWDEBUG) || defined (JWDEBUG2))
96                 log_text("addEntry (stacktrace):");
97                 printf("method %p\n",method);
98                 if (method) printf("method->name %p\n",method->name);
99                 if (method) utf_display(method->name); else printf("Native");
100                 if (method) {printf("\n");utf_display(method->class->name);}
101                 printf("\nnext buffer item %d\nLine:%ld\n",buffer->full,line);
102 #endif
103         } else {
104                 stacktraceelement *newBuffer;
105
106 #ifdef JWDEBUG
107                 log_text("stacktrace buffer full, resizing");
108 #endif
109
110                 newBuffer =
111                         (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
112                                                                                  sizeof(stacktraceelement));
113
114                 if (newBuffer==0) {
115                         log_text("OOM during stacktrace creation");
116                         assert(0);
117                 }
118
119                 memcpy(newBuffer,buffer->start,buffer->size*sizeof(stacktraceelement));
120                 if (buffer->needsFree) free(buffer->start);
121                 buffer->start=newBuffer;
122                 buffer->size=buffer->size+BLOCK_SIZEINCREMENT;
123                 buffer->needsFree=1;
124                 addEntry(buffer,method,line);
125         }
126 }
127
128 static int fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,methodinfo 
129                 *method,lineNumberTableEntry *startEntry, lineNumberTableEntry **entry, size_t *entriesAhead,u1 *adress) {
130
131         size_t ahead=*entriesAhead;
132         lineNumberTableEntry *ent=*entry;
133         lineNumberTableEntryInlineBegin *ilStart;
134
135         for (;ahead>0;ahead--,ent++) {
136                 if (adress>=ent->pc) {
137                         switch (ent->lineNr) {
138                                 case -1: /*begin of inlined method */
139                                         ilStart=(lineNumberTableEntryInlineBegin*)(++ent);
140                                         ent ++;
141                                         ahead--; ahead--;
142                                         if (fillInStackTrace_methodRecursive(buffer,ilStart->method,ent,&ent,&ahead,adress)) {
143                                                 addEntry(buffer,method,ilStart->lineNrOuter);
144                                                 return 1;
145                                         }
146                                         break;
147                                 case -2: /*end of inlined method*/
148                                         *entry=ent;
149                                         *entriesAhead=ahead;
150                                         return 0;
151                                         break;
152                                 default:
153                                         if (adress==ent->pc) {
154                                                 addEntry(buffer,method,ent->lineNr);
155                                                 return 1;
156                                         }
157                                         break;
158                         }
159                 } else {
160                         if (adress>startEntry->pc) {
161                                 ent--;
162                                 addEntry(buffer,method,ent->lineNr);
163                                 return 1;       
164                         } else {
165 #ifdef JWDEBUG
166                                 printf("trace point: %p\n",adress);
167 #endif
168                                 log_text("trace point before method");
169                                 assert(0);
170                         }
171                 }
172         }
173         ent--;
174         addEntry(buffer,method,ent->lineNr);
175         return 1;
176         
177 }
178
179 static void fillInStackTrace_method(stackTraceBuffer *buffer,methodinfo *method,u1 *dataSeg, u1* adress) {
180         size_t lineNumberTableSize=(*((size_t*)(dataSeg+LineNumberTableSize)));
181
182
183         if ( lineNumberTableSize == 0) {
184                 /*right now this happens only on 
185                 i386,if the native stub causes an exception in a <clinit> invocation (jowenn)*/
186                 addEntry(buffer,method,0);
187                 return;
188         } else {
189                 lineNumberTableEntry *ent; /*=(lineNumberTableEntry*) ((*((char**)(dataSeg+LineNumberTableStart))) - (sizeof(lineNumberTableEntry)-sizeof(void*)));*/
190                 void **calc;
191                 lineNumberTableEntry *startEntry;
192
193                 /*              printf("dataSeg: %p\n",dataSeg);*/
194                 calc=(void**)(dataSeg+LineNumberTableStart);
195                 /*              printf("position of line number table start reference in data segment: %p\n",calc);
196                                 printf("line number table start as found in table: %p\n",*calc);*/
197                 ent=(lineNumberTableEntry *) (((char*)(*calc) - (sizeof(lineNumberTableEntry)-sizeof(void*))));
198                 /*              printf("line number table start as calculated: %p\n",ent);*/
199                 ent-=(lineNumberTableSize-1);
200                 startEntry=ent;
201                 /*              printf("line number table real start (bottom end) as calculated(2): %p\n",startEntry);*/
202
203                 if (!fillInStackTrace_methodRecursive(buffer,method,startEntry,&ent,&lineNumberTableSize,adress)) {
204                         log_text("Trace point not found in suspected method");
205                         assert(0);
206                 }
207         }
208 }
209
210
211 typedef union {
212         u1*         u1ptr;
213         functionptr funcptr;
214         void*       voidptr;
215 } u1ptr_functionptr_union;
216
217 #define cast_funcptr_u1ptr(dest,src) \
218         { u1f.funcptr=src;\
219         dest=u1f.u1ptr; }       
220
221 #define cast_u1ptr_funcptr(dest,src) \
222         { u1f.u1ptr=src;\
223         dest=u1f.funcptr; }     
224
225 void  cacao_stacktrace_fillInStackTrace(void **target,CacaoStackTraceCollector coll)
226 {
227
228         stacktraceelement primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)]; 
229                 /*In most cases this should be enough -> one malloc less. I don't think temporary data should be
230                 allocated with the GC, only the result*/
231         stackTraceBuffer buffer;
232         buffer.needsFree=0;
233         buffer.start=primaryBlock;
234         buffer.size=BLOCK_INITIALSIZE; /*  *sizeof(stacktraceelement); */
235         buffer.full=0;
236 #ifdef JWDEBUG
237         log_text("entering cacao_stacktrace_fillInStacktrace");
238         {
239                 int i=0;
240                 struct native_stackframeinfo *tmpinfo;
241                 for (tmpinfo=(*(((void**)(builtin_asm_get_stackframeinfo()))));tmpinfo;tmpinfo=tmpinfo->oldThreadspecificHeadValue)
242                         i++;
243                 printf("native function depth:%d\n",i);
244                 
245         }
246 #endif
247         {
248                 struct native_stackframeinfo *info=(*(((void**)(builtin_asm_get_stackframeinfo()))));
249                 if (!info) {
250                         log_text("info ==0");
251                         *target=0;
252                         return;
253                 } else {
254                         u1 *tmp;
255                         u1 *dataseg; /*make it byte addressable*/
256                         methodinfo *currentMethod=0;
257                         /*void*/ functionptr returnAdress;
258                         u1* stackPtr;
259                         u1ptr_functionptr_union u1f;
260
261 /*                      utf_display(info->method->class->name);
262                         utf_display(info->method->name);*/
263                         
264                         while ((currentMethod!=0) ||  (info!=0)) {
265                                 if (currentMethod==0) { /*some builtin native */
266                                         currentMethod=info->method;
267                                         returnAdress=(functionptr)info->returnToFromNative;
268 #ifdef JWDEBUG
269                                         log_text("native");
270                                         printf("return to %p\n",returnAdress);
271 #endif
272                                         if (currentMethod) {
273 #ifdef JWDEBUG
274                                                 log_text("real native (not an internal helper)\n");
275                                                 printf("returnaddress %p, methodpointer %p, stackframe begin %p\n",info->returnToFromNative,info->method, info->beginOfJavaStackframe);
276 #if 0
277                                                 utf_display(currentMethod->class->name);
278                                                 utf_display(currentMethod->name);
279 #endif
280 #endif
281                                                 addEntry(&buffer,currentMethod,0);
282                                         }
283 #if defined(__ALPHA__)
284                                         if (info->savedpv!=0)
285                                                 dataseg=info->savedpv;
286                                         else
287                                                 dataseg=codegen_findmethod(returnAdress);
288 #elif defined(__I386__) || defined (__X86_64__)
289                                         cast_funcptr_u1ptr(dataseg,codegen_findmethod(returnAdress));
290 #endif
291                                         currentMethod=(*((methodinfo**)(dataseg+MethodPointer)));
292                                         if (info->beginOfJavaStackframe==0)
293                                                 stackPtr=((u1*)info)+sizeof(native_stackframeinfo);
294                                         else
295 #if defined(__ALPHA__)
296                                                 stackPtr=(u1*)(info->beginOfJavaStackframe);
297 #elif defined(__I386__) || defined (__X86_64__)
298                                                 stackPtr=(u1*)(info->beginOfJavaStackframe)+sizeof(void*);
299 #endif
300                                         info=info->oldThreadspecificHeadValue;
301                                 } else { /*method created by jit*/
302                                         u4 frameSize;
303 #ifdef JWDEBUG
304                                         log_text("JIT");
305 #endif
306 #if defined (__ALPHA__)
307                                         if (currentMethod->isleafmethod) {
308 #ifdef JWDEBUG
309                                                 printf("class.method:%s.%s\n",currentMethod->class->name->text,currentMethod->name->text);
310 #endif
311                                                 log_text("How could that happen ??? A leaf method in the middle of a stacktrace ??");
312                                                 assert(0);
313                                         }
314 #endif
315                                         /*utf_display(currentMethod->class->name);
316                                         utf_display(currentMethod->name);*/
317                                         cast_funcptr_u1ptr(tmp,returnAdress);
318                                         fillInStackTrace_method(&buffer,currentMethod,dataseg,tmp-1);
319                                         frameSize=*((u4*)(dataseg+FrameSize));
320 #if defined(__ALPHA__)
321                                         /* cacao saves the return adress as the first element of the stack frame on alphas*/
322                                         cast_funcptr_u1ptr (dataseg,codegen_findmethod(*((void**)(stackPtr+frameSize-sizeof(void*)))));
323                                         returnAdress=(*((void**)(stackPtr+frameSize-sizeof(void*))));
324 #elif defined(__I386__) || defined (__x86_64__)
325                                         /* on i386 the return adress is the first element before the stack frme*/
326                                         cast_u1ptr_funcptr(returnAdress,*((u1**)(stackPtr+frameSize)));
327                                         cast_funcptr_u1ptr(dataseg,codegen_findmethod(returnAdress));
328 #endif
329 /*                                      printf ("threadrootmethod %p\n",builtin_asm_get_threadrootmethod());
330                                         if (currentMethod==builtin_asm_get_threadrootmethod()) break;*/
331                                         currentMethod=(*((methodinfo**)(dataseg+MethodPointer)));
332 #if defined(__ALPHA__)
333                                         stackPtr+=frameSize;
334 #elif defined(__I386__) || defined (__x86_64__)
335                                         stackPtr+=frameSize+sizeof(void*);
336 #endif
337                                 }
338                         }
339                         
340                         if (coll) coll(target,&buffer);
341                         if (buffer.needsFree) free(buffer.start);
342 #ifdef JWDEBUG
343                         log_text("leaving cacao_stacktrace_fillInStacktrace");
344 #endif
345
346                         return;
347                 }
348                 /*log_text("\n=========================================================");*/
349         }
350         *target=0;
351 #ifdef JWDEBUG
352                 log_text("leaving(2) cacao_stacktrace_fillInStacktrace");
353 #endif
354
355 }
356
357
358 static
359 void stackTraceCollector(void **target, stackTraceBuffer *buffer) {
360         stackTraceBuffer *dest=*target=heap_allocate(sizeof(stackTraceBuffer)+buffer->full*sizeof(stacktraceelement),true,0);
361         memcpy(*target,buffer,sizeof(stackTraceBuffer));
362         memcpy(dest+1,buffer->start,buffer->full*sizeof(stacktraceelement));
363
364         dest->needsFree=0;
365         dest->size=dest->full;
366         dest->start=(stacktraceelement*)(dest+1);
367
368         /*
369         if (buffer->full>0) {
370                 printf("SOURCE BUFFER:%s\n",buffer->start[0].method->name->text);
371                 printf("DEST BUFFER:%s\n",dest->start[0].method->name->text);
372         } else printf("Buffer is empty\n");
373         */
374 }
375
376
377 void  cacao_stacktrace_NormalTrace(void **target) {
378         cacao_stacktrace_fillInStackTrace(target,&stackTraceCollector);
379 }
380
381
382
383 static
384 void classContextCollector(void **target, stackTraceBuffer *buffer) {
385         java_objectarray *tmpArray;
386         int i;
387         stacktraceelement *current;
388         stacktraceelement *start;
389         size_t size;
390         size_t targetSize;
391
392         size=buffer->full;
393         targetSize=0;
394         for (i=0;i<size;i++)
395                 if (buffer->start[i].method!=0) targetSize++;
396         start=buffer->start;
397         start++;
398         targetSize--;
399         if (targetSize > 0) {
400                 if ((start->method) && (start->method->class== class_java_lang_SecurityManager)) {
401 #if (defined(JWDEBUG) || defined(JWDEBUG3))
402                         printf("removing one frame");
403 #endif
404                         targetSize--;
405                         start++;
406                 }
407         }
408
409         tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
410
411 #if (defined(JWDEBUG) || defined(JWDEBUG3))
412         printf("==========================================================================================================\n");
413 #endif
414         for(i = 0, current = start; i < targetSize; i++, current++) {
415                 if (current->method==0) { i--; /*printf("Skipping\n");*/ continue;}
416 #if (defined(JWDEBUG) || defined(JWDEBUG3))
417                 {
418                         printf("after current->method check\n");
419                         if (current->method->class==0) printf("Error method defining class i null\n");
420                         else printf("method defining class is not null :)\n");
421                         printf("adding item to class context array:%s\n",current->method->class->name->text);
422                         printf("method for class: :%s\n",current->method->name->text);
423                 }
424 #endif
425                 use_class_as_object(current->method->class);
426 #ifdef JWDEBUG
427                 {
428                         printf("use_class_as_object_finished\n");
429                 }
430 #endif
431                 tmpArray->data[i] = (java_objectheader *) current->method->class;
432 #ifdef JWDEBUG
433                 printf("array item has been set\n");
434 #endif
435         }
436 #if (defined(JWDEBUG) || defined(JWDEBUG3))
437         printf("leaving classContextCollector");
438 #endif
439         *target=tmpArray;
440 }
441
442
443
444 java_objectarray *cacao_createClassContextArray() {
445         java_objectarray *array=0;
446         cacao_stacktrace_fillInStackTrace((void**)&array,&classContextCollector);
447         return array;
448         
449 }
450
451
452 static void classLoaderCollector(void **target, stackTraceBuffer *buffer)
453 {
454         stacktraceelement *current;
455         stacktraceelement *start;
456         methodinfo        *m;
457         ptrint             size;
458         s4                 i;
459
460         size = buffer->full;
461         start = &(buffer->start[0]);
462
463         for(i = 0, current = start; i < size; i++, current++) {
464                 m = current->method;
465
466                 if (!m)
467                         continue;
468
469                 if (m->class == class_java_security_PrivilegedAction) {
470                         *target = NULL;
471                         return;
472                 }
473
474                 if (m->class->classloader) {
475                         *target = (java_lang_ClassLoader *) m->class->classloader;
476                         return;
477                 }
478         }
479
480         *target = NULL;
481 }
482
483
484 java_objectheader *cacao_currentClassLoader() {
485         java_objectheader *header=0;
486         cacao_stacktrace_fillInStackTrace((void**)&header,&classLoaderCollector);
487         return header;
488 }
489
490
491 static
492 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {  
493         if (buffer->full >2) (*target)=buffer->start[2].method;
494         else (*target=0);
495 }
496
497 methodinfo *cacao_callingMethod() {
498         methodinfo *method;
499         cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
500         return method;
501 }
502
503
504 static
505 void getStackCollector(void **target, stackTraceBuffer *buffer)
506 {
507         java_objectarray *classes;
508         java_objectarray *methodnames;
509         java_objectarray **result=(java_objectarray**)target;
510         java_lang_String *str;
511         classinfo *c;
512         stacktraceelement *current;
513         int i,size;
514
515         /*log_text("getStackCollector");*/
516
517         size = buffer->full;
518
519         *result = builtin_anewarray(2, arrayclass_java_lang_Object);
520
521         if (!(*result))
522                 return;
523
524         classes = builtin_anewarray(size, class_java_lang_Class);
525
526         if (!classes)
527                 return;
528
529         methodnames = builtin_anewarray(size, class_java_lang_String);
530
531         if (!methodnames)
532                 return;
533
534         (*result)->data[0] = (java_objectheader *) classes;
535         (*result)->data[1] = (java_objectheader *) methodnames;
536
537         /*log_text("Before for loop");*/
538         for (i = 0, current = &(buffer->start[0]); i < size; i++, current++) {
539                 /*log_text("In loop");*/
540                 c = current->method->class;
541                 use_class_as_object(c);
542                 classes->data[i] = (java_objectheader *) c;
543                 str = javastring_new(current->method->name);
544                 if (!str)
545                         return;
546                 methodnames->data[i] = (java_objectheader *) str;
547                 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
548         }
549
550         /*if (*exceptionptr) panic("Exception in getStackCollector");*/
551
552         /*log_text("loop left");*/
553         return;
554
555 }
556
557
558 java_objectarray *cacao_getStackForVMAccessController()
559 {
560         java_objectarray *result=0;
561         cacao_stacktrace_fillInStackTrace((void**)&result,&getStackCollector);
562         return result;
563 }
564
565
566 /*
567  * These are local overrides for various environment variables in Emacs.
568  * Please do not remove this and leave it at the end of the file, where
569  * Emacs will automagically detect them.
570  * ---------------------------------------------------------------------
571  * Local variables:
572  * mode: c
573  * indent-tabs-mode: t
574  * c-basic-offset: 4
575  * tab-width: 4
576  * End:
577  */