* Some improvements
[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 2954 2005-07-09 13:49:50Z twisti $
32
33 */
34
35
36 #include <assert.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "config.h"
41 #include "asmoffsets.h"
42
43 #include "mm/boehm.h"
44 #include "native/native.h"
45
46 #include "vm/global.h"                   /* required here for native includes */
47 #include "native/include/java_lang_ClassLoader.h"
48
49 #include "toolbox/logging.h"
50 #include "vm/builtin.h"
51 #include "vm/class.h"
52 #include "vm/exceptions.h"
53 #include "vm/loader.h"
54 #include "vm/stringlocal.h"
55 #include "vm/tables.h"
56 #include "vm/jit/asmpart.h"
57 #include "vm/jit/codegen.inc.h"
58
59
60 #undef JWDEBUG
61 #undef JWDEBUG2
62 #undef JWDEBUG3
63
64 /*JoWenn: simplify collectors (trace doesn't contain internal methods)*/
65
66 /* the line number is only u2, but to avoid alignment problems it is made the same size as a native
67         pointer. In the structures where this is used, values of -1 or -2 have a special meainging, so
68         if java bytecode is ever extended to support more than 65535 lines/file, this could will have to
69         be changed.*/
70
71 #if defined(_ALPHA_) || defined(__X86_64__)
72         #define LineNumber u8
73 #else
74         #define LineNumber u4
75 #endif
76
77 typedef struct lineNumberTableEntry {
78 /* The special value of -1 means that a inlined function starts, a value of -2 means that an inlined function ends*/
79         LineNumber lineNr;
80         u1 *pc;
81 } lineNumberTableEntry;
82
83 typedef struct lineNumberTableEntryInlineBegin {
84 /*this should have the same layout and size as the lineNumberTableEntry*/
85         LineNumber lineNrOuter;
86         methodinfo *method;
87 } lineNumberTableEntryInlineBegin;
88
89
90 typedef void(*CacaoStackTraceCollector)(void **,stackTraceBuffer*);
91
92 #define BLOCK_INITIALSIZE 40
93 #define BLOCK_SIZEINCREMENT 40
94
95
96 /* stacktrace_create_inline_stackframeinfo *************************************
97
98    Creates an stackframe info structure for an inline exception.
99
100 *******************************************************************************/
101
102 void stacktrace_create_inline_stackframeinfo(stackframeinfo *sfi, u1 *pv,
103                                                                                          u1 *sp, functionptr ra)
104 {
105         void **osfi;
106
107         /* get current stackframe info pointer */
108
109         osfi = builtin_asm_get_stackframeinfo();
110
111 #if defined(__I386__) || defined(__X86_64__)
112         /* we don't have pv in asm_wrapper_patcher handy */
113
114         if (pv == NULL)
115                 pv = (u1 *) codegen_findmethod(ra);
116 #endif
117
118         /* fill new stackframe info structure */
119
120         sfi->oldThreadspecificHeadValue = *osfi;
121         sfi->addressOfThreadspecificHead = osfi;
122         sfi->method = NULL;
123         sfi->pv = pv;
124         sfi->beginOfJavaStackframe = sp;
125         sfi->returnToFromNative = ra;
126
127         /* store new stackframe info pointer */
128
129         *osfi = sfi;
130 }
131
132
133 /* stacktrace_create_native_stackframeinfo *************************************
134
135    Creates a stackframe info structure for a native stub.
136
137 *******************************************************************************/
138
139 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
140                                                                                          u1 *sp, functionptr ra)
141 {
142         void       **osfi;
143         methodinfo  *m;
144
145         /* get methodinfo pointer from data segment */
146
147         m = *((methodinfo **) (pv + MethodPointer));
148
149         /* get current stackframe info pointer */
150
151         osfi = builtin_asm_get_stackframeinfo();
152
153         /* fill new stackframe info structure */
154
155         sfi->oldThreadspecificHeadValue = *osfi;
156         sfi->addressOfThreadspecificHead = osfi;
157         sfi->method = m;
158         sfi->pv = NULL;
159         sfi->beginOfJavaStackframe = sp;
160         sfi->returnToFromNative = ra;
161
162         /* store new stackframe info pointer */
163
164         *osfi = sfi;
165 }
166
167
168 /* stacktrace_remove_stackframeinfo ********************************************
169
170    XXX
171
172 *******************************************************************************/
173
174 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
175 {
176         void **osfi;
177
178         /* get address of pointer */
179
180         osfi = sfi->addressOfThreadspecificHead;
181
182         /* restore the old pointer */
183
184         *osfi = sfi->oldThreadspecificHeadValue;
185 }
186
187
188 /* stacktrace_call_fillInStackTrace ********************************************
189
190    XXX
191
192 *******************************************************************************/
193
194 void stacktrace_call_fillInStackTrace(java_objectheader *o)
195 {
196         methodinfo *m;
197
198         /* resolve methodinfo pointer from exception object */
199
200         m = class_resolvemethod(o->vftbl->class,
201                                                         utf_fillInStackTrace,
202                                                         utf_void__java_lang_Throwable);
203
204         /* call function */
205
206         asm_calljavafunction(m, o, NULL, NULL, NULL);
207 }
208
209
210 static void addEntry(stackTraceBuffer* buffer,methodinfo*method ,LineNumber line) {
211         if (buffer->size>buffer->full) {
212                 stacktraceelement *tmp=&(buffer->start[buffer->full]);
213                 tmp->method=method;
214                 tmp->linenumber=line;
215                 buffer->full = buffer->full + 1;
216 #if (defined(JWDEBUG) || defined (JWDEBUG2))
217                 log_text("addEntry (stacktrace):");
218                 printf("method %p\n",method);
219                 if (method) printf("method->name %p\n",method->name);
220                 if (method) utf_display(method->name); else printf("Native");
221                 if (method) {printf("\n");utf_display(method->class->name);}
222                 printf("\nnext buffer item %d\nLine:%ld\n",buffer->full,line);
223 #endif
224         } else {
225                 stacktraceelement *newBuffer;
226
227 #ifdef JWDEBUG
228                 log_text("stacktrace buffer full, resizing");
229 #endif
230
231                 newBuffer =
232                         (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
233                                                                                  sizeof(stacktraceelement));
234
235                 if (newBuffer==0) {
236                         log_text("OOM during stacktrace creation");
237                         assert(0);
238                 }
239
240                 memcpy(newBuffer,buffer->start,buffer->size*sizeof(stacktraceelement));
241                 if (buffer->needsFree) free(buffer->start);
242                 buffer->start=newBuffer;
243                 buffer->size=buffer->size+BLOCK_SIZEINCREMENT;
244                 buffer->needsFree=1;
245                 addEntry(buffer,method,line);
246         }
247 }
248
249
250 /* stacktrace_fillInStackTrace_methodRecursive *********************************
251
252    XXX
253
254 *******************************************************************************/
255
256 static int stacktrace_fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,
257                                                                                                            methodinfo *method,
258                                                                                                            lineNumberTableEntry *startEntry,
259                                                                                                            lineNumberTableEntry **entry,
260                                                                                                            size_t *entriesAhead,
261                                                                                                            u1 *address)
262 {
263
264         size_t ahead=*entriesAhead;
265         lineNumberTableEntry *ent=*entry;
266         lineNumberTableEntryInlineBegin *ilStart;
267
268         for (; ahead > 0; ahead--, ent++) {
269                 if (address >= ent->pc) {
270                         switch (ent->lineNr) {
271                         case -1: /* begin of inlined method */
272                                 ilStart=(lineNumberTableEntryInlineBegin*)(++ent);
273                                 ent ++;
274                                 ahead--; ahead--;
275                                 if (stacktrace_fillInStackTrace_methodRecursive(buffer,ilStart->method,ent,&ent,&ahead,address)) {
276                                         addEntry(buffer,method,ilStart->lineNrOuter);
277                                         return 1;
278                                 }
279                                 break;
280                         case -2: /* end of inlined method */
281                                 *entry=ent;
282                                 *entriesAhead=ahead;
283                                 return 0;
284                                 break;
285                         default:
286                                 if (address == ent->pc) {
287                                         addEntry(buffer, method, ent->lineNr);
288                                         return 1;
289                                 }
290                                 break;
291                         }
292
293                 } else {
294                         if (address > startEntry->pc) {
295                                 ent--;
296                                 addEntry(buffer, method, ent->lineNr);
297                                 return 1;
298
299                         } else {
300                                 printf("trace point: %p\n", address);
301                                 log_text("trace point before method");
302                                 assert(0);
303                         }
304                 }
305         }
306
307         ent--;
308         addEntry(buffer, method, ent->lineNr);
309         return 1;
310 }
311
312
313 /* stacktrace_fillInStackTrace_method ******************************************
314
315    XXX
316
317 *******************************************************************************/
318
319 static void stacktrace_fillInStackTrace_method(stackTraceBuffer *buffer,
320                                                                                            methodinfo *method, u1 *dataSeg,
321                                                                                            u1 *address)
322 {
323         size_t lineNumberTableSize=(*((size_t*)(dataSeg+LineNumberTableSize)));
324         lineNumberTableEntry *ent;
325         void **calc;
326         lineNumberTableEntry *startEntry;
327
328         if (lineNumberTableSize == 0) {
329                 /* right now this happens only on i386,if the native stub causes an */
330                 /* exception in a <clinit> invocation (jowenn) */
331
332                 addEntry(buffer, method, 0);
333                 return;
334
335         } else {
336                 calc = (void **) (dataSeg + LineNumberTableStart);
337                 ent = (lineNumberTableEntry *) (((char *) (*calc) - (sizeof(lineNumberTableEntry) - SIZEOF_VOID_P)));
338
339                 ent -= (lineNumberTableSize - 1);
340                 startEntry = ent;
341
342                 if (!stacktrace_fillInStackTrace_methodRecursive(buffer, method,
343                                                                                                                  startEntry, &ent,
344                                                                                                                  &lineNumberTableSize,
345                                                                                                                  address)) {
346                         log_text("Trace point not found in suspected method");
347                         assert(0);
348                 }
349         }
350 }
351
352
353 /* cacao_stacktrace_fillInStackTrace *******************************************
354
355    XXX
356
357 *******************************************************************************/
358
359 void cacao_stacktrace_fillInStackTrace(void **target,
360                                                                            CacaoStackTraceCollector coll)
361 {
362         stacktraceelement      primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)];
363         stackTraceBuffer  buffer;
364         stackframeinfo   *info;
365         methodinfo       *m;
366         u1               *pv;
367         u1               *sp;
368         u4                framesize;
369         functionptr       ra;
370
371
372         /* In most cases this should be enough -> one malloc less. I don't think  */
373         /* temporary data should be allocated with the GC, only the result.       */
374
375         buffer.needsFree = 0;
376         buffer.start = primaryBlock;
377         buffer.size = BLOCK_INITIALSIZE; /*  *sizeof(stacktraceelement); */
378         buffer.full = 0;
379
380         info = *((void **) builtin_asm_get_stackframeinfo());
381
382         if (!info) {
383                 *target = NULL;
384                 return;
385
386         } else {
387                 m = NULL;
388
389                 while (m || info) {
390                         /* some builtin native */
391
392                         if (m == NULL) {
393                                 m = info->method;
394                                 ra = (functionptr) info->returnToFromNative;
395
396 #if 0
397                                 if (m) {
398                                         utf_display_classname(m->class->name);
399                                         printf(".");
400                                         utf_display(m->name);
401                                         utf_display(m->descriptor);
402                                         printf(": native\n");
403
404                                         addEntry(&buffer, m, 0);
405                                 } else {
406                                         printf("NULL: native\n");
407                                 }
408 #else
409                                 if (m) {
410                                         addEntry(&buffer, m, 0);
411                                 }
412 #endif
413
414                                 /* get data segment address */
415
416                                 if (info->pv) {
417                                         /* this is an inline info */
418
419                                         pv = info->pv;
420
421                                 } else {
422                                         /* this is an native stub info */
423
424                                         pv = (u1 *) codegen_findmethod(ra);
425                                 }
426
427                                 /* get methodinfo pointer from data segment */
428
429                                 m = *((methodinfo **) (pv + MethodPointer));
430
431                                 /* get stackpointer from stackframeinfo structure */
432
433                                 sp = (u1 *) info->beginOfJavaStackframe;
434
435                                 info = info->oldThreadspecificHeadValue;
436
437                         } else {
438                                 /* JIT method */
439
440 #if 0
441                                 utf_display_classname(m->class->name);
442                                 printf(".");
443                                 utf_display(m->name);
444                                 utf_display(m->descriptor);
445                                 printf(": JIT\n");
446 #endif
447
448                                 /* add the current method to the stacktrace */
449
450                                 stacktrace_fillInStackTrace_method(&buffer, m, pv,
451                                                                                                    ((u1 *) ra) - 1);
452
453                                 /* get the current stack frame size */
454
455                                 framesize = *((u4 *) (pv + FrameSize));
456
457                                 /* get return address of current stack frame */
458
459                                 ra = md_stacktrace_get_returnaddress(sp, framesize);
460
461                                 /* get data segment and methodinfo pointer from parent method */
462
463                                 pv = (u1 *) codegen_findmethod(ra);
464                                 m = *((methodinfo **) (pv + MethodPointer));
465
466                                 /* walk the stack */
467
468 #if defined(__I386__) || defined (__X86_64__)
469                                 sp += framesize + SIZEOF_VOID_P;
470 #else
471                                 sp += framesize;
472 #endif
473                         }
474                 }
475                         
476                 if (coll)
477                         coll(target, &buffer);
478
479                 if (buffer.needsFree)
480                         free(buffer.start);
481
482                 return;
483         }
484
485         *target = NULL;
486 }
487
488
489 static
490 void stackTraceCollector(void **target, stackTraceBuffer *buffer) {
491         stackTraceBuffer *dest=*target=heap_allocate(sizeof(stackTraceBuffer)+buffer->full*sizeof(stacktraceelement),true,0);
492         memcpy(*target,buffer,sizeof(stackTraceBuffer));
493         memcpy(dest+1,buffer->start,buffer->full*sizeof(stacktraceelement));
494
495         dest->needsFree=0;
496         dest->size=dest->full;
497         dest->start=(stacktraceelement*)(dest+1);
498
499         /*
500         if (buffer->full>0) {
501                 printf("SOURCE BUFFER:%s\n",buffer->start[0].method->name->text);
502                 printf("DEST BUFFER:%s\n",dest->start[0].method->name->text);
503         } else printf("Buffer is empty\n");
504         */
505 }
506
507
508 void cacao_stacktrace_NormalTrace(void **target)
509 {
510         cacao_stacktrace_fillInStackTrace(target, &stackTraceCollector);
511 }
512
513
514
515 static void classContextCollector(void **target, stackTraceBuffer *buffer)
516 {
517         java_objectarray  *tmpArray;
518         stacktraceelement *current;
519         stacktraceelement *start;
520         size_t size;
521         size_t targetSize;
522         s4 i;
523
524         size = buffer->full;
525         targetSize = 0;
526
527         for (i = 0; i < size; i++)
528                 if (buffer->start[i].method != 0)
529                         targetSize++;
530
531         start = buffer->start;
532         start++;
533         targetSize--;
534
535         if (targetSize > 0) {
536                 if (start->method && (start->method->class == class_java_lang_SecurityManager)) {
537                         targetSize--;
538                         start++;
539                 }
540         }
541
542         tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
543
544         for(i = 0, current = start; i < targetSize; i++, current++) {
545                 /* XXX TWISTI: should we use this skipping for native stubs? */
546
547                 if (!current->method) {
548                         i--;
549                         continue;
550                 }
551
552                 use_class_as_object(current->method->class);
553
554                 tmpArray->data[i] = (java_objectheader *) current->method->class;
555         }
556
557         *target = tmpArray;
558 }
559
560
561
562 java_objectarray *cacao_createClassContextArray(void)
563 {
564         java_objectarray *array=0;
565
566         cacao_stacktrace_fillInStackTrace((void **) &array, &classContextCollector);
567
568         return array;
569 }
570
571
572 /* stacktrace_classLoaderCollector *********************************************
573
574    XXX
575
576 *******************************************************************************/
577
578 static void stacktrace_classLoaderCollector(void **target,
579                                                                                         stackTraceBuffer *buffer)
580 {
581         stacktraceelement *current;
582         stacktraceelement *start;
583         methodinfo        *m;
584         ptrint             size;
585         s4                 i;
586
587         size = buffer->full;
588         start = &(buffer->start[0]);
589
590         for(i = 0, current = start; i < size; i++, current++) {
591                 m = current->method;
592
593                 if (!m)
594                         continue;
595
596                 if (m->class == class_java_security_PrivilegedAction) {
597                         *target = NULL;
598                         return;
599                 }
600
601                 if (m->class->classloader) {
602                         *target = (java_lang_ClassLoader *) m->class->classloader;
603                         return;
604                 }
605         }
606
607         *target = NULL;
608 }
609
610
611 /* cacao_currentClassLoader ****************************************************
612
613    XXX
614
615 *******************************************************************************/
616
617 java_objectheader *cacao_currentClassLoader(void)
618 {
619         java_objectheader *header=0;
620
621         cacao_stacktrace_fillInStackTrace((void**)&header,
622                                                                           &stacktrace_classLoaderCollector);
623
624         return header;
625 }
626
627
628 static
629 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {  
630         if (buffer->full >2) (*target)=buffer->start[2].method;
631         else (*target=0);
632 }
633
634 methodinfo *cacao_callingMethod() {
635         methodinfo *method;
636         cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
637         return method;
638 }
639
640
641 static
642 void getStackCollector(void **target, stackTraceBuffer *buffer)
643 {
644         java_objectarray *classes;
645         java_objectarray *methodnames;
646         java_objectarray **result=(java_objectarray**)target;
647         java_lang_String *str;
648         classinfo *c;
649         stacktraceelement *current;
650         int i,size;
651
652         /*log_text("getStackCollector");*/
653
654         size = buffer->full;
655
656         *result = builtin_anewarray(2, arrayclass_java_lang_Object);
657
658         if (!(*result))
659                 return;
660
661         classes = builtin_anewarray(size, class_java_lang_Class);
662
663         if (!classes)
664                 return;
665
666         methodnames = builtin_anewarray(size, class_java_lang_String);
667
668         if (!methodnames)
669                 return;
670
671         (*result)->data[0] = (java_objectheader *) classes;
672         (*result)->data[1] = (java_objectheader *) methodnames;
673
674         /*log_text("Before for loop");*/
675         for (i = 0, current = &(buffer->start[0]); i < size; i++, current++) {
676                 /*log_text("In loop");*/
677                 c = current->method->class;
678                 use_class_as_object(c);
679                 classes->data[i] = (java_objectheader *) c;
680                 str = javastring_new(current->method->name);
681                 if (!str)
682                         return;
683                 methodnames->data[i] = (java_objectheader *) str;
684                 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
685         }
686
687         /*if (*exceptionptr) panic("Exception in getStackCollector");*/
688
689         /*log_text("loop left");*/
690         return;
691
692 }
693
694
695 java_objectarray *cacao_getStackForVMAccessController(void)
696 {
697         java_objectarray *result = NULL;
698
699         cacao_stacktrace_fillInStackTrace((void **) &result, &getStackCollector);
700
701         return result;
702 }
703
704
705 /* stacktrace_dump_trace *******************************************************
706
707    This method is call from signal_handler_sigusr1 to dump the
708    stacktrace of the current thread to stdout.
709
710 *******************************************************************************/
711
712 void stacktrace_dump_trace(void)
713 {
714         stackTraceBuffer      *buffer;
715         stacktraceelement     *element;
716         methodinfo            *m;
717         s4                     i;
718
719 #if 0
720         /* get thread stackframeinfo */
721
722         info = &THREADINFO->_stackframeinfo;
723
724         /* fill stackframeinfo structure */
725
726         tmp.oldThreadspecificHeadValue = *info;
727         tmp.addressOfThreadspecificHead = info;
728         tmp.method = NULL;
729         tmp.beginOfJavaStackframe = NULL;
730         tmp.returnToFromNative = _mc->gregs[REG_RIP];
731
732         *info = &tmp;
733 #endif
734
735         /* generate stacktrace */
736
737         cacao_stacktrace_NormalTrace((void **) &buffer);
738
739         /* print stacktrace */
740
741         if (buffer) {
742                 element = buffer->start;
743
744                 for (i = 0; i < buffer->size; i++, element++) {
745                         m = element->method;
746
747                         printf("\tat ");
748                         utf_display_classname(m->class->name);
749                         printf(".");
750                         utf_display(m->name);
751                         utf_display(m->descriptor);
752
753                         if (m->flags & ACC_NATIVE) {
754                                 printf("(Native Method)\n");
755
756                         } else {
757                                 printf("(");
758                                 utf_display(m->class->sourcefile);
759                                 printf(":%d)\n", (u4) element->linenumber);
760                         }
761                 }
762         }
763
764         /* flush stdout */
765
766         fflush(stdout);
767 }
768
769
770 /*
771  * These are local overrides for various environment variables in Emacs.
772  * Please do not remove this and leave it at the end of the file, where
773  * Emacs will automagically detect them.
774  * ---------------------------------------------------------------------
775  * Local variables:
776  * mode: c
777  * indent-tabs-mode: t
778  * c-basic-offset: 4
779  * tab-width: 4
780  * End:
781  */