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