63d8d0b1298631b0f6672b0abac54583b48a80ae
[cacao.git] / src / vm / jit / stacktrace.c
1 /* src/vm/jit/stacktrace.c - machine independet stacktrace system
2
3    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, 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., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    Contact: cacao@cacaojvm.org
26
27    Authors: Joseph Wenninger
28
29    Changes: Christian Thalinger
30             Edwin Steiner
31
32    $Id: stacktrace.c 4921 2006-05-15 14:24:36Z twisti $
33
34 */
35
36
37 #include "config.h"
38
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include "vm/types.h"
44
45 #include "mm/boehm.h"
46 #include "mm/memory.h"
47 #include "native/native.h"
48
49 #include "vm/global.h"                   /* required here for native includes */
50 #include "native/include/java_lang_ClassLoader.h"
51 #include "native/include/java_lang_Throwable.h"
52 #include "native/include/java_lang_VMThrowable.h"
53
54 #if defined(ENABLE_THREADS)
55 # include "threads/native/threads.h"
56 #else
57 # include "threads/none/threads.h"
58 #endif
59
60 #include "toolbox/logging.h"
61 #include "vm/builtin.h"
62 #include "vm/class.h"
63 #include "vm/exceptions.h"
64 #include "vm/loader.h"
65 #include "vm/options.h"
66 #include "vm/stringlocal.h"
67 #include "vm/vm.h"
68 #include "vm/jit/asmpart.h"
69 #include "vm/jit/codegen-common.h"
70 #include "vm/jit/methodheader.h"
71 #include "vm/cycles-stats.h"
72
73
74 /* linenumbertable_entry ******************************************************/
75
76 /* Keep the type of line the same as the pointer type, otherwise we
77    run into alignment troubles (like on MIPS64). */
78
79 typedef struct linenumbertable_entry linenumbertable_entry;
80
81 struct linenumbertable_entry {
82         ptrint  line;               /* NOTE: see doc/inlining_stacktrace.txt for  */
83         u1     *pc;                 /*       special meanings of line and pc.     */
84 };
85
86 /* global variables ***********************************************************/
87
88 #if !defined(ENABLE_THREADS)
89 stackframeinfo *_no_threads_stackframeinfo = NULL;
90 #endif
91
92 CYCLES_STATS_DECLARE(stacktrace_overhead        ,100,1)
93 CYCLES_STATS_DECLARE(stacktrace_fillInStackTrace,40,5000)
94 CYCLES_STATS_DECLARE(stacktrace_getClassContext ,40,5000)
95 CYCLES_STATS_DECLARE(stacktrace_getCurrentClass ,40,5000)
96 CYCLES_STATS_DECLARE(stacktrace_getStack        ,40,10000)
97
98
99 /* stacktrace_create_stackframeinfo ********************************************
100
101    Creates an stackframe info structure for inline code in the
102    interpreter.
103
104 *******************************************************************************/
105
106 #if defined(ENABLE_INTRP)
107 void stacktrace_create_stackframeinfo(stackframeinfo *sfi, u1 *pv, u1 *sp,
108                                                                           u1 *ra)
109 {
110         stackframeinfo **psfi;
111         methodinfo      *m;
112
113         /* get current stackframe info pointer */
114
115         psfi = STACKFRAMEINFO;
116
117         /* if we don't have pv handy */
118
119         if (pv == NULL) {
120 #if defined(ENABLE_INTRP)
121                 if (opt_intrp)
122                         pv = codegen_findmethod(ra);
123                 else
124 #endif
125                         {
126 #if defined(ENABLE_JIT)
127                                 pv = md_codegen_findmethod(ra);
128 #endif
129                         }
130         }
131
132         /* get methodinfo pointer from data segment */
133
134         m = *((methodinfo **) (pv + MethodPointer));
135
136         /* fill new stackframe info structure */
137
138         sfi->prev   = *psfi;
139         sfi->method = m;
140         sfi->pv     = pv;
141         sfi->sp     = sp;
142         sfi->ra     = ra;
143
144         /* xpc is the same as ra, but is required in stacktrace_create */
145
146         sfi->xpc    = ra;
147
148         /* store new stackframe info pointer */
149
150         *psfi = sfi;
151 }
152 #endif /* defined(ENABLE_INTRP) */
153
154
155 /* stacktrace_create_inline_stackframeinfo *************************************
156
157    Creates an stackframe info structure for an inline exception stub.
158
159 *******************************************************************************/
160
161 void stacktrace_create_inline_stackframeinfo(stackframeinfo *sfi, u1 *pv,
162                                                                                          u1 *sp, u1 *ra, u1 *xpc)
163 {
164         stackframeinfo **psfi;
165
166         /* get current stackframe info pointer */
167
168         psfi = STACKFRAMEINFO;
169
170 #if defined(ENABLE_INTRP)
171         if (opt_intrp) {
172                 /* if we don't have pv handy */
173
174                 if (pv == NULL)
175                         pv = codegen_findmethod(ra);
176
177         }
178 #endif
179
180         /* fill new stackframe info structure */
181
182         sfi->prev   = *psfi;
183         sfi->method = NULL;
184         sfi->pv     = pv;
185         sfi->sp     = sp;
186         sfi->ra     = ra;
187         sfi->xpc    = xpc;
188
189         /* store new stackframe info pointer */
190
191         *psfi = sfi;
192 }
193
194
195 /* stacktrace_create_extern_stackframeinfo *************************************
196
197    Creates an stackframe info structure for an extern exception
198    (hardware or assembler).
199
200 *******************************************************************************/
201
202 void stacktrace_create_extern_stackframeinfo(stackframeinfo *sfi, u1 *pv,
203                                                                                          u1 *sp, u1 *ra, u1 *xpc)
204 {
205         stackframeinfo **psfi;
206 #if !defined(__I386__) && !defined(__X86_64__)
207         bool             isleafmethod;
208 #endif
209 #if defined(ENABLE_JIT)
210         s4               framesize;
211 #endif
212
213         /* get current stackframe info pointer */
214
215         psfi = STACKFRAMEINFO;
216
217         /* sometimes we don't have pv handy (e.g. in asmpart.S:
218        L_asm_call_jit_compiler_exception or in the interpreter). */
219
220         if (pv == NULL) {
221 #if defined(ENABLE_INTRP)
222                 if (opt_intrp)
223                         pv = codegen_findmethod(ra);
224                 else
225 #endif
226                         {
227 #if defined(ENABLE_JIT)
228                                 pv = md_codegen_findmethod(ra);
229 #endif
230                         }
231         }
232
233 #if defined(ENABLE_JIT)
234 # if defined(ENABLE_INTRP)
235         /* When using the interpreter, we pass RA to the function. */
236
237         if (!opt_intrp) {
238 # endif
239 # if defined(__I386__) || defined(__X86_64__)
240                 /* On i386 and x86_64 we always have to get the return address
241                    from the stack. */
242
243                 framesize = *((u4 *) (pv + FrameSize));
244
245                 ra = md_stacktrace_get_returnaddress(sp, framesize);
246 # else
247                 /* If the method is a non-leaf function, we need to get the return
248                    address from the stack. For leaf functions the return address
249                    is set correctly. This makes the assembler and the signal
250                    handler code simpler. */
251
252                 isleafmethod = *((s4 *) (pv + IsLeaf));
253
254                 if (!isleafmethod) {
255                         framesize = *((u4 *) (pv + FrameSize));
256
257                         ra = md_stacktrace_get_returnaddress(sp, framesize);
258                 }
259 # endif
260 # if defined(ENABLE_INTRP)
261         }
262 # endif
263 #endif /* defined(ENABLE_JIT) */
264
265         /* fill new stackframe info structure */
266
267         sfi->prev   = *psfi;
268         sfi->method = NULL;
269         sfi->pv     = pv;
270         sfi->sp     = sp;
271         sfi->ra     = ra;
272         sfi->xpc    = xpc;
273
274         /* store new stackframe info pointer */
275
276         *psfi = sfi;
277 }
278
279
280 /* stacktrace_create_native_stackframeinfo *************************************
281
282    Creates a stackframe info structure for a native stub.
283
284 *******************************************************************************/
285
286 void stacktrace_create_native_stackframeinfo(stackframeinfo *sfi, u1 *pv,
287                                                                                          u1 *sp, u1 *ra)
288 {
289         stackframeinfo **psfi;
290         methodinfo      *m;
291
292         /* get methodinfo pointer from data segment */
293
294         m = *((methodinfo **) (pv + MethodPointer));
295
296         /* get current stackframe info pointer */
297
298         psfi = STACKFRAMEINFO;
299
300         /* fill new stackframe info structure */
301
302         sfi->prev   = *psfi;
303         sfi->method = m;
304         sfi->pv     = NULL;
305         sfi->sp     = sp;
306         sfi->ra     = ra;
307         sfi->xpc    = NULL;
308
309         /* store new stackframe info pointer */
310
311         *psfi = sfi;
312 }
313
314
315 /* stacktrace_remove_stackframeinfo ********************************************
316
317    XXX
318
319 *******************************************************************************/
320
321 void stacktrace_remove_stackframeinfo(stackframeinfo *sfi)
322 {
323         stackframeinfo **psfi;
324
325         /* get current stackframe info pointer */
326
327         psfi = STACKFRAMEINFO;
328
329         /* restore the old pointer */
330
331         *psfi = sfi->prev;
332 }
333
334
335 /* stacktrace_inline_arithmeticexception ***************************************
336
337    Creates an ArithemticException for inline stub.
338
339 *******************************************************************************/
340
341 java_objectheader *stacktrace_inline_arithmeticexception(u1 *pv, u1 *sp,
342                                                                                                                  u1 *ra, u1 *xpc)
343 {
344         stackframeinfo     sfi;
345         java_objectheader *o;
346
347         /* create stackframeinfo */
348
349         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
350
351         /* create exception */
352
353         o = new_arithmeticexception();
354
355         /* remove stackframeinfo */
356
357         stacktrace_remove_stackframeinfo(&sfi);
358
359         return o;
360 }
361
362
363 /* stacktrace_inline_arrayindexoutofboundsexception ****************************
364
365    Creates an ArrayIndexOutOfBoundsException for inline stub.
366
367 *******************************************************************************/
368
369 java_objectheader *stacktrace_inline_arrayindexoutofboundsexception(u1 *pv,
370                                                                                                                                         u1 *sp,
371                                                                                                                                         u1 *ra,
372                                                                                                                                         u1 *xpc,
373                                                                                                                                         s4 index)
374 {
375         stackframeinfo     sfi;
376         java_objectheader *o;
377
378         /* create stackframeinfo */
379
380         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
381
382         /* create exception */
383
384         o = new_arrayindexoutofboundsexception(index);
385
386         /* remove stackframeinfo */
387
388         stacktrace_remove_stackframeinfo(&sfi);
389
390         return o;
391 }
392
393
394 /* stacktrace_inline_arraystoreexception ***************************************
395
396    Creates an ArrayStoreException for inline stub.
397
398 *******************************************************************************/
399
400 java_objectheader *stacktrace_inline_arraystoreexception(u1 *pv, u1 *sp, u1 *ra,
401                                                                                                                  u1 *xpc)
402 {
403         stackframeinfo     sfi;
404         java_objectheader *o;
405
406         /* create stackframeinfo */
407
408         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
409
410         /* create exception */
411
412         o = new_arraystoreexception();
413
414         /* remove stackframeinfo */
415
416         stacktrace_remove_stackframeinfo(&sfi);
417
418         return o;
419 }
420
421
422 /* stacktrace_inline_classcastexception ****************************************
423
424    Creates an ClassCastException for inline stub.
425
426 *******************************************************************************/
427
428 java_objectheader *stacktrace_inline_classcastexception(u1 *pv, u1 *sp, u1 *ra,
429                                                                                                                 u1 *xpc)
430 {
431         stackframeinfo     sfi;
432         java_objectheader *o;
433
434         /* create stackframeinfo */
435
436         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
437
438         /* create exception */
439
440         o = new_classcastexception();
441
442         /* remove stackframeinfo */
443
444         stacktrace_remove_stackframeinfo(&sfi);
445
446         return o;
447 }
448
449
450 /* stacktrace_inline_nullpointerexception **************************************
451
452    Creates an NullPointerException for inline stub.
453
454 *******************************************************************************/
455
456 java_objectheader *stacktrace_inline_nullpointerexception(u1 *pv, u1 *sp,
457                                                                                                                   u1 *ra, u1 *xpc)
458 {
459         stackframeinfo     sfi;
460         java_objectheader *o;
461
462         /* create stackframeinfo */
463
464         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
465
466         /* create exception */
467
468         o = new_nullpointerexception();
469
470         /* remove stackframeinfo */
471
472         stacktrace_remove_stackframeinfo(&sfi);
473
474         return o;
475 }
476
477
478 /* stacktrace_inline_fillInStackTrace ******************************************
479
480    Fills in the correct stacktrace into an existing exception object
481    (this one is for inline exception stubs).
482
483 *******************************************************************************/
484
485 java_objectheader *stacktrace_inline_fillInStackTrace(u1 *pv, u1 *sp, u1 *ra,
486                                                                                                           u1 *xpc)
487 {
488         stackframeinfo     sfi;
489         java_objectheader *o;
490         methodinfo        *m;
491
492         /* create stackframeinfo */
493
494         stacktrace_create_inline_stackframeinfo(&sfi, pv, sp, ra, xpc);
495
496         /* get exception */
497
498         o = *exceptionptr;
499         assert(o);
500
501         /* clear exception */
502
503         *exceptionptr = NULL;
504
505         /* resolve methodinfo pointer from exception object */
506
507         m = class_resolvemethod(o->vftbl->class,
508                                                         utf_fillInStackTrace,
509                                                         utf_void__java_lang_Throwable);
510
511         /* call function */
512
513         (void) vm_call_method(m, o);
514
515         /* remove stackframeinfo */
516
517         stacktrace_remove_stackframeinfo(&sfi);
518
519         return o;
520 }
521
522
523 /* stacktrace_hardware_arithmeticexception *************************************
524
525    Creates an ArithemticException for inline stub.
526
527 *******************************************************************************/
528
529 java_objectheader *stacktrace_hardware_arithmeticexception(u1 *pv, u1 *sp,
530                                                                                                                    u1 *ra, u1 *xpc)
531 {
532         stackframeinfo     sfi;
533         java_objectheader *o;
534
535         /* create stackframeinfo */
536
537         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
538
539         /* create exception */
540
541         o = new_arithmeticexception();
542
543         /* remove stackframeinfo */
544
545         stacktrace_remove_stackframeinfo(&sfi);
546
547         return o;
548 }
549
550
551 /* stacktrace_hardware_nullpointerexception ************************************
552
553    Creates an NullPointerException for the SIGSEGV signal handler.
554
555 *******************************************************************************/
556
557 java_objectheader *stacktrace_hardware_nullpointerexception(u1 *pv, u1 *sp,
558                                                                                                                         u1 *ra, u1 *xpc)
559 {
560         stackframeinfo     sfi;
561         java_objectheader *o;
562
563         /* create stackframeinfo */
564
565         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
566
567         /* create exception */
568
569         o = new_nullpointerexception();
570
571         /* remove stackframeinfo */
572
573         stacktrace_remove_stackframeinfo(&sfi);
574
575         return o;
576 }
577
578
579 /* stacktrace_add_entry ********************************************************
580
581    Adds a new entry to the stacktrace buffer.
582
583 *******************************************************************************/
584
585 static void stacktrace_add_entry(stacktracebuffer *stb, methodinfo *m, u2 line)
586 {
587         stacktrace_entry *ste;
588
589         /* check if we already reached the buffer capacity */
590
591         if (stb->used >= stb->capacity) {
592                 /* reallocate new memory */
593
594                 stb->entries = DMREALLOC(stb->entries, stacktrace_entry, stb->capacity,
595                                                                  stb->capacity + STACKTRACE_CAPACITY_INCREMENT);
596
597                 /* set new buffer capacity */
598
599                 stb->capacity = stb->capacity + STACKTRACE_CAPACITY_INCREMENT;
600         }
601
602         /* insert the current entry */
603
604         ste = &(stb->entries[stb->used]);
605
606         ste->method     = m;
607         ste->linenumber = line;
608
609         /* increase entries used count */
610
611         stb->used += 1;
612 }
613
614
615 /* stacktrace_add_method_intern ************************************************
616
617    This function is used by stacktrace_add_method to search the line number
618    table for the line corresponding to a given pc. The function recurses for
619    inlined methods.
620
621 *******************************************************************************/
622
623 static bool stacktrace_add_method_intern(stacktracebuffer *stb, 
624                                                                                  methodinfo *m, 
625                                                                                  linenumbertable_entry *lntentry,
626                                                                                  ptrint lntsize,
627                                                                                  u1 *pc)
628 {
629         linenumbertable_entry *lntinline;   /* special entry for inlined method */
630
631         assert(stb);
632         assert(lntentry);
633
634         /* Find the line number for the specified PC (going backwards
635            in the linenumber table). The linenumber table size is zero
636            in native stubs. */
637
638         for (; lntsize > 0; lntsize--, lntentry--) {
639
640                 /* did we reach the current line? */
641
642                 /* Note: In case of inlining this may actually compare the pc
643                    against a methodinfo *, yielding a non-sensical
644                    result. This is no problem, however, as we ignore such
645                    entries in the switch below. This way we optimize for the
646                    common case (ie. a real pc in lntentry->pc). */
647
648                 if (pc >= lntentry->pc) {
649
650                         /* check for special inline entries (see
651                            doc/inlining_stacktrace.txt for details */
652
653                         if ((s4)lntentry->line < 0) {
654                                 switch (lntentry->line) {
655                                         case -1: 
656                                                 /* begin of inlined method (ie. INLINE_END
657                                                    instruction) */
658
659                                                 lntinline = --lntentry;/* get entry with methodinfo * */
660                                                 lntentry--;            /* skip the special entry      */
661                                                 lntsize -= 2;
662
663                                                 /* search inside the inlined method */
664                                                 if (stacktrace_add_method_intern(
665                                                                         stb, 
666                                                                         (methodinfo*) lntinline->pc,
667                                                                         lntentry,
668                                                                         lntsize,
669                                                                         pc))
670                                                 {
671                                                         /* the inlined method contained the pc */
672                                                         assert(lntinline->line <= -3);
673                                                         stacktrace_add_entry(stb, m, (-3) - lntinline->line);
674                                                         return true;
675                                                 }
676                                                 /* pc was not in inlined method, continue
677                                                    search.  Entries inside the inlined method
678                                                    will be skipped because their lntentry->pc
679                                                    is higher than pc.  */
680                                                 break;
681
682                                         case -2: 
683                                                 /* end of inlined method */
684                                                 return false;
685
686                                         /* default: is only reached for an -3-line entry
687                                            after a skipped -2 entry. We can safely ignore
688                                            it and continue searching.  */
689                                 }
690                         }
691                         else {
692                                 /* found a normal entry */
693                                 stacktrace_add_entry(stb, m, lntentry->line);
694                                 return true;
695                         }
696                 }
697         }
698
699         /* not found */
700         return false;
701 }
702
703 /* stacktrace_add_method *******************************************************
704
705    Add stacktrace entries[1] for the given method to the stacktrace buffer.
706
707    IN:
708        stb.........stacktracebuffer to fill
709            m...........method for which entries should be created
710            pv..........pv of method
711            pc..........position of program counter within the method's code
712
713    OUT:
714        true, if stacktrace entries were successfully created, false otherwise.
715
716    [1] In case of inlined methods there may be more than one stacktrace
717        entry for a codegen-level method. (see doc/inlining_stacktrace.txt)
718
719 *******************************************************************************/
720
721 static bool stacktrace_add_method(stacktracebuffer *stb, methodinfo *m, u1 *pv,
722                                                                   u1 *pc)
723 {
724         ptrint                 lntsize;     /* size of line number table          */
725         u1                    *lntstart;    /* start of line number table         */
726         linenumbertable_entry *lntentry;    /* points to last entry in the table  */
727         codeinfo              *code;        /* compiled realization of method     */
728
729         /* get size of line number table */
730
731         lntsize  = *((ptrint *) (pv + LineNumberTableSize));
732         lntstart = *((u1 **)    (pv + LineNumberTableStart));
733
734         /* Subtract the size of the line number entry of the structure,
735            since the line number table start points to the pc. */
736
737         lntentry = (linenumbertable_entry *) (lntstart - SIZEOF_VOID_P);
738
739         /* find the realization of the method the pc is in    */
740         /* XXX Note: This is preliminary. It would be cleaner */
741         /* to get the codeinfo * from the PV                  */
742
743         code = m->code;
744         while (1) {
745                 if (!code) {
746 #ifndef NDEBUG
747                         method_println(m);
748                         dolog("Could not find codeinfo for Current PC: %p",(void*)pc);
749 #endif
750                         abort();
751                 }
752
753                 if (((ptrint)pc >= (ptrint)code->entrypoint)
754                                 &&
755                         ( (pc - (u1*)code->entrypoint) < code->mcodelength ))
756                 {
757                         /* found */
758                         break;
759                 }
760
761                 code = code->prev;
762         }
763
764         /* search the line number table */
765
766         if (stacktrace_add_method_intern(stb, m, lntentry, lntsize, pc))
767                 return true;
768
769         /* If we get here, just add the entry with line number 0. */
770
771         stacktrace_add_entry(stb, m, 0);
772
773         return true;
774 }
775
776
777 /* stacktrace_create ***********************************************************
778
779    Generates a stacktrace from the thread passed into a
780    stacktracebuffer.  The stacktracebuffer is allocated on the GC
781    heap.
782
783    RETURN VALUE:
784       pointer to the stacktracebuffer, or
785           NULL if an exception has been thrown
786
787 *******************************************************************************/
788
789 stacktracebuffer *stacktrace_create(threadobject* thread)
790 {
791         stacktracebuffer *stb;
792         stackframeinfo   *sfi;
793         methodinfo       *m;
794         u1               *pv;
795         u1               *sp;
796         u4                framesize;
797         u1               *ra;
798         u1               *xpc;
799
800         /* prevent compiler warnings */
801
802         pv = NULL;
803         sp = NULL;
804         ra = NULL;
805
806         /* create a stacktracebuffer in dump memory */
807
808         stb = DNEW(stacktracebuffer);
809
810         stb->capacity = STACKTRACE_CAPACITY_DEFAULT;
811         stb->used     = 0;
812         stb->entries  = DMNEW(stacktrace_entry, STACKTRACE_CAPACITY_DEFAULT);
813
814         /* The first element in the stackframe chain must always be a
815            native stackframeinfo (VMThrowable.fillInStackTrace is a native
816            function). */
817
818         /* We don't use the STACKFRAMEINFO macro here, as we have to use
819            the passed thread. */
820
821 #if defined(ENABLE_THREADS)
822         sfi = thread->_stackframeinfo;
823 #else
824         sfi = _no_threads_stackframeinfo;
825 #endif
826
827 #define PRINTMETHODS 0
828
829 #if PRINTMETHODS
830         printf("\n\nfillInStackTrace start:\n");
831         fflush(stdout);
832 #endif
833
834         /* Loop while we have a method pointer (asm_calljavafunction has
835            NULL) or there is a stackframeinfo in the chain. */
836
837         m = NULL;
838
839         while (m || sfi) {
840                 /* m == NULL should only happen for the first time and inline
841                    stackframe infos, like from the exception stubs or the
842                    patcher wrapper. */
843
844                 if (m == NULL) {
845                         /* for native stub stackframe infos, pv is always NULL */
846
847                         if (sfi->pv == NULL) {
848                                 /* get methodinfo, sp and ra from the current stackframe info */
849
850                                 m  = sfi->method;
851                                 sp = sfi->sp;           /* sp of parent Java function         */
852                                 ra = sfi->ra;
853
854                                 if (m)
855                                         stacktrace_add_entry(stb, m, 0);
856
857 #if PRINTMETHODS
858                                 printf("ra=%p sp=%p, ", ra, sp);
859                                 method_print(m);
860                                 printf(": native stub\n");
861                                 fflush(stdout);
862 #endif
863                                 /* This is an native stub stackframe info, so we can
864                                    get the parent pv from the return address
865                                    (ICMD_INVOKE*). */
866
867 #if defined(ENABLE_INTRP)
868                                 if (opt_intrp)
869                                         pv = codegen_findmethod(ra);
870                                 else
871 #endif
872                                         {
873 #if defined(ENABLE_JIT)
874                                                 pv = md_codegen_findmethod(ra);
875 #endif
876                                         }
877
878                                 /* get methodinfo pointer from parent data segment */
879
880                                 m = *((methodinfo **) (pv + MethodPointer));
881
882                         } else {
883                                 /* Inline stackframe infos are special: they have a
884                                    xpc of the actual exception position and the return
885                                    address saved since an inline stackframe info can
886                                    also be in a leaf method (no return address saved
887                                    on stack!!!).  ATTENTION: This one is also for
888                                    hardware exceptions!!! */
889
890                                 /* get methodinfo, sp and ra from the current stackframe info */
891
892                                 m   = sfi->method;      /* m == NULL                          */
893                                 pv  = sfi->pv;          /* pv of parent Java function         */
894                                 sp  = sfi->sp;          /* sp of parent Java function         */
895                                 ra  = sfi->ra;          /* ra of parent Java function         */
896                                 xpc = sfi->xpc;         /* actual exception position          */
897
898 #if PRINTMETHODS
899                                 printf("ra=%p sp=%p, ", ra, sp);
900                                 printf("NULL: inline stub\n");
901                                 fflush(stdout);
902 #endif
903
904                                 /* get methodinfo from current Java method */
905
906                                 m = *((methodinfo **) (pv + MethodPointer));
907
908                                 /* if m == NULL, this is a asm_calljavafunction call */
909
910                                 if (m != NULL) {
911 #if PRINTMETHODS
912                                         printf("ra=%p sp=%p, ", ra, sp);
913                                         method_print(m);
914                                         printf(": inline stub parent");
915                                         fflush(stdout);
916 #endif
917
918 #if defined(ENABLE_INTRP)
919                                         if (!opt_intrp) {
920 #endif
921
922                                         /* add the method to the stacktrace */
923
924                                         stacktrace_add_method(stb, m, pv, (u1 *) ((ptrint) xpc));
925
926                                         /* get the current stack frame size */
927
928                                         framesize = *((u4 *) (pv + FrameSize));
929
930 #if PRINTMETHODS
931                                         printf(", framesize=%d\n", framesize);
932                                         fflush(stdout);
933 #endif
934
935                                         /* set stack pointer to stackframe of parent Java */
936                                         /* function of the current Java function */
937
938 #if defined(__I386__) || defined (__X86_64__)
939                                         sp += framesize + SIZEOF_VOID_P;
940 #else
941                                         sp += framesize;
942 #endif
943
944                                         /* get data segment and methodinfo pointer from parent */
945                                         /* method */
946
947 #if defined(ENABLE_JIT)
948                                         pv = md_codegen_findmethod(ra);
949 #endif
950
951                                         m = *((methodinfo **) (pv + MethodPointer));
952
953 #if defined(ENABLE_INTRP)
954                                         }
955 #endif
956                                 }
957 #if PRINTMETHODS
958                                 else {
959                                         printf("ra=%p sp=%p, ", ra, sp);
960                                         printf("asm_calljavafunction\n");
961                                         fflush(stdout);
962                                 }
963 #endif
964                         }
965
966                         /* get previous stackframeinfo in the chain */
967
968                         sfi = sfi->prev;
969
970                 } else {
971 #if PRINTMETHODS
972                         printf("ra=%p sp=%p, ", ra, sp);
973                         method_print(m);
974                         printf(": JIT");
975                         fflush(stdout);
976 #endif
977
978                         /* JIT method found, add it to the stacktrace (we subtract
979                            1 from the return address since it points the the
980                            instruction after call). */
981
982                         stacktrace_add_method(stb, m, pv, (u1 *) ((ptrint) ra) - 1);
983
984                         /* get the current stack frame size */
985
986                         framesize = *((u4 *) (pv + FrameSize));
987
988 #if PRINTMETHODS
989                         printf(", framesize=%d\n", framesize);
990                         fflush(stdout);
991 #endif
992
993                         /* get return address of current stack frame */
994
995 #if defined(ENABLE_JIT)
996 # if defined(ENABLE_INTRP)
997                         if (opt_intrp)
998                                 ra = intrp_md_stacktrace_get_returnaddress(sp, framesize);
999                         else
1000 # endif
1001                                 ra = md_stacktrace_get_returnaddress(sp, framesize);
1002 #else
1003                         ra = intrp_md_stacktrace_get_returnaddress(sp, framesize);
1004 #endif
1005
1006                         /* get data segment and methodinfo pointer from parent method */
1007
1008 #if defined(ENABLE_INTRP)
1009                         if (opt_intrp)
1010                                 pv = codegen_findmethod(ra);
1011                         else
1012 #endif
1013                                 {
1014 #if defined(ENABLE_JIT)
1015                                         pv = md_codegen_findmethod(ra);
1016 #endif
1017                                 }
1018
1019                         m = *((methodinfo **) (pv + MethodPointer));
1020
1021                         /* walk the stack */
1022
1023 #if defined(ENABLE_INTRP)
1024                         if (opt_intrp)
1025                                 sp = *(u1 **) (sp - framesize);
1026                         else
1027 #endif
1028                                 {
1029 #if defined(__I386__) || defined (__X86_64__)
1030                                         sp += framesize + SIZEOF_VOID_P;
1031 #else
1032                                         sp += framesize;
1033 #endif
1034                                 }
1035                 }
1036         }
1037
1038         /* return the stacktracebuffer */
1039
1040         return stb;
1041 }
1042
1043
1044 /* stacktrace_fillInStackTrace *************************************************
1045
1046    Generate a stacktrace from the current thread for
1047    java.lang.VMThrowable.fillInStackTrace.
1048
1049 *******************************************************************************/
1050
1051 stacktracebuffer *stacktrace_fillInStackTrace(void)
1052 {
1053         stacktracebuffer *stb;
1054         stacktracebuffer *gcstb;
1055         s4                dumpsize;
1056         CYCLES_STATS_DECLARE_AND_START_WITH_OVERHEAD
1057
1058         /* mark start of dump memory area */
1059
1060         dumpsize = dump_size();
1061
1062         /* create a stacktrace from the current thread */
1063
1064         stb = stacktrace_create(THREADOBJECT);
1065         if (!stb)
1066                 goto return_NULL;
1067
1068         /* allocate memory from the GC heap and copy the stacktrace buffer */
1069
1070         gcstb = GCNEW(stacktracebuffer);
1071
1072         if (gcstb == NULL)
1073                 goto return_NULL;
1074
1075         gcstb->capacity = stb->capacity;
1076         gcstb->used     = stb->used;
1077         gcstb->entries  = GCMNEW(stacktrace_entry, stb->used);
1078
1079         if (gcstb->entries == NULL)
1080                 goto return_NULL;
1081
1082         MCOPY(gcstb->entries, stb->entries, stacktrace_entry, stb->used);
1083
1084         /* release dump memory */
1085
1086         dump_release(dumpsize);
1087
1088         CYCLES_STATS_END_WITH_OVERHEAD(stacktrace_fillInStackTrace,
1089                                                                    stacktrace_overhead)
1090         return gcstb;
1091
1092 return_NULL:
1093         dump_release(dumpsize);
1094
1095         CYCLES_STATS_END_WITH_OVERHEAD(stacktrace_fillInStackTrace,
1096                                                                    stacktrace_overhead)
1097
1098         return NULL;
1099 }
1100
1101
1102 /* stacktrace_getClassContext **************************************************
1103
1104    Creates a Class context array.
1105
1106    RETURN VALUE:
1107       the array of java.lang.Class objects, or
1108           NULL if an exception has been thrown
1109
1110 *******************************************************************************/
1111
1112 java_objectarray *stacktrace_getClassContext(void)
1113 {
1114         stacktracebuffer  *stb;
1115         stacktrace_entry  *ste;
1116         java_objectarray  *oa;
1117         s4                 oalength;
1118         s4                 i;
1119         s4                 dumpsize;
1120         CYCLES_STATS_DECLARE_AND_START
1121
1122         /* mark start of dump memory area */
1123
1124         dumpsize = dump_size();
1125
1126         /* create a stacktrace for the current thread */
1127
1128         stb = stacktrace_create(THREADOBJECT);
1129         if (!stb)
1130                 goto return_NULL;
1131
1132         /* calculate the size of the Class array */
1133
1134         for (i = 0, oalength = 0; i < stb->used; i++)
1135                 if (stb->entries[i].method != NULL)
1136                         oalength++;
1137
1138         /* The first entry corresponds to the method whose implementation */
1139         /* calls stacktrace_getClassContext. We remove that entry.        */
1140
1141         ste = &(stb->entries[0]);
1142         ste++;
1143         oalength--;
1144
1145         /* allocate the Class array */
1146
1147         oa = builtin_anewarray(oalength, class_java_lang_Class);
1148         if (!oa)
1149                 goto return_NULL;
1150
1151         /* fill the Class array from the stacktracebuffer */
1152
1153         for(i = 0; i < oalength; i++, ste++) {
1154                 if (ste->method == NULL) {
1155                         i--;
1156                         continue;
1157                 }
1158
1159                 oa->data[i] = (java_objectheader *) ste->method->class;
1160         }
1161
1162         /* release dump memory */
1163
1164         dump_release(dumpsize);
1165
1166         CYCLES_STATS_END(stacktrace_getClassContext)
1167
1168         return oa;
1169
1170 return_NULL:
1171         dump_release(dumpsize);
1172
1173         CYCLES_STATS_END(stacktrace_getClassContext)
1174
1175         return NULL;
1176 }
1177
1178
1179 /* stacktrace_getCurrentClass **************************************************
1180
1181    Find the current class by walking the stack trace.
1182
1183    Quote from the JNI documentation:
1184          
1185    In the Java 2 Platform, FindClass locates the class loader
1186    associated with the current native method.  If the native code
1187    belongs to a system class, no class loader will be
1188    involved. Otherwise, the proper class loader will be invoked to
1189    load and link the named class. When FindClass is called through the
1190    Invocation Interface, there is no current native method or its
1191    associated class loader. In that case, the result of
1192    ClassLoader.getBaseClassLoader is used."
1193
1194 *******************************************************************************/
1195
1196 classinfo *stacktrace_getCurrentClass(void)
1197 {
1198         stacktracebuffer  *stb;
1199         stacktrace_entry  *ste;
1200         methodinfo        *m;
1201         s4                 i;
1202         s4                 dumpsize;
1203         CYCLES_STATS_DECLARE_AND_START
1204
1205         /* mark start of dump memory area */
1206
1207         dumpsize = dump_size();
1208
1209         /* create a stacktrace for the current thread */
1210
1211         stb = stacktrace_create(THREADOBJECT);
1212         if (!stb)
1213                 goto return_NULL; /* XXX exception: how to distinguish from normal NULL return? */
1214
1215         /* iterate over all stacktrace entries and find the first suitable
1216            class */
1217
1218         for (i = 0, ste = &(stb->entries[0]); i < stb->used; i++, ste++) {
1219                 m = ste->method;
1220
1221                 if (m == NULL)
1222                         continue;
1223
1224                 if (m->class == class_java_security_PrivilegedAction)
1225                         goto return_NULL;
1226
1227                 if (m->class != NULL) {
1228                         dump_release(dumpsize);
1229
1230                         CYCLES_STATS_END(stacktrace_getCurrentClass)
1231
1232                         return m->class;
1233                 }
1234         }
1235
1236         /* no Java method found on the stack */
1237
1238 return_NULL:
1239         dump_release(dumpsize);
1240
1241         CYCLES_STATS_END(stacktrace_getCurrentClass)
1242
1243         return NULL;
1244 }
1245
1246
1247 /* stacktrace_getStack *********************************************************
1248
1249    Create a 2-dimensional array for java.security.VMAccessControler.
1250
1251    RETURN VALUE:
1252       the arrary, or
1253           NULL if an exception has been thrown
1254
1255 *******************************************************************************/
1256
1257 java_objectarray *stacktrace_getStack(void)
1258 {
1259         stacktracebuffer *stb;
1260         stacktrace_entry *ste;
1261         java_objectarray *oa;
1262         java_objectarray *classes;
1263         java_objectarray *methodnames;
1264         classinfo        *c;
1265         java_lang_String *str;
1266         s4                i;
1267         s4                dumpsize;
1268         CYCLES_STATS_DECLARE_AND_START
1269
1270         /* mark start of dump memory area */
1271
1272         dumpsize = dump_size();
1273
1274         /* create a stacktrace for the current thread */
1275
1276         stb = stacktrace_create(THREADOBJECT);
1277         if (!stb)
1278                 goto return_NULL;
1279
1280         /* get the first stacktrace entry */
1281
1282         ste = &(stb->entries[0]);
1283
1284         /* allocate all required arrays */
1285
1286         oa = builtin_anewarray(2, arrayclass_java_lang_Object);
1287
1288         if (!oa)
1289                 goto return_NULL;
1290
1291         classes = builtin_anewarray(stb->used, class_java_lang_Class);
1292
1293         if (!classes)
1294                 goto return_NULL;
1295
1296         methodnames = builtin_anewarray(stb->used, class_java_lang_String);
1297
1298         if (!methodnames)
1299                 goto return_NULL;
1300
1301         /* set up the 2-dimensional array */
1302
1303         oa->data[0] = (java_objectheader *) classes;
1304         oa->data[1] = (java_objectheader *) methodnames;
1305
1306         /* iterate over all stacktrace entries */
1307
1308         for (i = 0, ste = &(stb->entries[0]); i < stb->used; i++, ste++) {
1309                 c = ste->method->class;
1310
1311                 classes->data[i] = (java_objectheader *) c;
1312                 str = javastring_new(ste->method->name);
1313
1314                 if (!str)
1315                         goto return_NULL;
1316
1317                 methodnames->data[i] = (java_objectheader *) str;
1318         }
1319
1320         /* return the 2-dimensional array */
1321
1322         dump_release(dumpsize);
1323
1324         CYCLES_STATS_END(stacktrace_getStack)
1325
1326         return oa;
1327
1328 return_NULL:
1329         dump_release(dumpsize);
1330
1331         CYCLES_STATS_END(stacktrace_getStack)
1332
1333         return NULL;
1334 }
1335
1336
1337 /* stacktrace_print_trace_from_buffer ******************************************
1338
1339    Print the stacktrace of a given stacktracebuffer with CACAO intern
1340    methods (no Java help). This method is used by
1341    stacktrace_dump_trace and builtin_trace_exception.
1342
1343 *******************************************************************************/
1344
1345 static void stacktrace_print_trace_from_buffer(stacktracebuffer *stb)
1346 {
1347         stacktrace_entry *ste;
1348         methodinfo       *m;
1349         s4                i;
1350
1351         ste = &(stb->entries[0]);
1352
1353         for (i = 0; i < stb->used; i++, ste++) {
1354                 m = ste->method;
1355
1356                 printf("\tat ");
1357                 utf_display_printable_ascii_classname(m->class->name);
1358                 printf(".");
1359                 utf_display_printable_ascii(m->name);
1360                 utf_display_printable_ascii(m->descriptor);
1361
1362                 if (m->flags & ACC_NATIVE) {
1363                         puts("(Native Method)");
1364
1365                 } else {
1366                         printf("(");
1367                         utf_display_printable_ascii(m->class->sourcefile);
1368                         printf(":%d)\n", (u4) ste->linenumber);
1369                 }
1370         }
1371
1372         /* just to be sure */
1373
1374         fflush(stdout);
1375 }
1376
1377
1378 /* stacktrace_dump_trace *******************************************************
1379
1380    This method is call from signal_handler_sigusr1 to dump the
1381    stacktrace of the current thread to stdout.
1382
1383 *******************************************************************************/
1384
1385 void stacktrace_dump_trace(void)
1386 {
1387         stacktracebuffer *stb;
1388         s4                dumpsize;
1389
1390 #if 0
1391         /* get methodinfo pointer from data segment */
1392
1393         m = *((methodinfo **) (pv + MethodPointer));
1394
1395         /* get current stackframe info pointer */
1396
1397         psfi = STACKFRAMEINFO;
1398
1399         /* fill new stackframe info structure */
1400
1401         sfi->prev   = *psfi;
1402         sfi->method = NULL;
1403         sfi->pv     = NULL;
1404         sfi->sp     = sp;
1405         sfi->ra     = ra;
1406
1407         /* store new stackframe info pointer */
1408
1409         *psfi = sfi;
1410 #endif
1411
1412         /* mark start of dump memory area */
1413
1414         dumpsize = dump_size();
1415
1416         /* create a stacktrace for the current thread */
1417
1418         stb = stacktrace_create(THREADOBJECT);
1419
1420         /* print stacktrace */
1421
1422         if (stb) {
1423                 stacktrace_print_trace_from_buffer(stb);
1424
1425         } else {
1426                 puts("\t<<No stacktrace available>>");
1427                 fflush(stdout);
1428         }
1429
1430         dump_release(dumpsize);
1431 }
1432
1433
1434 /* stacktrace_print_trace ******************************************************
1435
1436    Print the stacktrace of a given exception. More or less a wrapper
1437    to stacktrace_print_trace_from_buffer.
1438
1439 *******************************************************************************/
1440
1441 void stacktrace_print_trace(java_objectheader *xptr)
1442 {
1443         java_lang_Throwable   *t;
1444         java_lang_VMThrowable *vmt;
1445         stacktracebuffer      *stb;
1446
1447         t = (java_lang_Throwable *) xptr;
1448
1449         if (t == NULL)
1450                 return;
1451
1452         /* now print the stacktrace */
1453
1454         vmt = t->vmState;
1455         stb = (stacktracebuffer *) vmt->vmData;
1456
1457         stacktrace_print_trace_from_buffer(stb);
1458 }
1459
1460
1461 #if defined(ENABLE_CYCLES_STATS)
1462 void stacktrace_print_cycles_stats(FILE *file)
1463 {
1464         CYCLES_STATS_PRINT_OVERHEAD(stacktrace_overhead,file);
1465         CYCLES_STATS_PRINT(stacktrace_fillInStackTrace,file);
1466         CYCLES_STATS_PRINT(stacktrace_getClassContext ,file);
1467         CYCLES_STATS_PRINT(stacktrace_getCurrentClass ,file);
1468         CYCLES_STATS_PRINT(stacktrace_getStack        ,file);
1469 }
1470 #endif
1471
1472
1473 /*
1474  * These are local overrides for various environment variables in Emacs.
1475  * Please do not remove this and leave it at the end of the file, where
1476  * Emacs will automagically detect them.
1477  * ---------------------------------------------------------------------
1478  * Local variables:
1479  * mode: c
1480  * indent-tabs-mode: t
1481  * c-basic-offset: 4
1482  * tab-width: 4
1483  * End:
1484  * vim:noexpandtab:sw=4:ts=4:
1485  */