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 2975 2005-07-10 22:20:21Z 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"
64 /*JoWenn: simplify collectors (trace doesn't contain internal methods)*/
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
71 #if defined(_ALPHA_) || defined(__X86_64__)
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*/
81 } lineNumberTableEntry;
83 typedef struct lineNumberTableEntryInlineBegin {
84 /*this should have the same layout and size as the lineNumberTableEntry*/
85 LineNumber lineNrOuter;
87 } lineNumberTableEntryInlineBegin;
90 typedef void(*CacaoStackTraceCollector)(void **,stackTraceBuffer*);
92 #define BLOCK_INITIALSIZE 40
93 #define BLOCK_SIZEINCREMENT 40
96 /* stacktrace_create_inline_stackframeinfo *************************************
98 Creates an stackframe info structure for an inline exception.
100 *******************************************************************************/
102 void stacktrace_create_inline_stackframeinfo(stackframeinfo *sfi, u1 *pv,
103 u1 *sp, functionptr ra)
107 /* get current stackframe info pointer */
109 osfi = builtin_asm_get_stackframeinfo();
111 /* sometimes we don't have pv in asmpart.S handy */
114 pv = (u1 *) (ptrint) codegen_findmethod(ra);
116 /* fill new stackframe info structure */
118 sfi->oldThreadspecificHeadValue = *osfi;
119 sfi->addressOfThreadspecificHead = osfi;
122 sfi->beginOfJavaStackframe = sp;
123 sfi->returnToFromNative = ra;
125 /* store new stackframe info pointer */
131 /* stacktrace_create_native_stackframeinfo *************************************
133 Creates a stackframe info structure for a native stub.
135 *******************************************************************************/
137 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
138 u1 *sp, functionptr ra)
143 /* get methodinfo pointer from data segment */
145 m = *((methodinfo **) (pv + MethodPointer));
147 /* get current stackframe info pointer */
149 osfi = builtin_asm_get_stackframeinfo();
151 /* fill new stackframe info structure */
153 sfi->oldThreadspecificHeadValue = *osfi;
154 sfi->addressOfThreadspecificHead = osfi;
157 sfi->beginOfJavaStackframe = sp;
158 sfi->returnToFromNative = ra;
160 /* store new stackframe info pointer */
166 /* stacktrace_remove_stackframeinfo ********************************************
170 *******************************************************************************/
172 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
176 /* get address of pointer */
178 osfi = sfi->addressOfThreadspecificHead;
180 /* restore the old pointer */
182 *osfi = sfi->oldThreadspecificHeadValue;
186 /* stacktrace_new_arithmeticexception ******************************************
188 Creates an ArithemticException for inline stub.
190 *******************************************************************************/
192 java_objectheader *stacktrace_new_arithmeticexception(u1 *pv, u1 *sp,
196 java_objectheader *o;
198 /* create stackframeinfo */
200 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
202 /* create exception */
204 o = new_arithmeticexception();
206 /* remove stackframeinfo */
208 stacktrace_remove_stackframeinfo(&sfi);
214 /* stacktrace_new_arrayindexoutofboundsexception *******************************
216 Creates an ArrayIndexOutOfBoundsException for inline stub.
218 *******************************************************************************/
220 java_objectheader *stacktrace_new_arrayindexoutofboundsexception(u1 *pv,
226 java_objectheader *o;
228 /* create stackframeinfo */
230 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
232 /* create exception */
234 o = new_arrayindexoutofboundsexception(index);
236 /* remove stackframeinfo */
238 stacktrace_remove_stackframeinfo(&sfi);
244 /* stacktrace_new_arraystoreexception ******************************************
246 Creates an ArrayStoreException for inline stub.
248 *******************************************************************************/
250 java_objectheader *stacktrace_new_arraystoreexception(u1 *pv, u1 *sp,
254 java_objectheader *o;
256 /* create stackframeinfo */
258 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
260 /* create exception */
262 o = new_arraystoreexception();
264 /* remove stackframeinfo */
266 stacktrace_remove_stackframeinfo(&sfi);
272 /* stacktrace_new_classcastexception *******************************************
274 Creates an ClassCastException for inline stub.
276 *******************************************************************************/
278 java_objectheader *stacktrace_new_classcastexception(u1 *pv, u1 *sp,
282 java_objectheader *o;
284 /* create stackframeinfo */
286 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
288 /* create exception */
290 o = new_classcastexception();
292 /* remove stackframeinfo */
294 stacktrace_remove_stackframeinfo(&sfi);
300 /* stacktrace_new_negativearraysizeexception ***********************************
302 Creates an NegativeArraySizeException for inline stub.
304 *******************************************************************************/
306 java_objectheader *stacktrace_new_negativearraysizeexception(u1 *pv, u1 *sp,
310 java_objectheader *o;
312 /* create stackframeinfo */
314 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
316 /* create exception */
318 o = new_negativearraysizeexception();
320 /* remove stackframeinfo */
322 stacktrace_remove_stackframeinfo(&sfi);
328 /* stacktrace_new_nullpointerexception *****************************************
330 Creates an NullPointerException for inline stub.
332 *******************************************************************************/
334 java_objectheader *stacktrace_new_nullpointerexception(u1 *pv, u1 *sp,
338 java_objectheader *o;
340 /* create stackframeinfo */
342 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
344 /* create exception */
346 o = new_nullpointerexception();
348 /* remove stackframeinfo */
350 stacktrace_remove_stackframeinfo(&sfi);
356 /* stacktrace_fillInStackTrace *************************************************
358 Fills in the correct stacktrace into an existing exception object.
360 *******************************************************************************/
362 java_objectheader *stacktrace_fillInStackTrace(u1 *pv, u1 *sp, functionptr ra)
365 java_objectheader *o;
368 /* create stackframeinfo */
370 stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra);
376 /* clear exception */
378 *exceptionptr = NULL;
380 /* resolve methodinfo pointer from exception object */
382 m = class_resolvemethod(o->vftbl->class,
383 utf_fillInStackTrace,
384 utf_void__java_lang_Throwable);
388 asm_calljavafunction(m, o, NULL, NULL, NULL);
390 /* remove stackframeinfo */
392 stacktrace_remove_stackframeinfo(&sfi);
398 static void addEntry(stackTraceBuffer* buffer,methodinfo*method ,LineNumber line) {
399 if (buffer->size>buffer->full) {
400 stacktraceelement *tmp=&(buffer->start[buffer->full]);
402 tmp->linenumber=line;
403 buffer->full = buffer->full + 1;
404 #if (defined(JWDEBUG) || defined (JWDEBUG2))
405 log_text("addEntry (stacktrace):");
406 printf("method %p\n",method);
407 if (method) printf("method->name %p\n",method->name);
408 if (method) utf_display(method->name); else printf("Native");
409 if (method) {printf("\n");utf_display(method->class->name);}
410 printf("\nnext buffer item %d\nLine:%ld\n",buffer->full,line);
413 stacktraceelement *newBuffer;
416 log_text("stacktrace buffer full, resizing");
420 (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
421 sizeof(stacktraceelement));
424 log_text("OOM during stacktrace creation");
428 memcpy(newBuffer,buffer->start,buffer->size*sizeof(stacktraceelement));
429 if (buffer->needsFree) free(buffer->start);
430 buffer->start=newBuffer;
431 buffer->size=buffer->size+BLOCK_SIZEINCREMENT;
433 addEntry(buffer,method,line);
438 /* stacktrace_fillInStackTrace_methodRecursive *********************************
442 *******************************************************************************/
444 static int stacktrace_fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,
446 lineNumberTableEntry *startEntry,
447 lineNumberTableEntry **entry,
448 size_t *entriesAhead,
452 size_t ahead=*entriesAhead;
453 lineNumberTableEntry *ent=*entry;
454 lineNumberTableEntryInlineBegin *ilStart;
456 for (; ahead > 0; ahead--, ent++) {
457 if (address >= ent->pc) {
458 switch (ent->lineNr) {
459 case -1: /* begin of inlined method */
460 ilStart=(lineNumberTableEntryInlineBegin*)(++ent);
463 if (stacktrace_fillInStackTrace_methodRecursive(buffer,ilStart->method,ent,&ent,&ahead,address)) {
464 addEntry(buffer,method,ilStart->lineNrOuter);
468 case -2: /* end of inlined method */
474 if (address == ent->pc) {
475 addEntry(buffer, method, ent->lineNr);
482 if (address > startEntry->pc) {
484 addEntry(buffer, method, ent->lineNr);
488 printf("trace point: %p\n", address);
489 log_text("trace point before method");
496 addEntry(buffer, method, ent->lineNr);
501 /* stacktrace_fillInStackTrace_method ******************************************
505 *******************************************************************************/
507 static void stacktrace_fillInStackTrace_method(stackTraceBuffer *buffer,
508 methodinfo *method, u1 *dataSeg,
511 size_t lineNumberTableSize=(*((size_t*)(dataSeg+LineNumberTableSize)));
512 lineNumberTableEntry *ent;
514 lineNumberTableEntry *startEntry;
516 if (lineNumberTableSize == 0) {
517 /* right now this happens only on i386,if the native stub causes an */
518 /* exception in a <clinit> invocation (jowenn) */
520 addEntry(buffer, method, 0);
524 calc = (void **) (dataSeg + LineNumberTableStart);
525 ent = (lineNumberTableEntry *) (((char *) (*calc) - (sizeof(lineNumberTableEntry) - SIZEOF_VOID_P)));
527 ent -= (lineNumberTableSize - 1);
530 if (!stacktrace_fillInStackTrace_methodRecursive(buffer, method,
532 &lineNumberTableSize,
534 log_text("Trace point not found in suspected method");
541 /* cacao_stacktrace_fillInStackTrace *******************************************
545 *******************************************************************************/
547 void cacao_stacktrace_fillInStackTrace(void **target,
548 CacaoStackTraceCollector coll)
550 stacktraceelement primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)];
551 stackTraceBuffer buffer;
552 stackframeinfo *info;
560 /* In most cases this should be enough -> one malloc less. I don't think */
561 /* temporary data should be allocated with the GC, only the result. */
563 buffer.needsFree = 0;
564 buffer.start = primaryBlock;
565 buffer.size = BLOCK_INITIALSIZE; /* *sizeof(stacktraceelement); */
568 info = *((void **) builtin_asm_get_stackframeinfo());
578 /* some builtin native */
582 ra = (functionptr) info->returnToFromNative;
586 utf_display_classname(m->class->name);
588 utf_display(m->name);
589 utf_display(m->descriptor);
590 printf(": native\n");
592 addEntry(&buffer, m, 0);
594 printf("NULL: native\n");
598 addEntry(&buffer, m, 0);
602 /* get data segment address */
605 /* this is an inline info */
610 /* this is an native stub info */
612 pv = (u1 *) (ptrint) codegen_findmethod(ra);
615 /* get methodinfo pointer from data segment */
617 m = *((methodinfo **) (pv + MethodPointer));
619 /* get stackpointer from stackframeinfo structure */
621 sp = (u1 *) info->beginOfJavaStackframe;
623 info = info->oldThreadspecificHeadValue;
629 utf_display_classname(m->class->name);
631 utf_display(m->name);
632 utf_display(m->descriptor);
636 /* add the current method to the stacktrace */
638 stacktrace_fillInStackTrace_method(&buffer, m, pv,
639 (u1 *) ((ptrint) ra) - 1);
641 /* get the current stack frame size */
643 framesize = *((u4 *) (pv + FrameSize));
645 /* get return address of current stack frame */
647 ra = md_stacktrace_get_returnaddress(sp, framesize);
649 /* get data segment and methodinfo pointer from parent method */
651 pv = (u1 *) (ptrint) codegen_findmethod(ra);
652 m = *((methodinfo **) (pv + MethodPointer));
656 #if defined(__I386__) || defined (__X86_64__)
657 sp += framesize + SIZEOF_VOID_P;
665 coll(target, &buffer);
667 if (buffer.needsFree)
678 void stackTraceCollector(void **target, stackTraceBuffer *buffer) {
679 stackTraceBuffer *dest=*target=heap_allocate(sizeof(stackTraceBuffer)+buffer->full*sizeof(stacktraceelement),true,0);
680 memcpy(*target,buffer,sizeof(stackTraceBuffer));
681 memcpy(dest+1,buffer->start,buffer->full*sizeof(stacktraceelement));
684 dest->size=dest->full;
685 dest->start=(stacktraceelement*)(dest+1);
688 if (buffer->full>0) {
689 printf("SOURCE BUFFER:%s\n",buffer->start[0].method->name->text);
690 printf("DEST BUFFER:%s\n",dest->start[0].method->name->text);
691 } else printf("Buffer is empty\n");
696 void cacao_stacktrace_NormalTrace(void **target)
698 cacao_stacktrace_fillInStackTrace(target, &stackTraceCollector);
703 static void classContextCollector(void **target, stackTraceBuffer *buffer)
705 java_objectarray *tmpArray;
706 stacktraceelement *current;
707 stacktraceelement *start;
715 for (i = 0; i < size; i++)
716 if (buffer->start[i].method != 0)
719 start = buffer->start;
723 if (targetSize > 0) {
724 if (start->method && (start->method->class == class_java_lang_SecurityManager)) {
730 tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
732 for(i = 0, current = start; i < targetSize; i++, current++) {
733 /* XXX TWISTI: should we use this skipping for native stubs? */
735 if (!current->method) {
740 use_class_as_object(current->method->class);
742 tmpArray->data[i] = (java_objectheader *) current->method->class;
750 java_objectarray *cacao_createClassContextArray(void)
752 java_objectarray *array=0;
754 cacao_stacktrace_fillInStackTrace((void **) &array, &classContextCollector);
760 /* stacktrace_classLoaderCollector *********************************************
764 *******************************************************************************/
766 static void stacktrace_classLoaderCollector(void **target,
767 stackTraceBuffer *buffer)
769 stacktraceelement *current;
770 stacktraceelement *start;
776 start = &(buffer->start[0]);
778 for(i = 0, current = start; i < size; i++, current++) {
784 if (m->class == class_java_security_PrivilegedAction) {
789 if (m->class->classloader) {
790 *target = (java_lang_ClassLoader *) m->class->classloader;
799 /* cacao_currentClassLoader ****************************************************
803 *******************************************************************************/
805 java_objectheader *cacao_currentClassLoader(void)
807 java_objectheader *header=0;
809 cacao_stacktrace_fillInStackTrace((void**)&header,
810 &stacktrace_classLoaderCollector);
817 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {
818 if (buffer->full >2) (*target)=buffer->start[2].method;
822 methodinfo *cacao_callingMethod() {
824 cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
830 void getStackCollector(void **target, stackTraceBuffer *buffer)
832 java_objectarray *classes;
833 java_objectarray *methodnames;
834 java_objectarray **result=(java_objectarray**)target;
835 java_lang_String *str;
837 stacktraceelement *current;
840 /*log_text("getStackCollector");*/
844 *result = builtin_anewarray(2, arrayclass_java_lang_Object);
849 classes = builtin_anewarray(size, class_java_lang_Class);
854 methodnames = builtin_anewarray(size, class_java_lang_String);
859 (*result)->data[0] = (java_objectheader *) classes;
860 (*result)->data[1] = (java_objectheader *) methodnames;
862 /*log_text("Before for loop");*/
863 for (i = 0, current = &(buffer->start[0]); i < size; i++, current++) {
864 /*log_text("In loop");*/
865 c = current->method->class;
866 use_class_as_object(c);
867 classes->data[i] = (java_objectheader *) c;
868 str = javastring_new(current->method->name);
871 methodnames->data[i] = (java_objectheader *) str;
872 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
875 /*if (*exceptionptr) panic("Exception in getStackCollector");*/
877 /*log_text("loop left");*/
883 java_objectarray *cacao_getStackForVMAccessController(void)
885 java_objectarray *result = NULL;
887 cacao_stacktrace_fillInStackTrace((void **) &result, &getStackCollector);
893 /* stacktrace_dump_trace *******************************************************
895 This method is call from signal_handler_sigusr1 to dump the
896 stacktrace of the current thread to stdout.
898 *******************************************************************************/
900 void stacktrace_dump_trace(void)
902 stackTraceBuffer *buffer;
903 stacktraceelement *element;
908 /* get thread stackframeinfo */
910 info = &THREADINFO->_stackframeinfo;
912 /* fill stackframeinfo structure */
914 tmp.oldThreadspecificHeadValue = *info;
915 tmp.addressOfThreadspecificHead = info;
917 tmp.beginOfJavaStackframe = NULL;
918 tmp.returnToFromNative = _mc->gregs[REG_RIP];
923 /* generate stacktrace */
925 cacao_stacktrace_NormalTrace((void **) &buffer);
927 /* print stacktrace */
930 element = buffer->start;
932 for (i = 0; i < buffer->size; i++, element++) {
936 utf_display_classname(m->class->name);
938 utf_display(m->name);
939 utf_display(m->descriptor);
941 if (m->flags & ACC_NATIVE) {
942 printf("(Native Method)\n");
946 utf_display(m->class->sourcefile);
947 printf(":%d)\n", (u4) element->linenumber);
959 * These are local overrides for various environment variables in Emacs.
960 * Please do not remove this and leave it at the end of the file, where
961 * Emacs will automagically detect them.
962 * ---------------------------------------------------------------------
965 * indent-tabs-mode: t