A number of utility functions are introduced for backtrace traversal to assist java stack trace collecting. Only JIT methods and VM natives should appear in stack trace so all native functions and trampolines must be filtered out.
A new per-thread variable bottom_stack_frame is introduced to detect when to stop tracig back. It is set to a native frame pointer of the function that calls the main() method. Signed-off-by: Tomek Grabiec <tgrab...@gmail.com> --- Makefile | 3 +- arch/x86/include/arch/stack-frame.h | 18 +++- arch/x86/stack-frame.c | 17 ++++ include/vm/stack-trace.h | 39 ++++++++ test/arch-x86/stack-frame-test_32.c | 6 + vm/jato.c | 4 + vm/stack-trace.c | 175 +++++++++++++++++++++++++++++++++++ 7 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 include/vm/stack-trace.h create mode 100644 vm/stack-trace.c diff --git a/Makefile b/Makefile index 76fc6b9..1d39741 100644 --- a/Makefile +++ b/Makefile @@ -104,7 +104,8 @@ VM_OBJS = \ vm/class.o \ vm/list.o \ vm/radix-tree.o \ - vm/guard-page.o + vm/guard-page.o \ + vm/stack-trace.o JAMVM_OBJS = \ vm/jato.o \ diff --git a/arch/x86/include/arch/stack-frame.h b/arch/x86/include/arch/stack-frame.h index 3f27fae..e288ac2 100644 --- a/arch/x86/include/arch/stack-frame.h +++ b/arch/x86/include/arch/stack-frame.h @@ -8,13 +8,19 @@ struct methodblock; struct expression; struct compilation_unit; +struct native_stack_frame { + void *prev; /* previous stack frame link */ + unsigned long return_address; + unsigned long args[0]; +} __attribute__((packed)); + struct jit_stack_frame { - struct jit_stack_frame *prev; - unsigned long old_ebx; - unsigned long old_esi; - unsigned long old_edi; - unsigned long return_address; - unsigned long args[0]; + void *prev; /* previous stack frame link */ + unsigned long old_ebx; + unsigned long old_esi; + unsigned long old_edi; + unsigned long return_address; + unsigned long args[0]; } __attribute__((packed)); unsigned long frame_local_offset(struct methodblock *, struct expression *); diff --git a/arch/x86/stack-frame.c b/arch/x86/stack-frame.c index 1754592..dc8ff82 100644 --- a/arch/x86/stack-frame.c +++ b/arch/x86/stack-frame.c @@ -26,6 +26,7 @@ #include <jit/expression.h> #include <jit/compilation-unit.h> +#include <jit/compiler.h> #include <vm/vm.h> #include <arch/stack-frame.h> #include <stdlib.h> @@ -100,3 +101,19 @@ unsigned long cu_frame_locals_offset(struct compilation_unit *cu) unsigned long frame_size = frame_locals_size(cu->stack_frame); return frame_size * sizeof(unsigned long); } + +/* + * Checks whether given native function was called from jit trampoline + * code. It checks whether return address points after a relative call + * to jit_magic_trampoline, which is typical for trampolines. + */ +bool called_from_jit_trampoline(struct native_stack_frame *frame) +{ + void **call_rel_target_p; + void *call_target; + + call_rel_target_p = (void **)(frame->return_address - sizeof(void*)); + call_target = *call_rel_target_p + frame->return_address; + + return call_target == &jit_magic_trampoline; +} diff --git a/include/vm/stack-trace.h b/include/vm/stack-trace.h new file mode 100644 index 0000000..5064ecf --- /dev/null +++ b/include/vm/stack-trace.h @@ -0,0 +1,39 @@ +#ifndef JATO_VM_STACK_TRACE_H +#define JATO_VM_STACK_TRACE_H + +#include <arch/stack-frame.h> +#include <vm/vm.h> + +#include <stdbool.h> + +/* + * Points to a native stack frame that is considered as bottom-most + * for given thread. + */ +extern __thread struct native_stack_frame *bottom_stack_frame; + +struct stack_trace_elem { + /* Holds instruction address of this stack trace element. */ + unsigned long addr; + + /* + * If true then @frame has format of struct native_stack_frame + * and struct jit_stack_frame otherwise. + */ + bool is_native; + + /* If true then frame belongs to a trampoline */ + bool is_trampoline; + + /* Points to a stack frame of this stack trace element. */ + void *frame; +}; + +int init_stack_trace_elem(struct stack_trace_elem *elem); +int get_prev_stack_trace_elem(struct stack_trace_elem *elem); +int skip_frames_from_class(struct stack_trace_elem *elem, struct object *class); +int get_stack_trace_depth(struct stack_trace_elem *elem); + +bool called_from_jit_trampoline(struct native_stack_frame *frame); + +#endif /* JATO_VM_STACK_TRACE_H */ diff --git a/test/arch-x86/stack-frame-test_32.c b/test/arch-x86/stack-frame-test_32.c index db53f06..1fd5b6a 100644 --- a/test/arch-x86/stack-frame-test_32.c +++ b/test/arch-x86/stack-frame-test_32.c @@ -8,6 +8,12 @@ #include <stdlib.h> #include <libharness.h> +/* Stub required by called_from_jit_trampoline() in arch/x86/stack-frame.c */ +void *jit_magic_trampoline(struct compilation_unit *cu) +{ + return NULL; +} + static void assert_local_offset(unsigned long expected, struct expression *local, unsigned long nr_args) { struct methodblock method = { diff --git a/vm/jato.c b/vm/jato.c index d323f36..10e3353 100644 --- a/vm/jato.c +++ b/vm/jato.c @@ -25,6 +25,7 @@ #include <stdarg.h> #include <signal.h> +#include <vm/stack-trace.h> #include <vm/natives.h> #include <vm/signal.h> #include <vm/vm.h> @@ -357,6 +358,9 @@ int main(int argc, char *argv[]) { executeStaticMethod(main_class, mb, array); else { java_main_fn java_main = method_trampoline_ptr(mb); + + bottom_stack_frame = __builtin_frame_address(0); + java_main(); } } diff --git a/vm/stack-trace.c b/vm/stack-trace.c new file mode 100644 index 0000000..448af23 --- /dev/null +++ b/vm/stack-trace.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2009 Tomasz Grabiec + * + * This file is released under the GPL version 2 with the following + * clarification and special exception: + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under terms + * of your choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. An + * independent module is a module which is not derived from or based on + * this library. If you modify this library, you may extend this exception + * to your version of the library, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. + * + * Please refer to the file LICENSE for details. + */ +#include <vm/stack-trace.h> +#include <vm/natives.h> + +#include <jit/cu-mapping.h> +#include <jit/compiler.h> + +__thread struct native_stack_frame *bottom_stack_frame; + +/* Checks whether given execution address is in kernel space. */ +static bool is_kernel(unsigned long addr) +{ + /* + * Assume that user stack segment is right below the kernel + * space. + */ + + return addr > (unsigned long)__builtin_frame_address(0); +} + +/** + * get_caller_stack_trace_elem - makes @elem to point to the stack + * trace element corresponding to the caller of given element. + * + * Returns 0 on success and -1 when bottom of stack trace reached. + */ +static int get_caller_stack_trace_elem(struct stack_trace_elem *elem) +{ + unsigned long new_addr; + unsigned long ret_addr; + void *new_frame; + + if (elem->is_native) { + struct native_stack_frame *frame; + + frame = elem->frame; + new_frame = frame->prev; + ret_addr = frame->return_address; + } else { + struct jit_stack_frame *frame; + + frame = elem->frame; + new_frame = frame->prev; + ret_addr = frame->return_address; + } + + /* + * We know only return addresses and we don't know the size of + * call instruction that was used. Therefore we don't know + * address of the call site beggining. We store address of the + * last byte of the call site instead which is enough + * information to obtain bytecode offset. + */ + new_addr = ret_addr - 1; + + /* + * We should not continue when address is in kernel becuase + * stack frame might have different format then. We end up in + * kernel when getting stack_trace from signal handler. + */ + if (is_kernel(new_addr) || new_frame == bottom_stack_frame) + return -1; + + elem->is_trampoline = elem->is_native && + called_from_jit_trampoline(elem->frame); + elem->is_native = is_native(new_addr) || elem->is_trampoline; + elem->addr = new_addr; + elem->frame = new_frame; + + return 0; +} + +/** + * get_prev_stack_trace_elem - makes @elem to point to the previous + * stack_trace element that has a compilation unit (JIT method or + * VM native). + * + * Returns 0 on success and -1 when bottom of stack trace reached. + */ +int get_prev_stack_trace_elem(struct stack_trace_elem *elem) +{ + while (get_caller_stack_trace_elem(elem) == 0) { + if (is_vm_native(elem->addr) || + !(elem->is_trampoline || elem->is_native)) + return 0; + } + + return -1; +} + +/** + * init_stack_trace_elem - initializes stack trace element so that it + * points to the nearest JIT or VM native frame. + * + * Returns 0 on success and -1 when bottom of stack trace reached. + */ +int init_stack_trace_elem(struct stack_trace_elem *elem) +{ + elem->is_native = true; + elem->addr = (unsigned long)&init_stack_trace_elem; + elem->frame = __builtin_frame_address(0); + + return get_prev_stack_trace_elem(elem); +} + +/** + * skip_frames_from_class - makes @elem to point to the nearest stack + * trace element which does not belong to any method of class + * which is an instance of @class. Note that this also skips all + * classes that extends @class. + * + * Returns 0 on success and -1 when bottom of stack trace reached. + */ +int skip_frames_from_class(struct stack_trace_elem *elem, struct object *class) +{ + struct compilation_unit *cu; + + do { + cu = get_cu_from_native_addr(elem->addr); + if (cu == NULL) { + fprintf(stderr, + "%s: no compilation unit mapping for %p\n", + __func__, (void*)elem->addr); + return -1; + } + + if (!isInstanceOf(class, cu->method->class)) + return 0; + + } while (get_prev_stack_trace_elem(elem) == 0); + + return -1; +} + +/** + * get_stack_trace_depth - returns number of stack trace elements, + * including @elem. + */ +int get_stack_trace_depth(struct stack_trace_elem *elem) +{ + struct stack_trace_elem tmp; + int depth; + + tmp = *elem; + depth = 1; + + while (get_prev_stack_trace_elem(&tmp) == 0) + depth++; + + return depth; +} -- 1.6.0.6 ------------------------------------------------------------------------------ Are you an open source citizen? Join us for the Open Source Bridge conference! Portland, OR, June 17-19. Two days of sessions, one day of unconference: $250. Need another reason to go? 24-hour hacker lounge. Register today! http://ad.doubleclick.net/clk;215844324;13503038;v?http://opensourcebridge.org _______________________________________________ Jatovm-devel mailing list Jatovm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jatovm-devel