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