예제 #1
0
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;
}
예제 #2
0
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;
}
예제 #3
0
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
}
예제 #4
0
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);
}
예제 #5
0
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 ();
}
예제 #6
0
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);
}
예제 #7
0
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
}