Skip to content

Commit 4c93e38

Browse files
[2019-12] [merp] MONO_DEBUG=no-gdb-stacktrace shouldn't disable MERP (#18611)
* [merp] MONO_DEBUG=no-gdb-stacktrace shouldn't disable MERP Only prevent `gdb` or `lldb` from being invoked. MERP is controlled by separate mechanisms --- configure flags for crash reporting and an explicit opt-in icall. The existing code collects crashed process data in-process, but it writes the data out (using mono_merp_invoke) in the forked child process. This is a bit surprising, but in the interest of not disturbing the code too much, this PR doesn't change that. We should revisit `dump_native_stacktrace` again in the future and rationalize these decisions. Addresses #18565 * Fix indentation Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
1 parent 8b72dbb commit 4c93e38

1 file changed

Lines changed: 134 additions & 112 deletions

File tree

mono/mini/mini-posix.c

Lines changed: 134 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,37 @@ assert_printer_callback (void)
914914
mono_dump_native_crash_info ("SIGABRT", NULL, NULL);
915915
}
916916

917+
#if !defined (HOST_WIN32)
918+
/**
919+
* fork_crash_safe:
920+
*
921+
* Version of \c fork that is safe to call from an async context such as a
922+
* signal handler even if the process crashed inside libc.
923+
*
924+
* Returns 0 to the child process, >0 to the parent process or <0 on error.
925+
*/
926+
static pid_t
927+
fork_crash_safe (void)
928+
{
929+
pid_t pid;
930+
/*
931+
* glibc fork acquires some locks, so if the crash happened inside malloc/free,
932+
* it will deadlock. Call the syscall directly instead.
933+
*/
934+
#if defined(HOST_ANDROID)
935+
/* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
936+
g_assert_not_reached ();
937+
#elif !defined(HOST_DARWIN) && defined(SYS_fork)
938+
pid = (pid_t) syscall (SYS_fork);
939+
#elif HAVE_FORK
940+
pid = (pid_t) fork ();
941+
#else
942+
g_assert_not_reached ();
943+
#endif
944+
return pid;
945+
}
946+
#endif
947+
917948
static void
918949
dump_native_stacktrace (const char *signal, MonoContext *mctx)
919950
{
@@ -956,159 +987,150 @@ dump_native_stacktrace (const char *signal, MonoContext *mctx)
956987
}
957988

958989
#if !defined(HOST_WIN32) && defined(HAVE_SYS_SYSCALL_H) && (defined(SYS_fork) || HAVE_FORK)
959-
if (!mini_debug_options.no_gdb_backtrace) {
960-
/* From g_spawn_command_line_sync () in eglib */
961-
pid_t pid;
962-
int status;
963-
pid_t crashed_pid = getpid ();
990+
pid_t crashed_pid = getpid ();
964991

965992
#ifndef DISABLE_CRASH_REPORTING
966-
gchar *output = NULL;
967-
MonoStackHash hashes;
968-
MonoStateMem merp_mem;
969-
memset (&merp_mem, 0, sizeof (merp_mem));
970-
971-
if (!double_faulted) {
972-
gboolean leave = FALSE;
973-
gboolean dump_for_merp = FALSE;
993+
gchar *output = NULL;
994+
MonoStackHash hashes;
995+
MonoStateMem merp_mem;
996+
memset (&merp_mem, 0, sizeof (merp_mem));
997+
998+
if (!double_faulted) {
999+
gboolean leave = FALSE;
1000+
gboolean dump_for_merp = FALSE;
9741001
#if defined(TARGET_OSX)
975-
dump_for_merp = mono_merp_enabled ();
1002+
dump_for_merp = mono_merp_enabled ();
9761003
#endif
9771004

9781005
#ifndef DISABLE_STRUCTURED_CRASH
979-
mini_register_sigterm_handler ();
1006+
mini_register_sigterm_handler ();
9801007
#endif
9811008

982-
if (!dump_for_merp) {
1009+
if (!dump_for_merp) {
9831010
#ifdef DISABLE_STRUCTURED_CRASH
984-
leave = TRUE;
1011+
leave = TRUE;
9851012
#endif
986-
}
1013+
}
9871014

988-
MonoContext *passed_ctx = NULL;
989-
if (!leave && mctx) {
990-
passed_ctx = mctx;
991-
}
1015+
MonoContext *passed_ctx = NULL;
1016+
if (!leave && mctx) {
1017+
passed_ctx = mctx;
1018+
}
9921019

993-
g_async_safe_printf ("\n=================================================================\n");
994-
g_async_safe_printf ("\tTelemetry Dumper:\n");
995-
g_async_safe_printf ("=================================================================\n");
1020+
g_async_safe_printf ("\n=================================================================\n");
1021+
g_async_safe_printf ("\tTelemetry Dumper:\n");
1022+
g_async_safe_printf ("=================================================================\n");
9961023

997-
if (!leave) {
998-
mono_summarize_timeline_start ();
999-
mono_summarize_toggle_assertions (TRUE);
1024+
if (!leave) {
1025+
mono_summarize_timeline_start ();
1026+
mono_summarize_toggle_assertions (TRUE);
10001027

1001-
int mono_max_summary_len = 500000;
1002-
int mono_state_tmp_file_tag = 1;
1003-
mono_state_alloc_mem (&merp_mem, mono_state_tmp_file_tag, mono_max_summary_len * sizeof (gchar));
1028+
int mono_max_summary_len = 500000;
1029+
int mono_state_tmp_file_tag = 1;
1030+
mono_state_alloc_mem (&merp_mem, mono_state_tmp_file_tag, mono_max_summary_len * sizeof (gchar));
10041031

1005-
// Returns success, so leave if !success
1006-
leave = !mono_threads_summarize (passed_ctx, &output, &hashes, FALSE, TRUE, (gchar *) merp_mem.mem, mono_max_summary_len);
1007-
}
1032+
// Returns success, so leave if !success
1033+
leave = !mono_threads_summarize (passed_ctx, &output, &hashes, FALSE, TRUE, (gchar *) merp_mem.mem, mono_max_summary_len);
1034+
}
10081035

1009-
if (!leave) {
1010-
// Wait for the other threads to clean up and exit their handlers
1011-
// We can't lock / wait indefinitely, in case one of these threads got stuck somehow
1012-
// while dumping.
1013-
g_async_safe_printf ("\nWaiting for dumping threads to resume\n");
1014-
sleep (1);
1015-
}
1036+
if (!leave) {
1037+
// Wait for the other threads to clean up and exit their handlers
1038+
// We can't lock / wait indefinitely, in case one of these threads got stuck somehow
1039+
// while dumping.
1040+
g_async_safe_printf ("\nWaiting for dumping threads to resume\n");
1041+
sleep (1);
1042+
}
10161043

1017-
// We want our crash, and don't have telemetry
1018-
// So we dump to disk
1019-
if (!leave && !dump_for_merp) {
1020-
mono_summarize_timeline_phase_log (MonoSummaryCleanup);
1021-
mono_crash_dump (output, &hashes);
1022-
mono_summarize_timeline_phase_log (MonoSummaryDone);
1023-
mono_summarize_toggle_assertions (FALSE);
1024-
}
1044+
// We want our crash, and don't have telemetry
1045+
// So we dump to disk
1046+
if (!leave && !dump_for_merp) {
1047+
mono_summarize_timeline_phase_log (MonoSummaryCleanup);
1048+
mono_crash_dump (output, &hashes);
1049+
mono_summarize_timeline_phase_log (MonoSummaryDone);
1050+
mono_summarize_toggle_assertions (FALSE);
10251051
}
1052+
}
10261053
#endif // DISABLE_CRASH_REPORTING
10271054

1028-
/*
1029-
* glibc fork acquires some locks, so if the crash happened inside malloc/free,
1030-
* it will deadlock. Call the syscall directly instead.
1031-
*/
1032-
#if defined(HOST_ANDROID)
1033-
/* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
1034-
g_assert_not_reached ();
1035-
#elif !defined(HOST_DARWIN) && defined(SYS_fork)
1036-
pid = (pid_t) syscall (SYS_fork);
1037-
#elif HAVE_FORK
1038-
pid = (pid_t) fork ();
1039-
#else
1040-
g_assert_not_reached ();
1055+
pid_t pid = crashed_pid; /* init to some >0 value */
1056+
gboolean need_to_fork = !mini_debug_options.no_gdb_backtrace;
1057+
1058+
#if defined (TARGET_OSX) && !defined (DISABLE_CRASH_REPORTING)
1059+
need_to_fork |= mono_merp_enabled ();
10411060
#endif
10421061

1062+
if (need_to_fork)
1063+
pid = fork_crash_safe ();
1064+
10431065
#if defined (HAVE_PRCTL) && defined(PR_SET_PTRACER)
1044-
if (pid > 0) {
1045-
// Allow gdb to attach to the process even if ptrace_scope sysctl variable is set to
1046-
// a value other than 0 (the most permissive ptrace scope). Most modern Linux
1047-
// distributions set the scope to 1 which allows attaching only to direct children of
1048-
// the current process
1049-
prctl (PR_SET_PTRACER, pid, 0, 0, 0);
1050-
}
1066+
if (need_to_fork && pid > 0) {
1067+
// Allow gdb to attach to the process even if ptrace_scope sysctl variable is set to
1068+
// a value other than 0 (the most permissive ptrace scope). Most modern Linux
1069+
// distributions set the scope to 1 which allows attaching only to direct children of
1070+
// the current process
1071+
prctl (PR_SET_PTRACER, pid, 0, 0, 0);
1072+
}
10511073
#endif
10521074

10531075
#if defined(TARGET_OSX) && !defined(DISABLE_CRASH_REPORTING)
1054-
if (!double_faulted && mono_merp_enabled ()) {
1055-
if (pid == 0) {
1056-
if (output) {
1057-
gboolean merp_upload_success = mono_merp_invoke (crashed_pid, signal, output, &hashes);
1058-
1059-
if (!merp_upload_success) {
1060-
g_async_safe_printf("\nThe MERP upload step has failed.\n");
1061-
} else {
1062-
// Remove
1063-
g_async_safe_printf("\nThe MERP upload step has succeeded.\n");
1064-
mono_summarize_timeline_phase_log (MonoSummaryDone);
1065-
}
1066-
mono_summarize_toggle_assertions (FALSE);
1076+
if (!double_faulted && mono_merp_enabled ()) {
1077+
/* FIXME: why are we running mono_merp_invoke in the forked process? */
1078+
if (pid == 0) {
1079+
if (output) {
1080+
gboolean merp_upload_success = mono_merp_invoke (crashed_pid, signal, output, &hashes);
1081+
1082+
if (!merp_upload_success) {
1083+
g_async_safe_printf("\nThe MERP upload step has failed.\n");
10671084
} else {
1068-
g_async_safe_printf("\nMerp dump step not run, no dump created.\n");
1085+
// Remove
1086+
g_async_safe_printf("\nThe MERP upload step has succeeded.\n");
1087+
mono_summarize_timeline_phase_log (MonoSummaryDone);
10691088
}
1089+
mono_summarize_toggle_assertions (FALSE);
1090+
} else {
1091+
g_async_safe_printf("\nMerp dump step not run, no dump created.\n");
10701092
}
10711093
}
1094+
}
10721095
#endif
10731096

1074-
if (pid == 0) {
1075-
dup2 (STDERR_FILENO, STDOUT_FILENO);
1076-
1077-
g_async_safe_printf ("\n=================================================================\n");
1078-
g_async_safe_printf("\tExternal Debugger Dump:\n");
1079-
g_async_safe_printf ("=================================================================\n");
1080-
mono_gdb_render_native_backtraces (crashed_pid);
1081-
_exit (1);
1082-
} else if (pid > 0) {
1083-
waitpid (pid, &status, 0);
1084-
} else {
1085-
// If we can't fork, do as little as possible before exiting
1086-
#ifndef DISABLE_CRASH_REPORTING
1087-
output = NULL;
1088-
#endif
1089-
}
1097+
if (!mini_debug_options.no_gdb_backtrace && pid == 0) {
1098+
dup2 (STDERR_FILENO, STDOUT_FILENO);
10901099

1091-
if (double_faulted) {
1092-
g_async_safe_printf("\nExiting early due to double fault.\n");
1100+
g_async_safe_printf ("\n=================================================================\n");
1101+
g_async_safe_printf("\tExternal Debugger Dump:\n");
1102+
g_async_safe_printf ("=================================================================\n");
1103+
mono_gdb_render_native_backtraces (crashed_pid);
1104+
_exit (1);
1105+
} else if (need_to_fork && pid > 0) {
1106+
int status;
1107+
waitpid (pid, &status, 0);
1108+
} else {
1109+
// If we can't fork, do as little as possible before exiting
10931110
#ifndef DISABLE_CRASH_REPORTING
1094-
mono_state_free_mem (&merp_mem);
1111+
output = NULL;
10951112
#endif
1096-
_exit (-1);
1097-
}
1113+
}
10981114

1115+
if (double_faulted) {
1116+
g_async_safe_printf("\nExiting early due to double fault.\n");
10991117
#ifndef DISABLE_CRASH_REPORTING
1100-
if (output) {
1101-
// We've already done our gdb dump and our telemetry steps. Before exiting,
1102-
// see if we can notify any attached debugger instances.
1103-
//
1104-
// At this point we are accepting that the below step might end in a crash
1105-
mini_get_dbg_callbacks ()->send_crash (output, &hashes, 0 /* wait # seconds */);
1106-
}
1107-
output = NULL;
11081118
mono_state_free_mem (&merp_mem);
11091119
#endif
1120+
_exit (-1);
1121+
}
11101122

1123+
#ifndef DISABLE_CRASH_REPORTING
1124+
if (output) {
1125+
// We've already done our gdb dump and our telemetry steps. Before exiting,
1126+
// see if we can notify any attached debugger instances.
1127+
//
1128+
// At this point we are accepting that the below step might end in a crash
1129+
mini_get_dbg_callbacks ()->send_crash (output, &hashes, 0 /* wait # seconds */);
11111130
}
1131+
output = NULL;
1132+
mono_state_free_mem (&merp_mem);
1133+
#endif
11121134
#endif
11131135
#else
11141136
#ifdef HOST_ANDROID

0 commit comments

Comments
 (0)