diff --git a/vm/vmcore/src/util/linux/crash_handler.cpp b/vm/vmcore/src/util/linux/crash_handler.cpp index fe08514..1beb213 100644 --- a/vm/vmcore/src/util/linux/crash_handler.cpp +++ b/vm/vmcore/src/util/linux/crash_handler.cpp @@ -14,44 +14,66 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** +/** * @author Intel, Evgueni Brevnov * @version $Revision: 1.1.2.1.4.3 $ - */ + */ +#include #include #include -#include #include -#include + +#include "environment.h" #include "crash_handler.h" -static char executable[128];// prepare executable argument -static char pid[128]; // prepare pid argument as a string -static sem_t gdb_started; // prevent forking debugger more than once +static char g_executable[1024]; // Executable file name +static char g_strpid[128]; // Current pid as a string +static sem_t g_sem_started; // Prevent forking debugger more than once +static bool g_prepared = false; // Flag is set if gdb crash handler is prepared +static bool g_enabled = false; // vm.crash_handler value is stored here + #if defined (__INTEL_COMPILER) #pragma warning ( push ) #pragma warning (disable:869) #endif -static void crash_handler (int signum, siginfo_t* info, void* context) { - if (0 == sem_trywait(&gdb_started)) { - if (fork() == 0) { - fprintf(stderr, "----------------------------------------\n" - "gdb %s %s\n" - "----------------------------------------\n" - , executable, pid); fflush(stderr); - execl("/usr/bin/gdb", "gdb", executable, pid, NULL); - perror("Can't run debugger"); - } else { - // give gdb chance to start before the default handler kills the app - sleep(10); - } - } else { - // gdb was already started, - // reset the abort handler - signal(signum, 0); + + +bool is_gdb_crash_handler_enabled() +{ + if (!g_prepared) + return false; + + if (VM_Global_State::loader_env == NULL) + return g_enabled; + + return get_boolean_property("vm.crash_handler", FALSE, VM_PROPERTIES); +} + + +bool gdb_crash_handler() +{ + if (!g_prepared || + 0 != sem_trywait(&g_sem_started)) // gdb was already started + return false; + + if (fork() == 0) + { + fprintf(stderr, "----------------------------------------\n" + "gdb %s %s\n" + "----------------------------------------\n" + , g_executable, g_strpid); + fflush(stderr); + + execlp("gdb", "gdb", g_executable, g_strpid, NULL); + perror("Can't run gdb"); + } + else + { + // give gdb chance to start before the default handler kills the app + sleep(10); } } #if defined (__INTEL_COMPILER) @@ -68,21 +90,16 @@ int get_executable_name(char executable[ return 0; } -static int get_pid(char pid[], int len) { - return snprintf(pid,len,"%d",getpid()); -} -void init_crash_handler() { - get_executable_name(executable, sizeof(executable)); - get_pid(pid, sizeof(pid)); - sem_init(&gdb_started, 0, 1); -} +int init_gdb_crash_handler() +{ + if (sem_init(&g_sem_started, 0, 1) != 0 || + get_executable_name(g_executable, sizeof(g_executable)) != 0) + return -1; -void install_crash_handler(int signum) { - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = crash_handler; - sigaction(signum, &sa, NULL); -} + snprintf(g_strpid, sizeof(g_strpid), "%d", getpid()); + g_prepared = true; + assert(VM_Global_State::loader_env); + g_enabled = get_boolean_property("vm.crash_handler", FALSE, VM_PROPERTIES); +} diff --git a/vm/vmcore/src/util/linux/crash_handler.h b/vm/vmcore/src/util/linux/crash_handler.h index 3c4d4b7..3be6e65 100644 --- a/vm/vmcore/src/util/linux/crash_handler.h +++ b/vm/vmcore/src/util/linux/crash_handler.h @@ -24,19 +24,28 @@ #define _CRASH_HANDLER_H /** * \file - * Provides definition needed to install crash handler (from crash_handler.cpp) + * Provides definition needed to install gdb crash handler. */ /** - * Initializes the static state needed for crash handler. + * Checks if gdb crash handler is enabled and prepared. + * + * @return true if gdb crash handler is enabled and ready for use. + */ +bool is_gdb_crash_handler_enabled(); + +/** + * Initializes the static state needed for gdb crash handler. + * + * @return 0 on success or negative value on failure */ -void init_crash_handler(); +int init_gdb_crash_handler(); /** - * Installs specified signal handler to call gdb. + * Invokes gdb. * - * @param signum A signal number constant, e.g. SIGABRT or SIGSEGV + * @return true on success or false on failure */ -void install_crash_handler(int signum); +bool gdb_crash_handler(); #endif // _CRASH_HANDLER_H diff --git a/vm/vmcore/src/util/linux/signals_em64t.cpp b/vm/vmcore/src/util/linux/signals_em64t.cpp index 2d57321..c01edbf 100644 --- a/vm/vmcore/src/util/linux/signals_em64t.cpp +++ b/vm/vmcore/src/util/linux/signals_em64t.cpp @@ -67,6 +67,7 @@ #include "thread_manager.h" #include "exception_filter.h" #include "interpreter.h" +#include "crash_handler.h" #include "stack_dump.h" // Variables used to locate the context from the signal handler @@ -380,10 +381,16 @@ void null_java_reference_handler(int sig fprintf(stderr, "SIGSEGV in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } @@ -402,10 +409,16 @@ void null_java_divide_by_zero_handler(in fprintf(stderr, "SIGFPE in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } /* @@ -532,10 +545,16 @@ void abort_handler (int signum, siginfo_ ucontext_t *uc = (ucontext_t *)context; Registers regs; linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } void initialize_signals() @@ -577,9 +596,8 @@ void initialize_signals() sigaction( SIGABRT, &sa, NULL); /* abort_handler installed */ - extern int get_executable_name(char*, int); - /* initialize the name of the executable (to be used by addr2line) */ - get_executable_name(executable, sizeof(executable)); + // Prepare gdb crash handler + init_gdb_crash_handler(); } //initialize_signals diff --git a/vm/vmcore/src/util/linux/signals_ia32.cpp b/vm/vmcore/src/util/linux/signals_ia32.cpp index bb2aeb4..587955f 100644 --- a/vm/vmcore/src/util/linux/signals_ia32.cpp +++ b/vm/vmcore/src/util/linux/signals_ia32.cpp @@ -491,10 +491,16 @@ void null_java_reference_handler(int sig fprintf(stderr, "SIGSEGV in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } @@ -516,10 +522,16 @@ void null_java_divide_by_zero_handler(in fprintf(stderr, "SIGFPE in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } void jvmti_jit_breakpoint_handler(int signum, siginfo_t* UNREF info, void* context) @@ -541,10 +553,16 @@ void jvmti_jit_breakpoint_handler(int si fprintf(stderr, "SIGTRAP in VM code.\n"); linux_ucontext_to_regs(®s, uc); + // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } /* @@ -640,17 +658,15 @@ void abort_handler (int signum, siginfo_ ucontext_t *uc = (ucontext_t *)context; linux_ucontext_to_regs(®s, uc); - // reset handler to avoid loop in case st_print_stack fails - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = SIG_DFL; - sigaction(SIGABRT, &sa, NULL); - // setup default handler signal(signum, SIG_DFL); - // print stack trace - st_print_stack(®s); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } /* @@ -779,15 +795,8 @@ void initialize_signals() sigaction( SIGABRT, &sa, NULL); /* abort_handler installed */ - extern int get_executable_name(char*, int); - /* initialize the name of the executable (to be used by addr2line) */ - get_executable_name(executable, sizeof(executable)); - - if (get_boolean_property("vm.crash_handler", FALSE, VM_PROPERTIES)) { - init_crash_handler(); - // can't install crash handler immediately, - // as we have already SIGABRT and SIGSEGV handlers - } + // Prepare gdb crash handler + init_gdb_crash_handler(); } //initialize_signals diff --git a/vm/vmcore/src/util/linux/signals_ipf.cpp b/vm/vmcore/src/util/linux/signals_ipf.cpp index e174d8c..29a7a20 100644 --- a/vm/vmcore/src/util/linux/signals_ipf.cpp +++ b/vm/vmcore/src/util/linux/signals_ipf.cpp @@ -75,10 +75,34 @@ #include "jvmti_break_intf.h" #include -/** - * the saved copy of the executable name. - */ -static char executable[1024]; + + +void linux_ucontext_to_regs(Registers* regs, ucontext_t* uc) +{ + memcpy(regs->gr, uc->uc_mcontext.sc_gr, sizeof(regs->gr)); + memcpy(regs->fp, uc->uc_mcontext.sc_fr, sizeof(regs->fp)); + memcpy(regs->br, uc->uc_mcontext.sc_br, sizeof(regs->br)); + + regs->preds = uc->uc_mcontext.sc_pr; + regs->nats = uc->uc_mcontext.sc_ar_rnat; + regs->pfs = uc->uc_mcontext.sc_ar_pfs; + regs->bsp = (uint64*)uc->uc_mcontext.sc_ar_bsp; + regs->ip = uc->uc_mcontext.sc_ip; +} + +void linux_regs_to_ucontext(ucontext_t* uc, Registers* regs) +{ + memcpy(uc->uc_mcontext.sc_gr, regs->gr, sizeof(regs->gr)); + memcpy(uc->uc_mcontext.sc_fr, regs->fp, sizeof(regs->fp)); + memcpy(uc->uc_mcontext.sc_br, regs->br, sizeof(regs->br)); + + uc->uc_mcontext.sc_pr = regs->preds; + uc->uc_mcontext.sc_ar_rnat = regs->nats; + uc->uc_mcontext.sc_ar_pfs = regs->pfs; + uc->uc_mcontext.sc_ar_bsp = (uint64)regs->bsp; + uc->uc_mcontext.sc_ip = regs->ip; +} + void asm_jvmti_exception_catch_callback() { // FIXME: not implemented @@ -97,9 +121,20 @@ static bool java_throw_from_sigcontext(u } void abort_handler (int signum, siginfo_t* info, void* context) { - fprintf(stderr, "FIXME: abort_handler\n"); + fprintf(stderr, "SIGABRT in VM code.\n"); + Registers regs; + ucontext_t *uc = (ucontext_t *)context; + linux_ucontext_to_regs(®s, uc); + + // setup default handler signal(signum, SIG_DFL); - kill(getpid(), signum); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } @@ -110,12 +145,6 @@ static void throw_from_sigcontext(uconte abort(); } -void linux_ucontext_to_regs(Registers* regs, ucontext_t* uc) -{ - //TODO: ADD Copying of IPF registers here like it was done on ia32!! - abort(); -} - void null_java_divide_by_zero_handler(int signum, siginfo_t* UNREF info, void* context) { ucontext_t *uc = (ucontext_t *)context; @@ -136,10 +165,16 @@ void null_java_divide_by_zero_handler(in fprintf(stderr, "SIGFPE in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); - st_print_stack(®s); - // crash with default handler - signal(signum, 0); + // setup default handler + signal(signum, SIG_DFL); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } @@ -373,9 +408,16 @@ void null_java_reference_handler(int sig fprintf(stderr, "SIGSEGV in VM code.\n"); Registers regs; linux_ucontext_to_regs(®s, uc); - st_print_stack(®s); - signal(signum, 0); + // setup default handler + signal(signum, SIG_DFL); + + if (!is_gdb_crash_handler_enabled() || + !gdb_crash_handler()) + { + // print stack trace + st_print_stack(®s); + } } void initialize_signals() { @@ -416,15 +458,9 @@ void initialize_signals() { sigaction( SIGABRT, &sa, NULL); /* abort_handler installed */ - extern int get_executable_name(char*, int); - /* initialize the name of the executable (to be used by addr2line) */ - get_executable_name(executable, sizeof(executable)); + // Prepare gdb crash handler + init_gdb_crash_handler(); - if (get_boolean_property("vm.crash_handler", FALSE, VM_PROPERTIES)) { - init_crash_handler(); - // can't install crash handler immediately, - // as we have already SIGABRT and SIGSEGV handlers - } } //initialize_signals void shutdown_signals() { @@ -446,21 +482,6 @@ static uint32 exam_point; bool SuspendThread(unsigned xx){ return 0; } bool ResumeThread(unsigned xx){ return 1; } -void linux_regs_to_ucontext(ucontext_t* uc, Registers* regs) -{ -//TODO: ADD Copying of IPF registers here like it was done on ia32!! -} - -static void linux_sigcontext_to_regs(Registers* regs, sigcontext* sc) -{ -//TODO: ADD Copying of IPF registers here like it was done on ia32!! -} - -static void linux_regs_to_sigcontext(sigcontext* sc, Registers* regs) -{ -//TODO: ADD Copying of IPF registers here like it was done on ia32!! -} - static bool linux_throw_from_sigcontext(ucontext_t *uc, Class* exc_clss) { // FIXME: not implemented