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 2954 2005-07-09 13:49:50Z 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 #if defined(__I386__) || defined(__X86_64__)
112 /* we don't have pv in asm_wrapper_patcher handy */
115 pv = (u1 *) codegen_findmethod(ra);
118 /* fill new stackframe info structure */
120 sfi->oldThreadspecificHeadValue = *osfi;
121 sfi->addressOfThreadspecificHead = osfi;
124 sfi->beginOfJavaStackframe = sp;
125 sfi->returnToFromNative = ra;
127 /* store new stackframe info pointer */
133 /* stacktrace_create_native_stackframeinfo *************************************
135 Creates a stackframe info structure for a native stub.
137 *******************************************************************************/
139 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
140 u1 *sp, functionptr ra)
145 /* get methodinfo pointer from data segment */
147 m = *((methodinfo **) (pv + MethodPointer));
149 /* get current stackframe info pointer */
151 osfi = builtin_asm_get_stackframeinfo();
153 /* fill new stackframe info structure */
155 sfi->oldThreadspecificHeadValue = *osfi;
156 sfi->addressOfThreadspecificHead = osfi;
159 sfi->beginOfJavaStackframe = sp;
160 sfi->returnToFromNative = ra;
162 /* store new stackframe info pointer */
168 /* stacktrace_remove_stackframeinfo ********************************************
172 *******************************************************************************/
174 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
178 /* get address of pointer */
180 osfi = sfi->addressOfThreadspecificHead;
182 /* restore the old pointer */
184 *osfi = sfi->oldThreadspecificHeadValue;
188 /* stacktrace_call_fillInStackTrace ********************************************
192 *******************************************************************************/
194 void stacktrace_call_fillInStackTrace(java_objectheader *o)
198 /* resolve methodinfo pointer from exception object */
200 m = class_resolvemethod(o->vftbl->class,
201 utf_fillInStackTrace,
202 utf_void__java_lang_Throwable);
206 asm_calljavafunction(m, o, NULL, NULL, NULL);
210 static void addEntry(stackTraceBuffer* buffer,methodinfo*method ,LineNumber line) {
211 if (buffer->size>buffer->full) {
212 stacktraceelement *tmp=&(buffer->start[buffer->full]);
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);
225 stacktraceelement *newBuffer;
228 log_text("stacktrace buffer full, resizing");
232 (stacktraceelement *) malloc((buffer->size + BLOCK_SIZEINCREMENT) *
233 sizeof(stacktraceelement));
236 log_text("OOM during stacktrace creation");
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;
245 addEntry(buffer,method,line);
250 /* stacktrace_fillInStackTrace_methodRecursive *********************************
254 *******************************************************************************/
256 static int stacktrace_fillInStackTrace_methodRecursive(stackTraceBuffer *buffer,
258 lineNumberTableEntry *startEntry,
259 lineNumberTableEntry **entry,
260 size_t *entriesAhead,
264 size_t ahead=*entriesAhead;
265 lineNumberTableEntry *ent=*entry;
266 lineNumberTableEntryInlineBegin *ilStart;
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);
275 if (stacktrace_fillInStackTrace_methodRecursive(buffer,ilStart->method,ent,&ent,&ahead,address)) {
276 addEntry(buffer,method,ilStart->lineNrOuter);
280 case -2: /* end of inlined method */
286 if (address == ent->pc) {
287 addEntry(buffer, method, ent->lineNr);
294 if (address > startEntry->pc) {
296 addEntry(buffer, method, ent->lineNr);
300 printf("trace point: %p\n", address);
301 log_text("trace point before method");
308 addEntry(buffer, method, ent->lineNr);
313 /* stacktrace_fillInStackTrace_method ******************************************
317 *******************************************************************************/
319 static void stacktrace_fillInStackTrace_method(stackTraceBuffer *buffer,
320 methodinfo *method, u1 *dataSeg,
323 size_t lineNumberTableSize=(*((size_t*)(dataSeg+LineNumberTableSize)));
324 lineNumberTableEntry *ent;
326 lineNumberTableEntry *startEntry;
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) */
332 addEntry(buffer, method, 0);
336 calc = (void **) (dataSeg + LineNumberTableStart);
337 ent = (lineNumberTableEntry *) (((char *) (*calc) - (sizeof(lineNumberTableEntry) - SIZEOF_VOID_P)));
339 ent -= (lineNumberTableSize - 1);
342 if (!stacktrace_fillInStackTrace_methodRecursive(buffer, method,
344 &lineNumberTableSize,
346 log_text("Trace point not found in suspected method");
353 /* cacao_stacktrace_fillInStackTrace *******************************************
357 *******************************************************************************/
359 void cacao_stacktrace_fillInStackTrace(void **target,
360 CacaoStackTraceCollector coll)
362 stacktraceelement primaryBlock[BLOCK_INITIALSIZE*sizeof(stacktraceelement)];
363 stackTraceBuffer buffer;
364 stackframeinfo *info;
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. */
375 buffer.needsFree = 0;
376 buffer.start = primaryBlock;
377 buffer.size = BLOCK_INITIALSIZE; /* *sizeof(stacktraceelement); */
380 info = *((void **) builtin_asm_get_stackframeinfo());
390 /* some builtin native */
394 ra = (functionptr) info->returnToFromNative;
398 utf_display_classname(m->class->name);
400 utf_display(m->name);
401 utf_display(m->descriptor);
402 printf(": native\n");
404 addEntry(&buffer, m, 0);
406 printf("NULL: native\n");
410 addEntry(&buffer, m, 0);
414 /* get data segment address */
417 /* this is an inline info */
422 /* this is an native stub info */
424 pv = (u1 *) codegen_findmethod(ra);
427 /* get methodinfo pointer from data segment */
429 m = *((methodinfo **) (pv + MethodPointer));
431 /* get stackpointer from stackframeinfo structure */
433 sp = (u1 *) info->beginOfJavaStackframe;
435 info = info->oldThreadspecificHeadValue;
441 utf_display_classname(m->class->name);
443 utf_display(m->name);
444 utf_display(m->descriptor);
448 /* add the current method to the stacktrace */
450 stacktrace_fillInStackTrace_method(&buffer, m, pv,
453 /* get the current stack frame size */
455 framesize = *((u4 *) (pv + FrameSize));
457 /* get return address of current stack frame */
459 ra = md_stacktrace_get_returnaddress(sp, framesize);
461 /* get data segment and methodinfo pointer from parent method */
463 pv = (u1 *) codegen_findmethod(ra);
464 m = *((methodinfo **) (pv + MethodPointer));
468 #if defined(__I386__) || defined (__X86_64__)
469 sp += framesize + SIZEOF_VOID_P;
477 coll(target, &buffer);
479 if (buffer.needsFree)
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));
496 dest->size=dest->full;
497 dest->start=(stacktraceelement*)(dest+1);
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");
508 void cacao_stacktrace_NormalTrace(void **target)
510 cacao_stacktrace_fillInStackTrace(target, &stackTraceCollector);
515 static void classContextCollector(void **target, stackTraceBuffer *buffer)
517 java_objectarray *tmpArray;
518 stacktraceelement *current;
519 stacktraceelement *start;
527 for (i = 0; i < size; i++)
528 if (buffer->start[i].method != 0)
531 start = buffer->start;
535 if (targetSize > 0) {
536 if (start->method && (start->method->class == class_java_lang_SecurityManager)) {
542 tmpArray = builtin_anewarray(targetSize, class_java_lang_Class);
544 for(i = 0, current = start; i < targetSize; i++, current++) {
545 /* XXX TWISTI: should we use this skipping for native stubs? */
547 if (!current->method) {
552 use_class_as_object(current->method->class);
554 tmpArray->data[i] = (java_objectheader *) current->method->class;
562 java_objectarray *cacao_createClassContextArray(void)
564 java_objectarray *array=0;
566 cacao_stacktrace_fillInStackTrace((void **) &array, &classContextCollector);
572 /* stacktrace_classLoaderCollector *********************************************
576 *******************************************************************************/
578 static void stacktrace_classLoaderCollector(void **target,
579 stackTraceBuffer *buffer)
581 stacktraceelement *current;
582 stacktraceelement *start;
588 start = &(buffer->start[0]);
590 for(i = 0, current = start; i < size; i++, current++) {
596 if (m->class == class_java_security_PrivilegedAction) {
601 if (m->class->classloader) {
602 *target = (java_lang_ClassLoader *) m->class->classloader;
611 /* cacao_currentClassLoader ****************************************************
615 *******************************************************************************/
617 java_objectheader *cacao_currentClassLoader(void)
619 java_objectheader *header=0;
621 cacao_stacktrace_fillInStackTrace((void**)&header,
622 &stacktrace_classLoaderCollector);
629 void callingMethodCollector(void **target, stackTraceBuffer *buffer) {
630 if (buffer->full >2) (*target)=buffer->start[2].method;
634 methodinfo *cacao_callingMethod() {
636 cacao_stacktrace_fillInStackTrace((void**)&method,&callingMethodCollector);
642 void getStackCollector(void **target, stackTraceBuffer *buffer)
644 java_objectarray *classes;
645 java_objectarray *methodnames;
646 java_objectarray **result=(java_objectarray**)target;
647 java_lang_String *str;
649 stacktraceelement *current;
652 /*log_text("getStackCollector");*/
656 *result = builtin_anewarray(2, arrayclass_java_lang_Object);
661 classes = builtin_anewarray(size, class_java_lang_Class);
666 methodnames = builtin_anewarray(size, class_java_lang_String);
671 (*result)->data[0] = (java_objectheader *) classes;
672 (*result)->data[1] = (java_objectheader *) methodnames;
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);
683 methodnames->data[i] = (java_objectheader *) str;
684 /*printf("getStackCollector: %s.%s\n",c->name->text,current->method->name->text);*/
687 /*if (*exceptionptr) panic("Exception in getStackCollector");*/
689 /*log_text("loop left");*/
695 java_objectarray *cacao_getStackForVMAccessController(void)
697 java_objectarray *result = NULL;
699 cacao_stacktrace_fillInStackTrace((void **) &result, &getStackCollector);
705 /* stacktrace_dump_trace *******************************************************
707 This method is call from signal_handler_sigusr1 to dump the
708 stacktrace of the current thread to stdout.
710 *******************************************************************************/
712 void stacktrace_dump_trace(void)
714 stackTraceBuffer *buffer;
715 stacktraceelement *element;
720 /* get thread stackframeinfo */
722 info = &THREADINFO->_stackframeinfo;
724 /* fill stackframeinfo structure */
726 tmp.oldThreadspecificHeadValue = *info;
727 tmp.addressOfThreadspecificHead = info;
729 tmp.beginOfJavaStackframe = NULL;
730 tmp.returnToFromNative = _mc->gregs[REG_RIP];
735 /* generate stacktrace */
737 cacao_stacktrace_NormalTrace((void **) &buffer);
739 /* print stacktrace */
742 element = buffer->start;
744 for (i = 0; i < buffer->size; i++, element++) {
748 utf_display_classname(m->class->name);
750 utf_display(m->name);
751 utf_display(m->descriptor);
753 if (m->flags & ACC_NATIVE) {
754 printf("(Native Method)\n");
758 utf_display(m->class->sourcefile);
759 printf(":%d)\n", (u4) element->linenumber);
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 * ---------------------------------------------------------------------
777 * indent-tabs-mode: t