* src/vm/exceptions.c (new_nullpointerexception): Renamed to
[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             Christian Thalinger
29             Edwin Steiner
30
31    $Id: stacktrace.c 5935 2006-11-08 20:27:37Z 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 #if 0
752         code = *((codeinfo **) (pv + CodeinfoPointer));
753 #endif
754
755 #if 1
756         /* XXX Note: This is preliminary. It would be cleaner */
757         /* to get the codeinfo * from the PV                  */
758
759         code = m->code;
760         while (1) {
761                 if (!code) {
762 #ifndef NDEBUG
763                         method_println(m);
764                         dolog("Could not find codeinfo for Current PC: %p",(void*)pc);
765 #endif
766                         abort();
767                 }
768
769                 if (((ptrint)pc >= (ptrint)code->entrypoint)
770                                 &&
771                         ( (pc - (u1*)code->entrypoint) < code->mcodelength ))
772                 {
773                         /* found */
774                         break;
775                 }
776
777                 code = code->prev;
778         }
779 #endif
780
781         /* search the line number table */
782
783         if (stacktrace_add_method_intern(stb, m, lntentry, lntsize, pc))
784                 return true;
785
786         /* If we get here, just add the entry with line number 0. */
787
788         stacktrace_add_entry(stb, m, 0);
789
790         return true;
791 }
792
793
794 /* stacktrace_create ***********************************************************
795
796    Generates a stacktrace from the thread passed into a
797    stacktracebuffer.  The stacktracebuffer is allocated on the GC
798    heap.
799
800    RETURN VALUE:
801       pointer to the stacktracebuffer, or
802           NULL if an exception has been thrown
803
804 *******************************************************************************/
805
806 stacktracebuffer *stacktrace_create(threadobject* thread)
807 {
808         stacktracebuffer *stb;
809         stackframeinfo   *sfi;
810         methodinfo       *m;
811         codeinfo         *code;
812         u1               *pv;
813         u1               *sp;
814         u4                framesize;
815         u1               *ra;
816         u1               *xpc;
817
818         /* prevent compiler warnings */
819
820         pv = NULL;
821         sp = NULL;
822         ra = NULL;
823
824         /* create a stacktracebuffer in dump memory */
825
826         stb = DNEW(stacktracebuffer);
827
828         stb->capacity = STACKTRACE_CAPACITY_DEFAULT;
829         stb->used     = 0;
830         stb->entries  = DMNEW(stacktrace_entry, STACKTRACE_CAPACITY_DEFAULT);
831
832         /* The first element in the stackframe chain must always be a
833            native stackframeinfo (VMThrowable.fillInStackTrace is a native
834            function). */
835
836         /* We don't use the STACKFRAMEINFO macro here, as we have to use
837            the passed thread. */
838
839 #if defined(ENABLE_THREADS)
840         sfi = thread->_stackframeinfo;
841 #else
842         sfi = _no_threads_stackframeinfo;
843 #endif
844
845 #define PRINTMETHODS 0
846
847 #if PRINTMETHODS
848         printf("\n\nfillInStackTrace start:\n");
849         fflush(stdout);
850 #endif
851
852         /* Loop while we have a method pointer (asm_calljavafunction has
853            NULL) or there is a stackframeinfo in the chain. */
854
855         m = NULL;
856
857         while ((m != NULL) || (sfi != NULL)) {
858                 /* m == NULL should only happen for the first time and inline
859                    stackframe infos, like from the exception stubs or the
860                    patcher wrapper. */
861
862                 if (m == NULL) {
863                         /* for native stub stackframe infos, pv is always NULL */
864
865                         if (sfi->pv == NULL) {
866                                 /* get methodinfo, sp and ra from the current stackframe info */
867
868                                 m  = sfi->method;
869                                 sp = sfi->sp;           /* sp of parent Java function         */
870                                 ra = sfi->ra;
871
872                                 if (m)
873                                         stacktrace_add_entry(stb, m, 0);
874
875 #if PRINTMETHODS
876                                 printf("ra=%p sp=%p, ", ra, sp);
877                                 method_print(m);
878                                 printf(": native stub\n");
879                                 fflush(stdout);
880 #endif
881                                 /* This is an native stub stackframe info, so we can
882                                    get the parent pv from the return address
883                                    (ICMD_INVOKE*). */
884
885 #if defined(ENABLE_INTRP)
886                                 if (opt_intrp)
887                                         pv = codegen_get_pv_from_pc(ra);
888                                 else
889 #endif
890                                         {
891 #if defined(ENABLE_JIT)
892                                                 pv = md_codegen_get_pv_from_pc(ra);
893 #endif
894                                         }
895
896                                 /* get methodinfo pointer from parent data segment */
897
898                                 code = *((codeinfo **) (pv + CodeinfoPointer));
899
900                                 /* For asm_vm_call_method the codeinfo pointer is
901                                    NULL. */
902
903                                 m = (code == NULL) ? NULL : code->m;
904
905                         } else {
906                                 /* Inline stackframe infos are special: they have a
907                                    xpc of the actual exception position and the return
908                                    address saved since an inline stackframe info can
909                                    also be in a leaf method (no return address saved
910                                    on stack!!!).  ATTENTION: This one is also for
911                                    hardware exceptions!!! */
912
913                                 /* get methodinfo, sp and ra from the current stackframe info */
914
915                                 m   = sfi->method;      /* m == NULL                          */
916                                 pv  = sfi->pv;          /* pv of parent Java function         */
917                                 sp  = sfi->sp;          /* sp of parent Java function         */
918                                 ra  = sfi->ra;          /* ra of parent Java function         */
919                                 xpc = sfi->xpc;         /* actual exception position          */
920
921 #if PRINTMETHODS
922                                 printf("ra=%p sp=%p, ", ra, sp);
923                                 printf("NULL: inline stub\n");
924                                 fflush(stdout);
925 #endif
926
927                                 /* get methodinfo from current Java method */
928
929                                 code = *((codeinfo **) (pv + CodeinfoPointer));
930
931                                 /* For asm_vm_call_method the codeinfo pointer is
932                                    NULL. */
933
934                                 m = (code == NULL) ? NULL : code->m;
935
936                                 /* if m == NULL, this is a asm_calljavafunction call */
937
938                                 if (m != NULL) {
939 #if PRINTMETHODS
940                                         printf("ra=%p sp=%p, ", ra, sp);
941                                         method_print(m);
942                                         printf(": inline stub parent");
943                                         fflush(stdout);
944 #endif
945
946 #if defined(ENABLE_INTRP)
947                                         if (!opt_intrp) {
948 #endif
949
950                                         /* add the method to the stacktrace */
951
952                                         stacktrace_add_method(stb, m, pv, (u1 *) ((ptrint) xpc));
953
954                                         /* get the current stack frame size */
955
956                                         framesize = *((u4 *) (pv + FrameSize));
957
958 #if PRINTMETHODS
959                                         printf(", framesize=%d\n", framesize);
960                                         fflush(stdout);
961 #endif
962
963                                         /* Set stack pointer to stackframe of parent Java
964                                            function of the current Java function. */
965
966 #if defined(__I386__) || defined (__X86_64__)
967                                         sp += framesize + SIZEOF_VOID_P;
968 #else
969                                         sp += framesize;
970 #endif
971
972                                         /* get data segment and methodinfo pointer from
973                                            parent method */
974
975 #if defined(ENABLE_JIT)
976                                         pv = md_codegen_get_pv_from_pc(ra);
977 #endif
978
979                                         code = *((codeinfo **) (pv + CodeinfoPointer));
980
981                                         /* For asm_vm_call_method the codeinfo pointer is
982                                            NULL. */
983
984                                         m = (code == NULL) ? NULL : code->m;
985
986 #if defined(ENABLE_INTRP)
987                                         }
988 #endif
989                                 }
990 #if PRINTMETHODS
991                                 else {
992                                         printf("ra=%p sp=%p, ", ra, sp);
993                                         printf("asm_calljavafunction\n");
994                                         fflush(stdout);
995                                 }
996 #endif
997                         }
998
999                         /* get previous stackframeinfo in the chain */
1000
1001                         sfi = sfi->prev;
1002
1003                 } else {
1004 #if PRINTMETHODS
1005                         printf("ra=%p sp=%p, ", ra, sp);
1006                         method_print(m);
1007                         printf(": JIT");
1008                         fflush(stdout);
1009 #endif
1010
1011                         /* JIT method found, add it to the stacktrace (we subtract
1012                            1 from the return address since it points the the
1013                            instruction after call). */
1014
1015                         stacktrace_add_method(stb, m, pv, (u1 *) ((ptrint) ra) - 1);
1016
1017                         /* get the current stack frame size */
1018
1019                         framesize = *((u4 *) (pv + FrameSize));
1020
1021 #if PRINTMETHODS
1022                         printf(", framesize=%d\n", framesize);
1023                         fflush(stdout);
1024 #endif
1025
1026                         /* get return address of current stack frame */
1027
1028 #if defined(ENABLE_JIT)
1029 # if defined(ENABLE_INTRP)
1030                         if (opt_intrp)
1031                                 ra = intrp_md_stacktrace_get_returnaddress(sp, framesize);
1032                         else
1033 # endif
1034                                 ra = md_stacktrace_get_returnaddress(sp, framesize);
1035 #else
1036                         ra = intrp_md_stacktrace_get_returnaddress(sp, framesize);
1037 #endif
1038
1039                         /* get data segment and methodinfo pointer from parent method */
1040
1041 #if defined(ENABLE_INTRP)
1042                         if (opt_intrp)
1043                                 pv = codegen_get_pv_from_pc(ra);
1044                         else
1045 #endif
1046                                 {
1047 #if defined(ENABLE_JIT)
1048                                         pv = md_codegen_get_pv_from_pc(ra);
1049 #endif
1050                                 }
1051
1052                         code = *((codeinfo **) (pv + CodeinfoPointer));
1053
1054                         /* For asm_vm_call_method the codeinfo pointer is NULL. */
1055
1056                         m = (code == NULL) ? NULL : code->m;
1057
1058                         /* walk the stack */
1059
1060 #if defined(ENABLE_INTRP)
1061                         if (opt_intrp)
1062                                 sp = *(u1 **) (sp - framesize);
1063                         else
1064 #endif
1065                                 {
1066 #if defined(__I386__) || defined (__X86_64__)
1067                                         sp += framesize + SIZEOF_VOID_P;
1068 #else
1069                                         sp += framesize;
1070 #endif
1071                                 }
1072                 }
1073         }
1074
1075         /* return the stacktracebuffer */
1076
1077         return stb;
1078 }
1079
1080
1081 /* stacktrace_fillInStackTrace *************************************************
1082
1083    Generate a stacktrace from the current thread for
1084    java.lang.VMThrowable.fillInStackTrace.
1085
1086 *******************************************************************************/
1087
1088 stacktracecontainer *stacktrace_fillInStackTrace(void)
1089 {
1090         stacktracebuffer    *stb;
1091         stacktracecontainer *gcstc;
1092         s4                   gcstc_size;
1093         s4                   dumpsize;
1094         CYCLES_STATS_DECLARE_AND_START_WITH_OVERHEAD
1095
1096         /* mark start of dump memory area */
1097
1098         dumpsize = dump_size();
1099
1100         /* create a stacktrace from the current thread */
1101
1102         stb = stacktrace_create(THREADOBJECT);
1103         if (!stb)
1104                 goto return_NULL;
1105
1106         /* allocate memory from the GC heap and copy the stacktrace buffer */
1107         /* ATTENTION: use stacktracecontainer for this and make it look like
1108        an array */
1109
1110         gcstc_size = sizeof(stacktracebuffer) +
1111                      sizeof(stacktrace_entry) * stb->used;
1112         gcstc = (stacktracecontainer *) builtin_newarray_byte(gcstc_size);
1113
1114         if (gcstc == NULL)
1115                 goto return_NULL;
1116
1117         gcstc->stb.capacity = stb->capacity;
1118         gcstc->stb.used     = stb->used;
1119         gcstc->stb.entries  = gcstc->data;
1120
1121         MCOPY(gcstc->data, stb->entries, stacktrace_entry, stb->used);
1122
1123         /* release dump memory */
1124
1125         dump_release(dumpsize);
1126
1127         CYCLES_STATS_END_WITH_OVERHEAD(stacktrace_fillInStackTrace,
1128                                                                    stacktrace_overhead)
1129         return gcstc;
1130
1131 return_NULL:
1132         dump_release(dumpsize);
1133
1134         CYCLES_STATS_END_WITH_OVERHEAD(stacktrace_fillInStackTrace,
1135                                                                    stacktrace_overhead)
1136
1137         return NULL;
1138 }
1139
1140
1141 /* stacktrace_getClassContext **************************************************
1142
1143    Creates a Class context array.
1144
1145    RETURN VALUE:
1146       the array of java.lang.Class objects, or
1147           NULL if an exception has been thrown
1148
1149 *******************************************************************************/
1150
1151 java_objectarray *stacktrace_getClassContext(void)
1152 {
1153         stacktracebuffer  *stb;
1154         stacktrace_entry  *ste;
1155         java_objectarray  *oa;
1156         s4                 oalength;
1157         s4                 i;
1158         s4                 dumpsize;
1159         CYCLES_STATS_DECLARE_AND_START
1160
1161         /* mark start of dump memory area */
1162
1163         dumpsize = dump_size();
1164
1165         /* create a stacktrace for the current thread */
1166
1167         stb = stacktrace_create(THREADOBJECT);
1168         if (!stb)
1169                 goto return_NULL;
1170
1171         /* calculate the size of the Class array */
1172
1173         for (i = 0, oalength = 0; i < stb->used; i++)
1174                 if (stb->entries[i].method != NULL)
1175                         oalength++;
1176
1177         /* The first entry corresponds to the method whose implementation */
1178         /* calls stacktrace_getClassContext. We remove that entry.        */
1179
1180         ste = &(stb->entries[0]);
1181         ste++;
1182         oalength--;
1183
1184         /* allocate the Class array */
1185
1186         oa = builtin_anewarray(oalength, class_java_lang_Class);
1187         if (!oa)
1188                 goto return_NULL;
1189
1190         /* fill the Class array from the stacktracebuffer */
1191
1192         for(i = 0; i < oalength; i++, ste++) {
1193                 if (ste->method == NULL) {
1194                         i--;
1195                         continue;
1196                 }
1197
1198                 oa->data[i] = (java_objectheader *) ste->method->class;
1199         }
1200
1201         /* release dump memory */
1202
1203         dump_release(dumpsize);
1204
1205         CYCLES_STATS_END(stacktrace_getClassContext)
1206
1207         return oa;
1208
1209 return_NULL:
1210         dump_release(dumpsize);
1211
1212         CYCLES_STATS_END(stacktrace_getClassContext)
1213
1214         return NULL;
1215 }
1216
1217
1218 /* stacktrace_getCurrentClass **************************************************
1219
1220    Find the current class by walking the stack trace.
1221
1222    Quote from the JNI documentation:
1223          
1224    In the Java 2 Platform, FindClass locates the class loader
1225    associated with the current native method.  If the native code
1226    belongs to a system class, no class loader will be
1227    involved. Otherwise, the proper class loader will be invoked to
1228    load and link the named class. When FindClass is called through the
1229    Invocation Interface, there is no current native method or its
1230    associated class loader. In that case, the result of
1231    ClassLoader.getBaseClassLoader is used."
1232
1233 *******************************************************************************/
1234
1235 classinfo *stacktrace_getCurrentClass(void)
1236 {
1237         stacktracebuffer  *stb;
1238         stacktrace_entry  *ste;
1239         methodinfo        *m;
1240         s4                 i;
1241         s4                 dumpsize;
1242         CYCLES_STATS_DECLARE_AND_START
1243
1244         /* mark start of dump memory area */
1245
1246         dumpsize = dump_size();
1247
1248         /* create a stacktrace for the current thread */
1249
1250         stb = stacktrace_create(THREADOBJECT);
1251         if (!stb)
1252                 goto return_NULL; /* XXX exception: how to distinguish from normal NULL return? */
1253
1254         /* iterate over all stacktrace entries and find the first suitable
1255            class */
1256
1257         for (i = 0, ste = &(stb->entries[0]); i < stb->used; i++, ste++) {
1258                 m = ste->method;
1259
1260                 if (m == NULL)
1261                         continue;
1262
1263                 if (m->class == class_java_security_PrivilegedAction)
1264                         goto return_NULL;
1265
1266                 if (m->class != NULL) {
1267                         dump_release(dumpsize);
1268
1269                         CYCLES_STATS_END(stacktrace_getCurrentClass)
1270
1271                         return m->class;
1272                 }
1273         }
1274
1275         /* no Java method found on the stack */
1276
1277 return_NULL:
1278         dump_release(dumpsize);
1279
1280         CYCLES_STATS_END(stacktrace_getCurrentClass)
1281
1282         return NULL;
1283 }
1284
1285
1286 /* stacktrace_getStack *********************************************************
1287
1288    Create a 2-dimensional array for java.security.VMAccessControler.
1289
1290    RETURN VALUE:
1291       the arrary, or
1292           NULL if an exception has been thrown
1293
1294 *******************************************************************************/
1295
1296 java_objectarray *stacktrace_getStack(void)
1297 {
1298         stacktracebuffer *stb;
1299         stacktrace_entry *ste;
1300         java_objectarray *oa;
1301         java_objectarray *classes;
1302         java_objectarray *methodnames;
1303         classinfo        *c;
1304         java_lang_String *str;
1305         s4                i;
1306         s4                dumpsize;
1307         CYCLES_STATS_DECLARE_AND_START
1308
1309         /* mark start of dump memory area */
1310
1311         dumpsize = dump_size();
1312
1313         /* create a stacktrace for the current thread */
1314
1315         stb = stacktrace_create(THREADOBJECT);
1316         if (!stb)
1317                 goto return_NULL;
1318
1319         /* get the first stacktrace entry */
1320
1321         ste = &(stb->entries[0]);
1322
1323         /* allocate all required arrays */
1324
1325         oa = builtin_anewarray(2, arrayclass_java_lang_Object);
1326
1327         if (!oa)
1328                 goto return_NULL;
1329
1330         classes = builtin_anewarray(stb->used, class_java_lang_Class);
1331
1332         if (!classes)
1333                 goto return_NULL;
1334
1335         methodnames = builtin_anewarray(stb->used, class_java_lang_String);
1336
1337         if (!methodnames)
1338                 goto return_NULL;
1339
1340         /* set up the 2-dimensional array */
1341
1342         oa->data[0] = (java_objectheader *) classes;
1343         oa->data[1] = (java_objectheader *) methodnames;
1344
1345         /* iterate over all stacktrace entries */
1346
1347         for (i = 0, ste = &(stb->entries[0]); i < stb->used; i++, ste++) {
1348                 c = ste->method->class;
1349
1350                 classes->data[i] = (java_objectheader *) c;
1351                 str = javastring_new(ste->method->name);
1352
1353                 if (!str)
1354                         goto return_NULL;
1355
1356                 methodnames->data[i] = (java_objectheader *) str;
1357         }
1358
1359         /* return the 2-dimensional array */
1360
1361         dump_release(dumpsize);
1362
1363         CYCLES_STATS_END(stacktrace_getStack)
1364
1365         return oa;
1366
1367 return_NULL:
1368         dump_release(dumpsize);
1369
1370         CYCLES_STATS_END(stacktrace_getStack)
1371
1372         return NULL;
1373 }
1374
1375
1376 /* stacktrace_print_trace_from_buffer ******************************************
1377
1378    Print the stacktrace of a given stacktracebuffer with CACAO intern
1379    methods (no Java help). This method is used by
1380    stacktrace_dump_trace and builtin_trace_exception.
1381
1382 *******************************************************************************/
1383
1384 static void stacktrace_print_trace_from_buffer(stacktracebuffer *stb)
1385 {
1386         stacktrace_entry *ste;
1387         methodinfo       *m;
1388         s4                i;
1389
1390         ste = &(stb->entries[0]);
1391
1392         for (i = 0; i < stb->used; i++, ste++) {
1393                 m = ste->method;
1394
1395                 printf("\tat ");
1396                 utf_display_printable_ascii_classname(m->class->name);
1397                 printf(".");
1398                 utf_display_printable_ascii(m->name);
1399                 utf_display_printable_ascii(m->descriptor);
1400
1401                 if (m->flags & ACC_NATIVE) {
1402                         puts("(Native Method)");
1403
1404                 } else {
1405                         printf("(");
1406                         utf_display_printable_ascii(m->class->sourcefile);
1407                         printf(":%d)\n", (u4) ste->linenumber);
1408                 }
1409         }
1410
1411         /* just to be sure */
1412
1413         fflush(stdout);
1414 }
1415
1416
1417 /* stacktrace_dump_trace *******************************************************
1418
1419    This method is call from signal_handler_sigusr1 to dump the
1420    stacktrace of the current thread to stdout.
1421
1422 *******************************************************************************/
1423
1424 void stacktrace_dump_trace(threadobject *thread)
1425 {
1426         stacktracebuffer *stb;
1427         s4                dumpsize;
1428
1429 #if 0
1430         /* get methodinfo pointer from data segment */
1431
1432         m = *((methodinfo **) (pv + MethodPointer));
1433
1434         /* get current stackframe info pointer */
1435
1436         psfi = STACKFRAMEINFO;
1437
1438         /* fill new stackframe info structure */
1439
1440         sfi->prev   = *psfi;
1441         sfi->method = NULL;
1442         sfi->pv     = NULL;
1443         sfi->sp     = sp;
1444         sfi->ra     = ra;
1445
1446         /* store new stackframe info pointer */
1447
1448         *psfi = sfi;
1449 #endif
1450
1451         /* mark start of dump memory area */
1452
1453         dumpsize = dump_size();
1454
1455         /* create a stacktrace for the current thread */
1456
1457         stb = stacktrace_create(thread);
1458
1459         /* print stacktrace */
1460
1461         if (stb)
1462                 stacktrace_print_trace_from_buffer(stb);
1463         else {
1464                 puts("\t<<No stacktrace available>>");
1465                 fflush(stdout);
1466         }
1467
1468         dump_release(dumpsize);
1469 }
1470
1471
1472 /* stacktrace_print_trace ******************************************************
1473
1474    Print the stacktrace of a given exception. More or less a wrapper
1475    to stacktrace_print_trace_from_buffer.
1476
1477 *******************************************************************************/
1478
1479 void stacktrace_print_trace(java_objectheader *xptr)
1480 {
1481         java_lang_Throwable   *t;
1482         java_lang_VMThrowable *vmt;
1483         stacktracecontainer   *stc;
1484         stacktracebuffer      *stb;
1485
1486         t = (java_lang_Throwable *) xptr;
1487
1488         if (t == NULL)
1489                 return;
1490
1491         /* now print the stacktrace */
1492
1493         vmt = t->vmState;
1494         stc = (stacktracecontainer *) vmt->vmData;
1495         stb = &(stc->stb);
1496
1497         stacktrace_print_trace_from_buffer(stb);
1498 }
1499
1500
1501 #if defined(ENABLE_CYCLES_STATS)
1502 void stacktrace_print_cycles_stats(FILE *file)
1503 {
1504         CYCLES_STATS_PRINT_OVERHEAD(stacktrace_overhead,file);
1505         CYCLES_STATS_PRINT(stacktrace_fillInStackTrace,file);
1506         CYCLES_STATS_PRINT(stacktrace_getClassContext ,file);
1507         CYCLES_STATS_PRINT(stacktrace_getCurrentClass ,file);
1508         CYCLES_STATS_PRINT(stacktrace_getStack        ,file);
1509 }
1510 #endif
1511
1512
1513 /*
1514  * These are local overrides for various environment variables in Emacs.
1515  * Please do not remove this and leave it at the end of the file, where
1516  * Emacs will automagically detect them.
1517  * ---------------------------------------------------------------------
1518  * Local variables:
1519  * mode: c
1520  * indent-tabs-mode: t
1521  * c-basic-offset: 4
1522  * tab-width: 4
1523  * End:
1524  * vim:noexpandtab:sw=4:ts=4:
1525  */