static gboolean native_stack_with_lldb (pid_t crashed_pid, const char **argv, FILE *commands, char* commands_filename) { gchar *lldb; lldb = g_find_program_in_path ("lldb"); if (!lldb) return FALSE; argv [0] = lldb; argv [1] = "--batch"; argv [2] = "--source"; argv [3] = commands_filename; argv [4] = "--no-lldbinit"; fprintf (commands, "process attach --pid %ld\n", (long) crashed_pid); fprintf (commands, "thread list\n"); fprintf (commands, "thread backtrace all\n"); if (mini_get_debug_options ()->verbose_gdb) { for (int i = 0; i < 32; ++i) { fprintf (commands, "reg read\n"); fprintf (commands, "frame info\n"); fprintf (commands, "frame variable\n"); fprintf (commands, "up\n"); } } fprintf (commands, "detach\n"); fprintf (commands, "quit\n"); return TRUE; }
static gboolean native_stack_with_gdb (pid_t crashed_pid, const char **argv, FILE *commands, char* commands_filename) { gchar *gdb; gdb = g_find_program_in_path ("gdb"); if (!gdb) return FALSE; argv [0] = gdb; argv [1] = "-batch"; argv [2] = "-x"; argv [3] = commands_filename; argv [4] = "-nx"; fprintf (commands, "attach %ld\n", (long) crashed_pid); fprintf (commands, "info threads\n"); fprintf (commands, "thread apply all bt\n"); if (mini_get_debug_options ()->verbose_gdb) { for (int i = 0; i < 32; ++i) { fprintf (commands, "info registers\n"); fprintf (commands, "info frame\n"); fprintf (commands, "info locals\n"); fprintf (commands, "up\n"); } } return TRUE; }
void mono_runtime_install_handlers (void) { #ifndef MONO_CROSS_COMPILE win32_seh_init(); win32_seh_set_handler(SIGFPE, mono_sigfpe_signal_handler); win32_seh_set_handler(SIGILL, mono_sigill_signal_handler); win32_seh_set_handler(SIGSEGV, mono_sigsegv_signal_handler); if (mini_get_debug_options ()->handle_sigint) win32_seh_set_handler(SIGINT, mono_sigint_signal_handler); #endif }
void mono_runtime_posix_install_handlers (void) { sigset_t signal_set; sigemptyset (&signal_set); if (mini_get_debug_options ()->handle_sigint) { add_signal_handler (SIGINT, mono_sigint_signal_handler, SA_RESTART); sigaddset (&signal_set, SIGINT); } add_signal_handler (SIGFPE, mono_sigfpe_signal_handler, 0); sigaddset (&signal_set, SIGFPE); add_signal_handler (SIGQUIT, sigquit_signal_handler, SA_RESTART); sigaddset (&signal_set, SIGQUIT); add_signal_handler (SIGILL, mono_sigill_signal_handler, 0); sigaddset (&signal_set, SIGILL); add_signal_handler (SIGBUS, mono_sigsegv_signal_handler, 0); sigaddset (&signal_set, SIGBUS); if (mono_jit_trace_calls != NULL) { add_signal_handler (SIGUSR2, sigusr2_signal_handler, SA_RESTART); sigaddset (&signal_set, SIGUSR2); } /* it seems to have become a common bug for some programs that run as parents * of many processes to block signal delivery for real time signals. * We try to detect and work around their breakage here. */ if (mono_gc_get_suspend_signal () != -1) sigaddset (&signal_set, mono_gc_get_suspend_signal ()); if (mono_gc_get_restart_signal () != -1) sigaddset (&signal_set, mono_gc_get_restart_signal ()); sigaddset (&signal_set, SIGCHLD); signal (SIGPIPE, SIG_IGN); sigaddset (&signal_set, SIGPIPE); add_signal_handler (SIGABRT, sigabrt_signal_handler, 0); sigaddset (&signal_set, SIGABRT); /* catch SIGSEGV */ add_signal_handler (SIGSEGV, mono_sigsegv_signal_handler, 0); sigaddset (&signal_set, SIGSEGV); sigprocmask (SIG_UNBLOCK, &signal_set, NULL); }
void mono_runtime_cleanup_handlers (void) { if (mini_get_debug_options ()->handle_sigint) remove_signal_handler (SIGINT); remove_signal_handler (SIGFPE); remove_signal_handler (SIGQUIT); remove_signal_handler (SIGILL); remove_signal_handler (SIGBUS); if (mono_jit_trace_calls != NULL) remove_signal_handler (SIGUSR2); remove_signal_handler (SIGABRT); remove_signal_handler (SIGSEGV); free_saved_signal_handlers (); }
static void add_signal_handler (int signo, gpointer handler) { struct sigaction sa; struct sigaction previous_sa; #ifdef MONO_ARCH_USE_SIGACTION sa.sa_sigaction = handler; sigemptyset (&sa.sa_mask); sa.sa_flags = SA_SIGINFO; #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK /*Apple likes to deliver SIGBUS for *0 */ #ifdef __APPLE__ if (signo == SIGSEGV || signo == SIGBUS) { #else if (signo == SIGSEGV) { #endif sa.sa_flags |= SA_ONSTACK; /* * libgc will crash when trying to do stack marking for threads which are on * an altstack, so delay the suspend signal after the signal handler has * executed. */ if (mono_gc_get_suspend_signal () != -1) sigaddset (&sa.sa_mask, mono_gc_get_suspend_signal ()); } #endif if (signo == SIGSEGV) { /* * Delay abort signals while handling SIGSEGVs since they could go unnoticed. */ sigset_t block_mask; sigemptyset (&block_mask); sigaddset (&sa.sa_mask, mono_thread_get_abort_signal ()); } #else sa.sa_handler = handler; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; #endif g_assert (sigaction (signo, &sa, &previous_sa) != -1); /* if there was already a handler in place for this signal, store it */ if (! (previous_sa.sa_flags & SA_SIGINFO) && (SIG_DFL == previous_sa.sa_handler)) { /* it there is no sa_sigaction function and the sa_handler is default, we can safely ignore this */ } else { if (mono_do_signal_chaining) save_old_signal_handler (signo, &previous_sa); } } static void remove_signal_handler (int signo) { struct sigaction sa; struct sigaction *saved_action = get_saved_signal_handler (signo); if (!saved_action) { sa.sa_handler = SIG_DFL; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (signo, &sa, NULL); } else { g_assert (sigaction (signo, saved_action, NULL) != -1); } } void mono_runtime_posix_install_handlers (void) { sigset_t signal_set; if (mini_get_debug_options ()->handle_sigint) add_signal_handler (SIGINT, mono_sigint_signal_handler); add_signal_handler (SIGFPE, mono_sigfpe_signal_handler); add_signal_handler (SIGQUIT, sigquit_signal_handler); add_signal_handler (SIGILL, mono_sigill_signal_handler); add_signal_handler (SIGBUS, mono_sigsegv_signal_handler); if (mono_jit_trace_calls != NULL) add_signal_handler (SIGUSR2, sigusr2_signal_handler); if (!mono_thread_info_new_interrupt_enabled ()) add_signal_handler (mono_thread_get_abort_signal (), sigusr1_signal_handler); /* it seems to have become a common bug for some programs that run as parents * of many processes to block signal delivery for real time signals. * We try to detect and work around their breakage here. */ sigemptyset (&signal_set); sigaddset (&signal_set, mono_thread_get_abort_signal ()); sigprocmask (SIG_UNBLOCK, &signal_set, NULL); signal (SIGPIPE, SIG_IGN); add_signal_handler (SIGABRT, sigabrt_signal_handler); /* catch SIGSEGV */ add_signal_handler (SIGSEGV, mono_sigsegv_signal_handler); }
static void dump_native_stacktrace (const char *signal, void *ctx) { #ifdef HAVE_BACKTRACE_SYMBOLS void *array [256]; char **names; int i, size; mono_runtime_printf_err ("\nNative stacktrace:\n"); size = backtrace (array, 256); names = backtrace_symbols (array, size); for (i = 0; i < size; ++i) { mono_runtime_printf_err ("\t%s", names [i]); } g_free (names); /* Try to get more meaningful information using gdb */ char *debugger_log = mono_debugger_state_str (); if (debugger_log) { fprintf (stderr, "\n\tDebugger session state:\n%s\n", debugger_log); } #if !defined(HOST_WIN32) && defined(HAVE_SYS_SYSCALL_H) && (defined(SYS_fork) || HAVE_FORK) if (!mini_get_debug_options ()->no_gdb_backtrace) { /* From g_spawn_command_line_sync () in eglib */ pid_t pid; int status; pid_t crashed_pid = getpid (); #if defined(TARGET_OSX) MonoStackHash hashes; #endif gchar *output = NULL; MonoContext mctx; if (ctx) { gboolean leave = FALSE; gboolean dump_for_merp = FALSE; #if defined(TARGET_OSX) dump_for_merp = mono_merp_enabled (); #endif if (!dump_for_merp) { #ifdef DISABLE_STRUCTURED_CRASH leave = TRUE; #elif defined(TARGET_OSX) mini_register_sigterm_handler (); #endif } #ifdef TARGET_OSX if (!leave) { mono_sigctx_to_monoctx (ctx, &mctx); // Do before forking if (!mono_threads_summarize (&mctx, &output, &hashes)) g_assert_not_reached (); } // We want our crash, and don't have telemetry // So we dump to disk if (!leave && !dump_for_merp) mono_crash_dump (output); #endif } /* * glibc fork acquires some locks, so if the crash happened inside malloc/free, * it will deadlock. Call the syscall directly instead. */ #if defined(HOST_ANDROID) /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */ g_assert_not_reached (); #elif !defined(HOST_DARWIN) && defined(SYS_fork) pid = (pid_t) syscall (SYS_fork); #elif HAVE_FORK pid = (pid_t) fork (); #else g_assert_not_reached (); #endif #if defined (HAVE_PRCTL) && defined(PR_SET_PTRACER) if (pid > 0) { // Allow gdb to attach to the process even if ptrace_scope sysctl variable is set to // a value other than 0 (the most permissive ptrace scope). Most modern Linux // distributions set the scope to 1 which allows attaching only to direct children of // the current process prctl (PR_SET_PTRACER, pid, 0, 0, 0); } #endif #if defined(TARGET_OSX) if (mono_merp_enabled ()) { if (pid == 0) { if (!ctx) { mono_runtime_printf_err ("\nMust always pass non-null context when using merp.\n"); exit (1); } char *full_version = mono_get_runtime_build_info (); mono_merp_invoke (crashed_pid, signal, output, &hashes, full_version); exit (1); } } #endif if (pid == 0) { dup2 (STDERR_FILENO, STDOUT_FILENO); mono_gdb_render_native_backtraces (crashed_pid); exit (1); } mono_runtime_printf_err ("\nDebug info from gdb:\n"); waitpid (pid, &status, 0); } #endif #else #ifdef HOST_ANDROID /* set DUMPABLE for this process so debuggerd can attach with ptrace(2), see: * https://android.googlesource.com/platform/bionic/+/151da681000c07da3c24cd30a3279b1ca017f452/linker/debugger.cpp#206 * this has changed on later versions of Android. Also, we don't want to * set this on start-up as DUMPABLE has security implications. */ prctl (PR_SET_DUMPABLE, 1); mono_runtime_printf_err ("\nNo native Android stacktrace (see debuggerd output).\n"); #endif #endif }