* stacktrace_create_inline_stackframeinfo,
[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 2933 2005-07-08 11:59:57Z 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         /* fill new stackframe info structure */
112
113         sfi->oldThreadspecificHeadValue = *osfi;
114         sfi->addressOfThreadspecificHead = osfi;
115         sfi->method = NULL;
116         sfi->pv = pv;
117         sfi->beginOfJavaStackframe = sp;
118         sfi->returnToFromNative = ra;
119
120         /* store new stackframe info pointer */
121
122         *osfi = sfi;
123 }
124
125
126 /* stacktrace_create_native_stackframeinfo *************************************
127
128    Creates a stackframe info structure for a native stub.
129
130 *******************************************************************************/
131
132 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
133                                                                                          u1 *sp, functionptr ra)
134 {
135         void       **osfi;
136         methodinfo  *m;
137
138         /* get methodinfo pointer from data segment */
139
140         m = *((methodinfo **) (pv + MethodPointer));
141
142         /* get current stackframe info pointer */
143
144         osfi = builtin_asm_get_stackframeinfo();
145
146         /* fill new stackframe info structure */
147
148         sfi->oldThreadspecificHeadValue = *osfi;
149         sfi->addressOfThreadspecificHead = osfi;
150         sfi->method = m;
151         sfi->pv = NULL;
152         sfi->beginOfJavaStackframe = sp;
153         sfi->returnToFromNative = ra;
154
155         /* store new stackframe info pointer */
156
157         *osfi = sfi;
158 }
159
160
161 /* stacktrace_remove_stackframeinfo ********************************************
162
163    XXX
164
165 *******************************************************************************/
166
167 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
168 {
169         void **osfi;
170
171         /* get address of pointer */
172
173         osfi = sfi->addressOfThreadspecificHead;
174
175         /* restore the old pointer */
176
177         *osfi = sfi->oldThreadspecificHeadValue;
178 }
179
180
181 /* stacktrace_call_fillInStackTrace ********************************************
182
183    XXX
184
185 *******************************************************************************/
186
187 void stacktrace_call_fillInStackTrace(java_objectheader *o)
188 {
189         methodinfo *m;
190
191         /* resolve methodinfo pointer from exception object */
192
193         m = class_resolvemethod(o->vftbl->class,
194                                                         utf_fillInStackTrace,
195                                                         utf_void__java_lang_Throwable);
196
197         /* call function */
198
199         asm_calljavafunction(m, o, NULL, NULL, NULL);
200 }
201
202
203 static void addEntry(stackTraceBuffer* buffer,methodinfo*method ,LineNumber line) {
204         if (buffer->size>buffer->full) {
205                 stacktraceelement *tmp=&(buffer->start[buffer->full]);
206                 tmp->method=method;
207                 tmp->linenumber=line;
208                 buffer->full = buffer->full + 1;
209 #if (defined(JWDEBUG) || defined (JWDEBUG2))
210                 log_text("addEntry (stacktrace):");
211                 printf("method %p\n",method);
212                 if (method) printf("method->name %p\n",method->name);
213                 if (method) utf_display(method->name); else printf("Native");
214                 if (method) {printf("\n");utf_display(method->class->name);}
215                 printf("\nnext buffer item %d\nLine:%ld\n",buffer->full,line);
216 #endif
217         } else {
218                 stacktraceelement *newBuffer;
219
220 #ifdef JWDEBUG
221                 log_text("stacktrace buffer full, resizing");
222 #endif
223
224                 newBuffer =
225                         (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
226                                                                                  sizeof(stacktraceelement));
227
228                 if (newBuffer==0) {
229                         log_text("OOM during stacktrace creation");
230                         assert(0);
231                 }
232
233                 memcpy(newBuffer,buffer->start,buffer->size*sizeof(stacktraceelement));
234                 if (buffer->needsFree) free(buffer->start);
235                 buffer->start=newBuffer;
236                 buffer->size=buffer->size+BLOCK_SIZEINCREMENT;
237                 buffer->needsFree=1;
238                 addEntry(buffer,method,line);
239         }
240 }
241
242
243 /* stacktrace_fillInStackTrace_methodRecursive *********************************
244
245    XXX
246
247 *******************************************************************************/
248
249 static int stacktrace_fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,
250                                                                                                            methodinfo *method,
251                                                                                                            lineNumberTableEntry *startEntry,
252                                                                                                            lineNumberTableEntry **entry,
253                                                                                                            size_t *entriesAhead,
254                                                                                                            u1 *address)
255 {
256
257         size_t ahead=*entriesAhead;
258         lineNumberTableEntry *ent=*entry;
259         lineNumberTableEntryInlineBegin *ilStart;
260
261         for (; ahead > 0; ahead--, ent++) {
262                 if (address >= ent->pc) {
263                         switch (ent->lineNr) {
264                         case -1: /* begin of inlined method */
265                                 ilStart=(lineNumberTableEntryInlineBegin*)(++ent);
266                                 ent ++;
267                                 ahead--; ahead--;
268                                 if (stacktrace_fillInStackTrace_methodRecursive(buffer,ilStart->method,ent,&ent,&ahead,address)) {
269                                         addEntry(buffer,method,ilStart->lineNrOuter);
270                                         return 1;
271                                 }
272                                 break;
273                         case -2: /* end of inlined method */
274                                 *entry=ent;
275                                 *entriesAhead=ahead;
276                                 return 0;
277                                 break;
278                         default:
279                                 if (address == ent->pc) {
280                                         addEntry(buffer, method, ent->lineNr);
281                                         return 1;
282                                 }
283                                 break;
284                         }
285
286                 } else {
287                         if (address > startEntry->pc) {
288                                 ent--;
289                                 addEntry(buffer, method, ent->lineNr);
290                                 return 1;
291
292                         } else {
293                                 printf("trace point: %p\n", address);
294                                 log_text("trace point before method");
295                                 assert(0);
296                         }
297                 }
298         }
299
300         ent--;
301         addEntry(buffer, method, ent->lineNr);
302         return 1;
303 }
304
305
306 /* stacktrace_fillInStackTrace_method ******************************************
307
308    XXX
309
310 *******************************************************************************/
311
312 static void stacktrace_fillInStackTrace_method(stackTraceBuffer *buffer,
313                                                                                            methodinfo *method, u1 *dataSeg,
314                                                                                            u1 *address)
315 {
316         size_t lineNumberTableSize=(*((size_t*)(dataSeg+LineNumberTableSize)));
317         lineNumberTableEntry *ent;
318         void **calc;
319         lineNumberTableEntry *startEntry;
320
321         if (lineNumberTableSize == 0) {
322                 /* right now this happens only on i386,if the native stub causes an */
323                 /* exception in a <clinit> invocation (jowenn) */
324
325                 addEntry(buffer, method, 0);
326                 return;
327
328         } else {
329                 calc = (void **) (dataSeg + LineNumberTableStart);
330                 ent = (lineNumberTableEntry *) (((char *) (*calc) - (sizeof(lineNumberTableEntry) - SIZEOF_VOID_P)));
331
332                 ent -= (lineNumberTableSize - 1);
333                 startEntry = ent;
334
335                 if (!stacktrace_fillInStackTrace_methodRecursive(buffer, method,
336                                                                                                                  startEntry, &ent,
337                                                                                                                  &lineNumberTableSize,
338                                                                                                                  address)) {
339                         log_text("Trace point not found in suspected method");
340                         assert(0);
341                 }
342         }
343 }
344
345
346 /* cacao_stacktrace_fillInStackTrace *******************************************
347
348    XXX
349
350 *******************************************************************************/
351
352 void cacao_stacktrace_fillInStackTrace(void **target,
353                                                                            CacaoStackTraceCollector coll)
354 {
355         stacktraceelement      primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)];
356         stackTraceBuffer  buffer;
357         stackframeinfo   *info;
358         methodinfo       *m;
359         u1               *pv;
360         u1               *sp;
361         u4                framesize;
362         functionptr       ra;
363
364
365         /* In most cases this should be enough -> one malloc less. I don't think  */
366         /* temporary data should be allocated with the GC, only the result.       */
367
368         buffer.needsFree = 0;
369         buffer.start = primaryBlock;
370         buffer.size = BLOCK_INITIALSIZE; /*  *sizeof(stacktraceelement); */
371         buffer.full = 0;
372
373         info = *((void **) builtin_asm_get_stackframeinfo());
374
375         if (!info) {
376                 *target = NULL;
377                 return;
378
379         } else {
380                 m = NULL;
381
382                 while (m || info) {
383                         /* some builtin native */
384
385                         if (m == NULL) {
386                                 m = info->method;
387                                 ra = (functionptr) info->returnToFromNative;
388
389 #if 0
390                                 if (m) {
391                                         utf_display_classname(m->class->name);
392                                         printf(".");
393                                         utf_display(m->name);
394                                         utf_display(m->descriptor);
395                                         printf(": native\n");
396
397                                         addEntry(&buffer, m, 0);
398                                 } else {
399                                         printf("NULL: native\n");
400                                 }
401 #else
402                                 if (m) {
403                                         addEntry(&buffer, m, 0);
404                                 }
405 #endif
406
407                                 /* get data segment address */
408
409                                 if (info->pv) {
410                                         /* this is an inline info */
411
412                                         pv = info->pv;
413
414                                 } else {
415                                         /* this is an native stub info */
416
417                                         pv = (u1 *) codegen_findmethod(ra);
418                                 }
419
420                                 /* get methodinfo pointer from data segment */
421
422                                 m = *((methodinfo **) (pv + MethodPointer));
423
424                                 if (info->beginOfJavaStackframe == 0) {
425                                         sp = ((u1 *) info) + sizeof(stackframeinfo);
426
427                                 } else {
428 #if defined(__I386__) || defined (__X86_64__)
429                                         sp = (u1 *) info->beginOfJavaStackframe + SIZEOF_VOID_P;
430 #else
431                                         sp = (u1 *) info->beginOfJavaStackframe;
432 #endif
433                                 }
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         cacao_stacktrace_fillInStackTrace(target,&stackTraceCollector);
510 }
511
512
513
514 static void classContextCollector(void **target, stackTraceBuffer *buffer)
515 {
516         java_objectarray  *tmpArray;
517         stacktraceelement *current;
518         stacktraceelement *start;
519         size_t size;
520         size_t targetSize;
521         s4 i;
522
523         size = buffer->full;
524         targetSize = 0;
525
526         for (i = 0; i < size; i++)
527                 if (buffer->start[i].method != 0)
528                         targetSize++;
529
530         start = buffer->start;
531         start++;
532         targetSize--;
533
534         if (targetSize > 0) {
535                 if (start->method && (start->method->class == class_java_lang_SecurityManager)) {
536                         targetSize--;
537                         start++;
538                 }
539         }
540
541         tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
542
543         for(i = 0, current = start; i < targetSize; i++, current++) {
544                 /* XXX TWISTI: should we use this skipping for native stubs? */
545
546                 if (!current->method) {
547                         i--;
548                         continue;
549                 }
550
551                 use_class_as_object(current->method->class);
552
553                 tmpArray->data[i] = (java_objectheader *) current->method->class;
554         }
555
556         *target = tmpArray;
557 }
558
559
560
561 java_objectarray *cacao_createClassContextArray() {
562         java_objectarray *array=0;
563         cacao_stacktrace_fillInStackTrace((void**)&array,&classContextCollector);
564         return array;
565         
566 }
567
568
569 /* stacktrace_classLoaderCollector *********************************************
570
571    XXX
572
573 *******************************************************************************/
574
575 static void stacktrace_classLoaderCollector(void **target,
576                                                                                         stackTraceBuffer *buffer)
577 {
578         stacktraceelement *current;
579         stacktraceelement *start;
580         methodinfo        *m;
581         ptrint             size;
582         s4                 i;
583
584         size = buffer->full;
585         start = &(buffer->start[0]);
586
587         for(i = 0, current = start; i < size; i++, current++) {
588                 m = current->method;
589
590                 if (!m)
591                         continue;
592
593                 if (m->class == class_java_security_PrivilegedAction) {
594                         *target = NULL;
595                         return;
596                 }
597
598                 if (m->class->classloader) {
599                         *target = (java_lang_ClassLoader *) m->class->classloader;
600                         return;
601                 }
602         }
603
604         *target = NULL;
605 }
606
607
608 /* cacao_currentClassLoader ****************************************************
609
610    XXX
611
612 *******************************************************************************/
613
614 java_objectheader *cacao_currentClassLoader(void)
615 {
616         java_objectheader *header=0;
617
618         cacao_stacktrace_fillInStackTrace((void**)&header,
619                                                                           &stacktrace_classLoaderCollector);
620
621         return header;
622 }
623
624
625 static
626 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {  
627         if (buffer->full >2) (*target)=buffer->start[2].method;
628         else (*target=0);
629 }
630
631 methodinfo *cacao_callingMethod() {
632         methodinfo *method;
633         cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
634         return method;
635 }
636
637
638 static
639 void getStackCollector(void **target, stackTraceBuffer *buffer)
640 {
641         java_objectarray *classes;
642         java_objectarray *methodnames;
643         java_objectarray **result=(java_objectarray**)target;
644         java_lang_String *str;
645         classinfo *c;
646         stacktraceelement *current;
647         int i,size;
648
649         /*log_text("getStackCollector");*/
650
651         size = buffer->full;
652
653         *result = builtin_anewarray(2, arrayclass_java_lang_Object);
654
655         if (!(*result))
656                 return;
657
658         classes = builtin_anewarray(size, class_java_lang_Class);
659
660         if (!classes)
661                 return;
662
663         methodnames = builtin_anewarray(size, class_java_lang_String);
664
665         if (!methodnames)
666                 return;
667
668         (*result)->data[0] = (java_objectheader *) classes;
669         (*result)->data[1] = (java_objectheader *) methodnames;
670
671         /*log_text("Before for loop");*/
672         for (i = 0, current = &(buffer->start[0]); i < size; i++, current++) {
673                 /*log_text("In loop");*/
674                 c = current->method->class;
675                 use_class_as_object(c);
676                 classes->data[i] = (java_objectheader *) c;
677                 str = javastring_new(current->method->name);
678                 if (!str)
679                         return;
680                 methodnames->data[i] = (java_objectheader *) str;
681                 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
682         }
683
684         /*if (*exceptionptr) panic("Exception in getStackCollector");*/
685
686         /*log_text("loop left");*/
687         return;
688
689 }
690
691
692 java_objectarray *cacao_getStackForVMAccessController()
693 {
694         java_objectarray *result=0;
695         cacao_stacktrace_fillInStackTrace((void**)&result,&getStackCollector);
696         return result;
697 }
698
699
700 /* stacktrace_dump_trace *******************************************************
701
702    This method is call from signal_handler_sigusr1 to dump the
703    stacktrace of the current thread to stdout.
704
705 *******************************************************************************/
706
707 void stacktrace_dump_trace(void)
708 {
709         stackTraceBuffer      *buffer;
710         stacktraceelement     *element;
711         methodinfo            *m;
712         s4                     i;
713
714 #if 0
715         /* get thread stackframeinfo */
716
717         info = &THREADINFO->_stackframeinfo;
718
719         /* fill stackframeinfo structure */
720
721         tmp.oldThreadspecificHeadValue = *info;
722         tmp.addressOfThreadspecificHead = info;
723         tmp.method = NULL;
724         tmp.beginOfJavaStackframe = NULL;
725         tmp.returnToFromNative = _mc->gregs[REG_RIP];
726
727         *info = &tmp;
728 #endif
729
730         /* generate stacktrace */
731
732         cacao_stacktrace_NormalTrace((void **) &buffer);
733
734         /* print stacktrace */
735
736         if (buffer) {
737                 element = buffer->start;
738
739                 for (i = 0; i < buffer->size; i++, element++) {
740                         m = element->method;
741
742                         printf("\tat ");
743                         utf_display_classname(m->class->name);
744                         printf(".");
745                         utf_display(m->name);
746                         utf_display(m->descriptor);
747
748                         if (m->flags & ACC_NATIVE) {
749                                 printf("(Native Method)\n");
750
751                         } else {
752                                 printf("(");
753                                 utf_display(m->class->sourcefile);
754                                 printf(":%d)\n", (u4) element->linenumber);
755                         }
756                 }
757         }
758
759         /* flush stdout */
760
761         fflush(stdout);
762 }
763
764
765 /*
766  * These are local overrides for various environment variables in Emacs.
767  * Please do not remove this and leave it at the end of the file, where
768  * Emacs will automagically detect them.
769  * ---------------------------------------------------------------------
770  * Local variables:
771  * mode: c
772  * indent-tabs-mode: t
773  * c-basic-offset: 4
774  * tab-width: 4
775  * End:
776  */