/* src/vm/jit/methodtree.c - AVL tree of methods Copyright (C) 2008 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include "mm/memory.hpp" #include "toolbox/avl.h" #include "vm/jit/asmpart.h" #include "vm/jit/methodtree.h" #include "vm/jit/stacktrace.hpp" /* methodtree_element *********************************************************/ typedef struct methodtree_element_t methodtree_element_t; struct methodtree_element_t { void *startpc; void *endpc; }; /* in this tree we store all method addresses *********************************/ static avl_tree_t *methodtree = NULL; /* static functions ***********************************************************/ static int methodtree_comparator(const void *treenode, const void *node); /* methodtree_init ************************************************************* Initialize the global method tree. *******************************************************************************/ void methodtree_init(void) { #if defined(ENABLE_JIT) methodtree_element_t *mte; #endif methodtree = avl_create(&methodtree_comparator); #if defined(ENABLE_JIT) /* Insert asm_vm_call_method. */ mte = NEW(methodtree_element_t); mte->startpc = (u1 *) (ptrint) asm_vm_call_method; mte->endpc = (u1 *) (ptrint) asm_vm_call_method_end; avl_insert(methodtree, mte); #endif } /* methodtree_comparator ******************************************************* Comparator function used for the AVL tree of methods. ARGUMENTS: treenode ... the node from the tree node ....... the node to compare to the tree-node RETURN VALUE: 0 .... found -1 ... go left 1 .... go right *******************************************************************************/ static int methodtree_comparator(const void *treenode, const void *node) { methodtree_element_t *mte; methodtree_element_t *mtepc; mte = (methodtree_element_t *) treenode; mtepc = (methodtree_element_t *) node; /* compare both startpc and endpc of pc, even if they have the same value, otherwise the avl_probe sometimes thinks the element is already in the tree */ #ifdef __S390__ /* On S390 addresses are 31 bit. Compare only 31 bits of value. */ # define ADDR_MASK(a) ((a) & 0x7FFFFFFF) #else # define ADDR_MASK(a) (a) #endif if (ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->startpc) && ADDR_MASK((long) mtepc->startpc) <= ADDR_MASK((long) mte->endpc) && ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->endpc) && ADDR_MASK((long) mtepc->endpc) <= ADDR_MASK((long) mte->endpc)) { return 0; } else if (ADDR_MASK((long) mtepc->startpc) < ADDR_MASK((long) mte->startpc)) { return -1; } else { return 1; } # undef ADDR_MASK } /* methodtree_insert *********************************************************** Insert the machine code range of a method into the AVL tree of methods. ARGUMENTS: startpc ... start address of the method endpc ..... end address of the method *******************************************************************************/ void methodtree_insert(void *startpc, void *endpc) { methodtree_element_t *mte; /* Allocate new method entry. */ mte = NEW(methodtree_element_t); mte->startpc = startpc; mte->endpc = endpc; /* This function does not return an error, but asserts for duplicate entries. */ avl_insert(methodtree, mte); } /* methodtree_find ************************************************************* Find the PV for the given PC by searching in the AVL tree of methods. *******************************************************************************/ void *methodtree_find(void *pc) { void *pv; // This flag indicates whether a methodtree lookup is failing. We need // to keep track of this to avoid endless loops during stacktrace creation. static bool methodtree_find_failing = false; /* Try to find a method. */ pv = methodtree_find_nocheck(pc); if (pv == NULL) { /* No method was found. Let's dump a stacktrace. */ #if defined(ENABLE_VMLOG) vmlog_cacao_signl("SIGSEGV"); #endif log_println("We received a SIGSEGV and tried to handle it, but we were"); log_println("unable to find a Java method at:"); log_println(""); #if SIZEOF_VOID_P == 8 log_println("PC=0x%016lx", pc); #else log_println("PC=0x%08x", pc); #endif log_println(""); // Detect and avoid endless loops. if (methodtree_find_failing) vm_abort("Exiting without stacktrace..."); else methodtree_find_failing = true; // Actually try to dump a stacktrace. log_println("Dumping the current stacktrace:"); stacktrace_print_current(); vm_abort("Exiting..."); } return pv; } /* methodtree_find_nocheck ***************************************************** Find the PV for the given PC by searching in the AVL tree of methods. This method does not check the return value and is used by the profiler. *******************************************************************************/ void *methodtree_find_nocheck(void *pc) { methodtree_element_t mtepc; methodtree_element_t *mte; mtepc.startpc = pc; mtepc.endpc = pc; mte = avl_find(methodtree, &mtepc); if (mte == NULL) return NULL; else return mte->startpc; } /* * These are local overrides for various environment variables in Emacs. * Please do not remove this and leave it at the end of the file, where * Emacs will automagically detect them. * --------------------------------------------------------------------- * Local variables: * mode: c * indent-tabs-mode: t * c-basic-offset: 4 * tab-width: 4 * End: * vim:noexpandtab:sw=4:ts=4: */