1 /* src/vm/jit/stacktrace.c
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
8 This file is part of CACAO.
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.
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.
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
25 Contact: cacao@complang.tuwien.ac.at
27 Authors: Joseph Wenninger
29 Changes: Christian Thalinger
31 $Id: stacktrace.c 3073 2005-07-20 10:40:41Z twisti $
41 #include "asmoffsets.h"
44 #include "native/native.h"
46 #include "vm/global.h" /* required here for native includes */
47 #include "native/include/java_lang_ClassLoader.h"
49 #include "toolbox/logging.h"
50 #include "vm/builtin.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"
65 /* lineNumberTableEntry *******************************************************/
67 /* Keep the type of line the same as the pointer type, otherwise we run into */
68 /* alignment troubles (like on MIPS64). */
70 typedef struct lineNumberTableEntry {
73 } lineNumberTableEntry;
76 typedef struct lineNumberTableEntryInlineBegin {
77 /* this should have the same layout and size as the lineNumberTableEntry */
80 } lineNumberTableEntryInlineBegin;
83 typedef void(*CacaoStackTraceCollector)(void **,stackTraceBuffer*);
85 #define BLOCK_INITIALSIZE 40
86 #define BLOCK_SIZEINCREMENT 40
89 /* stacktrace_create_inline_stackframeinfo *************************************
91 Creates an stackframe info structure for an inline exception stub.
93 *******************************************************************************/
95 void stacktrace_create_inline_stackframeinfo(stackframeinfo *sfi, u1 *pv,
96 u1 *sp, functionptr ra,
101 /* get current stackframe info pointer */
103 osfi = builtin_asm_get_stackframeinfo();
105 /* fill new stackframe info structure */
107 sfi->oldThreadspecificHeadValue = *osfi;
108 sfi->addressOfThreadspecificHead = osfi;
115 /* store new stackframe info pointer */
121 /* stacktrace_create_extern_stackframeinfo *************************************
123 Creates an stackframe info structure for an extern exception
124 (hardware or assembler).
126 *******************************************************************************/
128 void stacktrace_create_extern_stackframeinfo(stackframeinfo *sfi, u1 *pv,
129 u1 *sp, functionptr ra,
133 #if !defined(__I386__) && !defined(__X86_64__)
138 /* get current stackframe info pointer */
140 osfi = builtin_asm_get_stackframeinfo();
142 /* sometimes we don't have pv handy (e.g. in asmpart.S: */
143 /* L_asm_call_jit_compiler_exception) */
146 pv = (u1 *) (ptrint) codegen_findmethod(ra);
148 #if defined(__I386__) || defined(__X86_64__)
149 /* On i386 and x86_64 we always have to get the return address from the */
152 framesize = *((u4 *) (pv + FrameSize));
154 ra = md_stacktrace_get_returnaddress(sp, framesize);
156 /* If the method is a non-leaf function, we need to get the return */
157 /* address from the stack. For leaf functions the return address is set */
158 /* correctly. This makes the assembler and the signal handler code */
161 isleafmethod = *((s4 *) (pv + IsLeaf));
164 framesize = *((u4 *) (pv + FrameSize));
166 ra = md_stacktrace_get_returnaddress(sp, framesize);
170 /* fill new stackframe info structure */
172 sfi->oldThreadspecificHeadValue = *osfi;
173 sfi->addressOfThreadspecificHead = osfi;
180 /* store new stackframe info pointer */
186 /* stacktrace_create_native_stackframeinfo *************************************
188 Creates a stackframe info structure for a native stub.
190 *******************************************************************************/
192 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
193 u1 *sp, functionptr ra)
198 /* get methodinfo pointer from data segment */
200 m = *((methodinfo **) (pv + MethodPointer));
202 /* get current stackframe info pointer */
204 osfi = builtin_asm_get_stackframeinfo();
206 /* fill new stackframe info structure */
208 sfi->oldThreadspecificHeadValue = *osfi;
209 sfi->addressOfThreadspecificHead = osfi;
216 /* store new stackframe info pointer */
222 /* stacktrace_remove_stackframeinfo ********************************************
226 *******************************************************************************/
228 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
232 /* get address of pointer */
234 osfi = sfi->addressOfThreadspecificHead;
236 /* restore the old pointer */
238 *osfi = sfi->oldThreadspecificHeadValue;
242 /* stacktrace_inline_arithmeticexception ***************************************
244 Creates an ArithemticException for inline stub.
246 *******************************************************************************/
248 java_objectheader *stacktrace_inline_arithmeticexception(u1 *pv, u1 *sp,
253 java_objectheader *o;
255 /* create stackframeinfo */
257 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
259 /* create exception */
261 o = new_arithmeticexception();
263 /* remove stackframeinfo */
265 stacktrace_remove_stackframeinfo(&sfi);
271 /* stacktrace_inline_arrayindexoutofboundsexception ****************************
273 Creates an ArrayIndexOutOfBoundsException for inline stub.
275 *******************************************************************************/
277 java_objectheader *stacktrace_inline_arrayindexoutofboundsexception(u1 *pv,
284 java_objectheader *o;
286 /* create stackframeinfo */
288 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
290 /* create exception */
292 o = new_arrayindexoutofboundsexception(index);
294 /* remove stackframeinfo */
296 stacktrace_remove_stackframeinfo(&sfi);
302 /* stacktrace_inline_arraystoreexception ***************************************
304 Creates an ArrayStoreException for inline stub.
306 *******************************************************************************/
308 java_objectheader *stacktrace_inline_arraystoreexception(u1 *pv, u1 *sp,
313 java_objectheader *o;
315 /* create stackframeinfo */
317 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
319 /* create exception */
321 o = new_arraystoreexception();
323 /* remove stackframeinfo */
325 stacktrace_remove_stackframeinfo(&sfi);
331 /* stacktrace_inline_classcastexception ****************************************
333 Creates an ClassCastException for inline stub.
335 *******************************************************************************/
337 java_objectheader *stacktrace_inline_classcastexception(u1 *pv, u1 *sp,
342 java_objectheader *o;
344 /* create stackframeinfo */
346 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
348 /* create exception */
350 o = new_classcastexception();
352 /* remove stackframeinfo */
354 stacktrace_remove_stackframeinfo(&sfi);
360 /* stacktrace_inline_negativearraysizeexception ********************************
362 Creates an NegativeArraySizeException for inline stub.
364 *******************************************************************************/
366 java_objectheader *stacktrace_inline_negativearraysizeexception(u1 *pv, u1 *sp,
371 java_objectheader *o;
373 /* create stackframeinfo */
375 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
377 /* create exception */
379 o = new_negativearraysizeexception();
381 /* remove stackframeinfo */
383 stacktrace_remove_stackframeinfo(&sfi);
389 /* stacktrace_inline_nullpointerexception **************************************
391 Creates an NullPointerException for inline stub.
393 *******************************************************************************/
395 java_objectheader *stacktrace_inline_nullpointerexception(u1 *pv, u1 *sp,
400 java_objectheader *o;
402 /* create stackframeinfo */
404 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
406 /* create exception */
408 o = new_nullpointerexception();
410 /* remove stackframeinfo */
412 stacktrace_remove_stackframeinfo(&sfi);
418 /* stacktrace_hardware_arithmeticexception *************************************
420 Creates an ArithemticException for inline stub.
422 *******************************************************************************/
424 java_objectheader *stacktrace_hardware_arithmeticexception(u1 *pv, u1 *sp,
429 java_objectheader *o;
431 /* create stackframeinfo */
433 stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
435 /* create exception */
437 o = new_arithmeticexception();
439 /* remove stackframeinfo */
441 stacktrace_remove_stackframeinfo(&sfi);
447 /* stacktrace_hardware_nullpointerexception ************************************
449 Creates an NullPointerException for the SIGSEGV signal handler.
451 *******************************************************************************/
453 java_objectheader *stacktrace_hardware_nullpointerexception(u1 *pv, u1 *sp,
458 java_objectheader *o;
460 /* create stackframeinfo */
462 stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
464 /* create exception */
466 o = new_nullpointerexception();
468 /* remove stackframeinfo */
470 stacktrace_remove_stackframeinfo(&sfi);
476 /* stacktrace_inline_fillInStackTrace ******************************************
478 Fills in the correct stacktrace into an existing exception object
479 (this one is for inline exception stubs).
481 *******************************************************************************/
483 java_objectheader *stacktrace_inline_fillInStackTrace(u1 *pv, u1 *sp,
488 java_objectheader *o;
491 /* create stackframeinfo */
493 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
499 /* clear exception */
501 *exceptionptr = NULL;
503 /* resolve methodinfo pointer from exception object */
505 m = class_resolvemethod(o->vftbl->class,
506 utf_fillInStackTrace,
507 utf_void__java_lang_Throwable);
511 asm_calljavafunction(m, o, NULL, NULL, NULL);
513 /* remove stackframeinfo */
515 stacktrace_remove_stackframeinfo(&sfi);
521 /* addEntry ********************************************************************
525 *******************************************************************************/
527 static void addEntry(stackTraceBuffer *buffer, methodinfo *method, u2 line)
529 if (buffer->size > buffer->full) {
530 stacktraceelement *tmp = &(buffer->start[buffer->full]);
532 tmp->method = method;
533 tmp->linenumber = line;
534 buffer->full = buffer->full + 1;
537 stacktraceelement *newBuffer;
540 (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
541 sizeof(stacktraceelement));
543 if (newBuffer == 0) {
544 log_text("OOM during stacktrace creation");
548 memcpy(newBuffer, buffer->start, buffer->size * sizeof(stacktraceelement));
549 if (buffer->needsFree)
552 buffer->start = newBuffer;
553 buffer->size = buffer->size + BLOCK_SIZEINCREMENT;
554 buffer->needsFree = 1;
556 addEntry(buffer, method, line);
561 /* stacktrace_fillInStackTrace_methodRecursive *********************************
565 *******************************************************************************/
567 static bool stacktrace_fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,
569 lineNumberTableEntry *lntentry,
574 lineNumberTableEntryInlineBegin *lntinline;
577 /* find the line number for the specified pc (going backwards) */
579 for (; lntsize > 0; lntsize--, lntentry--) {
580 /* did we reach the current line? */
582 if (pc >= lntentry->pc) {
583 /* check for special inline entries */
585 switch (lntentry->line) {
587 /* XXX TWISTI we have to think about this inline stuff again */
589 case -1: /* begin of inlined method */
590 lntinline = (lineNumberTableEntryInlineBegin *) (--lntentry);
592 lntsize--; lntsize--;
593 if (stacktrace_fillInStackTrace_methodRecursive(buffer,
599 addEntry(buffer, method, ilStart->lineNrOuter);
605 case -2: /* end of inlined method */
607 *entriesAhead = ahead;
613 addEntry(buffer, method, lntentry->line);
619 /* this should not happen */
627 /* stacktrace_fillInStackTrace_method ******************************************
631 *******************************************************************************/
633 static void stacktrace_fillInStackTrace_method(stackTraceBuffer *buffer,
634 methodinfo *method, u1 *pv,
637 ptrint lntsize; /* size of line number table */
638 u1 *lntstart; /* start of line number table */
639 lineNumberTableEntry *lntentry; /* points to last entry in the table */
641 /* get size of line number table */
643 lntsize = *((ptrint *) (pv + LineNumberTableSize));
644 lntstart = *((u1 **) (pv + LineNumberTableStart));
646 /* subtract the size of the line number entry of the structure, since the */
647 /* line number table start points to the pc */
649 lntentry = (lineNumberTableEntry *) (lntstart - SIZEOF_VOID_P);
652 /* this happens when an exception is thrown in the native stub */
654 addEntry(buffer, method, 0);
657 if (!stacktrace_fillInStackTrace_methodRecursive(buffer,
662 log_text("Trace point not found in suspected method");
669 /* cacao_stacktrace_fillInStackTrace *******************************************
673 *******************************************************************************/
675 void cacao_stacktrace_fillInStackTrace(void **target,
676 CacaoStackTraceCollector coll)
678 stacktraceelement primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)];
679 stackTraceBuffer buffer;
680 stackframeinfo *info;
688 /* prevent compiler warnings */
694 /* In most cases this should be enough -> one malloc less. I don't think */
695 /* temporary data should be allocated with the GC, only the result. */
697 buffer.needsFree = 0;
698 buffer.start = primaryBlock;
699 buffer.size = BLOCK_INITIALSIZE; /* *sizeof(stacktraceelement); */
702 /* the first element in the stackframe chain must always be a native */
703 /* stackframeinfo (VMThrowable.fillInStackTrace is a native function) */
705 info = *((void **) builtin_asm_get_stackframeinfo());
712 #define PRINTMETHODS 0
715 printf("\n\nfillInStackTrace start:\n");
718 /* loop while we have a method pointer (asm_calljavafunction has NULL) or */
719 /* there is a stackframeinfo in the chain */
724 /* m == NULL should only happen for the first time and inline */
725 /* stackframe infos, like from the exception stubs or the patcher */
729 /* for native stub stackframe infos, pv is always NULL */
731 if (info->pv == NULL) {
732 /* get methodinfo, sp and ra from the current stackframe info */
735 sp = info->sp; /* sp of parent Java function */
739 addEntry(&buffer, m, 0);
742 utf_display_classname(m->class->name);
744 utf_display(m->name);
745 utf_display(m->descriptor);
746 printf(": native stub\n");
748 /* this is an native stub stackframe info, so we can get the */
749 /* parent pv from the return address (ICMD_INVOKE*) */
751 pv = (u1 *) (ptrint) codegen_findmethod(ra);
753 /* get methodinfo pointer from parent data segment */
755 m = *((methodinfo **) (pv + MethodPointer));
758 /* Inline stackframe infos are special: they have a xpc of */
759 /* the actual exception position and the return address saved */
760 /* since an inline stackframe info can also be in a leaf */
761 /* method (no return address saved on stack!!!). */
762 /* ATTENTION: This one is also for hardware exceptions!!! */
764 /* get methodinfo, sp and ra from the current stackframe info */
766 m = info->method; /* m == NULL */
767 pv = info->pv; /* pv of parent Java function */
768 sp = info->sp; /* sp of parent Java function */
769 ra = info->ra; /* ra of parent Java function */
770 xpc = info->xpc; /* actual exception position */
773 printf("NULL: inline stub\n");
776 /* get methodinfo from current Java method */
778 m = *((methodinfo **) (pv + MethodPointer));
780 /* if m == NULL, this is a asm_calljavafunction call */
784 utf_display_classname(m->class->name);
786 utf_display(m->name);
787 utf_display(m->descriptor);
788 printf(": inline stub parent\n");
791 /* add it to the stacktrace */
793 stacktrace_fillInStackTrace_method(&buffer, m, pv,
794 (u1 *) ((ptrint) xpc));
796 /* get the current stack frame size */
798 framesize = *((u4 *) (pv + FrameSize));
800 /* set stack pointer to stackframe of parent Java */
801 /* function of the current Java function */
803 #if defined(__I386__) || defined (__X86_64__)
804 sp += framesize + SIZEOF_VOID_P;
809 /* get data segment and methodinfo pointer from parent */
812 pv = (u1 *) (ptrint) codegen_findmethod(ra);
813 m = *((methodinfo **) (pv + MethodPointer));
817 /* get next stackframeinfo in the chain */
819 info = info->oldThreadspecificHeadValue;
823 utf_display_classname(m->class->name);
825 utf_display(m->name);
826 utf_display(m->descriptor);
830 /* JIT method found, add it to the stacktrace (we subtract 1 from */
831 /* the return address since it points the the instruction after */
834 stacktrace_fillInStackTrace_method(&buffer, m, pv,
835 (u1 *) ((ptrint) ra) - 1);
837 /* get the current stack frame size */
839 framesize = *((u4 *) (pv + FrameSize));
841 /* get return address of current stack frame */
843 ra = md_stacktrace_get_returnaddress(sp, framesize);
845 /* get data segment and methodinfo pointer from parent method */
847 pv = (u1 *) (ptrint) codegen_findmethod(ra);
848 m = *((methodinfo **) (pv + MethodPointer));
852 #if defined(__I386__) || defined (__X86_64__)
853 sp += framesize + SIZEOF_VOID_P;
861 coll(target, &buffer);
863 if (buffer.needsFree)
869 void stackTraceCollector(void **target, stackTraceBuffer *buffer) {
870 stackTraceBuffer *dest=*target=heap_allocate(sizeof(stackTraceBuffer)+buffer->full*sizeof(stacktraceelement),true,0);
871 memcpy(*target,buffer,sizeof(stackTraceBuffer));
872 memcpy(dest+1,buffer->start,buffer->full*sizeof(stacktraceelement));
875 dest->size=dest->full;
876 dest->start=(stacktraceelement*)(dest+1);
879 if (buffer->full>0) {
880 printf("SOURCE BUFFER:%s\n",buffer->start[0].method->name->text);
881 printf("DEST BUFFER:%s\n",dest->start[0].method->name->text);
882 } else printf("Buffer is empty\n");
887 void cacao_stacktrace_NormalTrace(void **target)
889 cacao_stacktrace_fillInStackTrace(target, &stackTraceCollector);
894 static void classContextCollector(void **target, stackTraceBuffer *buffer)
896 java_objectarray *tmpArray;
897 stacktraceelement *current;
898 stacktraceelement *start;
906 for (i = 0; i < size; i++)
907 if (buffer->start[i].method != 0)
910 start = buffer->start;
914 if (targetSize > 0) {
915 if (start->method && (start->method->class == class_java_lang_SecurityManager)) {
921 tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
923 for(i = 0, current = start; i < targetSize; i++, current++) {
924 /* XXX TWISTI: should we use this skipping for native stubs? */
926 if (!current->method) {
931 use_class_as_object(current->method->class);
933 tmpArray->data[i] = (java_objectheader *) current->method->class;
941 java_objectarray *cacao_createClassContextArray(void)
943 java_objectarray *array=0;
945 cacao_stacktrace_fillInStackTrace((void **) &array, &classContextCollector);
951 /* stacktrace_classLoaderCollector *********************************************
955 *******************************************************************************/
957 static void stacktrace_classLoaderCollector(void **target,
958 stackTraceBuffer *buffer)
960 stacktraceelement *current;
961 stacktraceelement *start;
967 start = &(buffer->start[0]);
969 for(i = 0, current = start; i < size; i++, current++) {
975 if (m->class == class_java_security_PrivilegedAction) {
980 if (m->class->classloader) {
981 *target = (java_lang_ClassLoader *) m->class->classloader;
990 /* cacao_currentClassLoader ****************************************************
994 *******************************************************************************/
996 java_objectheader *cacao_currentClassLoader(void)
998 java_objectheader *header=0;
1000 cacao_stacktrace_fillInStackTrace((void**)&header,
1001 &stacktrace_classLoaderCollector);
1008 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {
1009 if (buffer->full >2) (*target)=buffer->start[2].method;
1013 methodinfo *cacao_callingMethod() {
1015 cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
1021 void getStackCollector(void **target, stackTraceBuffer *buffer)
1023 java_objectarray *classes;
1024 java_objectarray *methodnames;
1025 java_objectarray **result=(java_objectarray**)target;
1026 java_lang_String *str;
1028 stacktraceelement *current;
1031 /*log_text("getStackCollector");*/
1033 size = buffer->full;
1035 *result = builtin_anewarray(2, arrayclass_java_lang_Object);
1040 classes = builtin_anewarray(size, class_java_lang_Class);
1045 methodnames = builtin_anewarray(size, class_java_lang_String);
1050 (*result)->data[0] = (java_objectheader *) classes;
1051 (*result)->data[1] = (java_objectheader *) methodnames;
1053 /*log_text("Before for loop");*/
1054 for (i = 0, current = &(buffer->start[0]); i < size; i++, current++) {
1055 /*log_text("In loop");*/
1056 c = current->method->class;
1057 use_class_as_object(c);
1058 classes->data[i] = (java_objectheader *) c;
1059 str = javastring_new(current->method->name);
1062 methodnames->data[i] = (java_objectheader *) str;
1063 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
1066 /*if (*exceptionptr) panic("Exception in getStackCollector");*/
1068 /*log_text("loop left");*/
1074 java_objectarray *cacao_getStackForVMAccessController(void)
1076 java_objectarray *result = NULL;
1078 cacao_stacktrace_fillInStackTrace((void **) &result, &getStackCollector);
1084 /* stacktrace_dump_trace *******************************************************
1086 This method is call from signal_handler_sigusr1 to dump the
1087 stacktrace of the current thread to stdout.
1089 *******************************************************************************/
1091 void stacktrace_dump_trace(void)
1093 stackTraceBuffer *buffer;
1094 stacktraceelement *element;
1099 /* get thread stackframeinfo */
1101 info = &THREADINFO->_stackframeinfo;
1103 /* fill stackframeinfo structure */
1105 tmp.oldThreadspecificHeadValue = *info;
1106 tmp.addressOfThreadspecificHead = info;
1109 tmp.ra = _mc->gregs[REG_RIP];
1114 /* generate stacktrace */
1116 cacao_stacktrace_NormalTrace((void **) &buffer);
1118 /* print stacktrace */
1121 element = buffer->start;
1123 for (i = 0; i < buffer->size; i++, element++) {
1124 m = element->method;
1127 utf_display_classname(m->class->name);
1129 utf_display(m->name);
1130 utf_display(m->descriptor);
1132 if (m->flags & ACC_NATIVE) {
1133 printf("(Native Method)\n");
1137 utf_display(m->class->sourcefile);
1138 printf(":%d)\n", (u4) element->linenumber);
1150 * These are local overrides for various environment variables in Emacs.
1151 * Please do not remove this and leave it at the end of the file, where
1152 * Emacs will automagically detect them.
1153 * ---------------------------------------------------------------------
1156 * indent-tabs-mode: t