* src/vm/jit/methodtree.c (methodtree_find): Detect and avoid endless loop when
[cacao.git] / src / vm / jit / methodtree.c
1 /* src/vm/jit/methodtree.c - AVL tree of methods
2
3    Copyright (C) 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <stdint.h>
29
30 #include "mm/memory.hpp"
31
32 #include "toolbox/avl.h"
33
34 #include "vm/jit/asmpart.h"
35 #include "vm/jit/methodtree.h"
36 #include "vm/jit/stacktrace.hpp"
37
38
39 /* methodtree_element *********************************************************/
40
41 typedef struct methodtree_element_t methodtree_element_t;
42
43 struct methodtree_element_t {
44         void *startpc;
45         void *endpc;
46 };
47
48
49 /* in this tree we store all method addresses *********************************/
50
51 static avl_tree_t *methodtree = NULL;
52
53
54 /* static functions ***********************************************************/
55
56 static int methodtree_comparator(const void *treenode, const void *node);
57
58
59 /* methodtree_init *************************************************************
60
61    Initialize the global method tree.
62
63 *******************************************************************************/
64
65 void methodtree_init(void)
66 {
67 #if defined(ENABLE_JIT)
68         methodtree_element_t *mte;
69 #endif
70
71         methodtree = avl_create(&methodtree_comparator);
72
73 #if defined(ENABLE_JIT)
74         /* Insert asm_vm_call_method. */
75
76         mte = NEW(methodtree_element_t);
77
78         mte->startpc = (u1 *) (ptrint) asm_vm_call_method;
79         mte->endpc   = (u1 *) (ptrint) asm_vm_call_method_end;
80
81         avl_insert(methodtree, mte);
82 #endif
83 }
84
85
86 /* methodtree_comparator *******************************************************
87
88    Comparator function used for the AVL tree of methods.
89
90    ARGUMENTS:
91        treenode ... the node from the tree
92        node ....... the node to compare to the tree-node
93
94    RETURN VALUE:
95        0 .... found
96        -1 ... go left
97        1 .... go right
98
99 *******************************************************************************/
100
101 static int methodtree_comparator(const void *treenode, const void *node)
102 {
103         methodtree_element_t *mte;
104         methodtree_element_t *mtepc;
105
106         mte   = (methodtree_element_t *) treenode;
107         mtepc = (methodtree_element_t *) node;
108
109         /* compare both startpc and endpc of pc, even if they have the same value,
110            otherwise the avl_probe sometimes thinks the element is already in the
111            tree */
112
113 #ifdef __S390__
114         /* On S390 addresses are 31 bit. Compare only 31 bits of value.
115          */
116 #       define ADDR_MASK(a) ((a) & 0x7FFFFFFF)
117 #else
118 #       define ADDR_MASK(a) (a)
119 #endif
120
121         if (ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->startpc) &&
122                 ADDR_MASK((long) mtepc->startpc) <= ADDR_MASK((long) mte->endpc) &&
123                 ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->endpc) &&
124                 ADDR_MASK((long) mtepc->endpc) <= ADDR_MASK((long) mte->endpc)) {
125                 return 0;
126
127         } else if (ADDR_MASK((long) mtepc->startpc) < ADDR_MASK((long) mte->startpc)) {
128                 return -1;
129
130         } else {
131                 return 1;
132         }
133
134 #       undef ADDR_MASK
135 }
136
137
138 /* methodtree_insert ***********************************************************
139
140    Insert the machine code range of a method into the AVL tree of
141    methods.
142
143    ARGUMENTS:
144        startpc ... start address of the method
145            endpc ..... end address of the method
146
147 *******************************************************************************/
148
149 void methodtree_insert(void *startpc, void *endpc)
150 {
151         methodtree_element_t *mte;
152
153         /* Allocate new method entry. */
154
155         mte = NEW(methodtree_element_t);
156
157         mte->startpc = startpc;
158         mte->endpc   = endpc;
159
160         /* This function does not return an error, but asserts for
161            duplicate entries. */
162
163         avl_insert(methodtree, mte);
164 }
165
166
167 /* methodtree_find *************************************************************
168
169    Find the PV for the given PC by searching in the AVL tree of
170    methods.
171
172 *******************************************************************************/
173
174 void *methodtree_find(void *pc)
175 {
176         void *pv;
177
178         // This flag indicates whether a methodtree lookup is failing. We need
179         // to keep track of this to avoid endless loops during stacktrace creation.
180         static bool methodtree_find_failing = false;
181
182         /* Try to find a method. */
183
184         pv = methodtree_find_nocheck(pc);
185
186         if (pv == NULL) {
187                 /* No method was found.  Let's dump a stacktrace. */
188
189 #if defined(ENABLE_VMLOG)
190                 vmlog_cacao_signl("SIGSEGV");
191 #endif
192
193                 log_println("We received a SIGSEGV and tried to handle it, but we were");
194                 log_println("unable to find a Java method at:");
195                 log_println("");
196 #if SIZEOF_VOID_P == 8
197                 log_println("PC=0x%016lx", pc);
198 #else
199                 log_println("PC=0x%08x", pc);
200 #endif
201                 log_println("");
202
203                 // Detect and avoid endless loops.
204                 if (methodtree_find_failing)
205                         vm_abort("Exiting without stacktrace...");
206                 else
207                         methodtree_find_failing = true;
208
209                 // Actually try to dump a stacktrace.
210                 log_println("Dumping the current stacktrace:");
211                 stacktrace_print_current();
212
213                 vm_abort("Exiting...");
214         }
215
216         return pv;
217 }
218
219
220 /* methodtree_find_nocheck *****************************************************
221
222    Find the PV for the given PC by searching in the AVL tree of
223    methods.  This method does not check the return value and is used
224    by the profiler.
225
226 *******************************************************************************/
227
228 void *methodtree_find_nocheck(void *pc)
229 {
230         methodtree_element_t  mtepc;
231         methodtree_element_t *mte;
232
233         mtepc.startpc = pc;
234         mtepc.endpc   = pc;
235
236         mte = avl_find(methodtree, &mtepc);
237
238         if (mte == NULL)
239                 return NULL;
240         else
241                 return mte->startpc;
242 }
243
244
245 /*
246  * These are local overrides for various environment variables in Emacs.
247  * Please do not remove this and leave it at the end of the file, where
248  * Emacs will automagically detect them.
249  * ---------------------------------------------------------------------
250  * Local variables:
251  * mode: c
252  * indent-tabs-mode: t
253  * c-basic-offset: 4
254  * tab-width: 4
255  * End:
256  * vim:noexpandtab:sw=4:ts=4:
257  */