diff options
-rw-r--r-- | gdb/NEWS | 7 | ||||
-rw-r--r-- | gdb/config.in | 6 | ||||
-rwxr-xr-x | gdb/configure | 51 | ||||
-rw-r--r-- | gdb/configure.ac | 22 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 18 | ||||
-rw-r--r-- | gdb/event-top.c | 140 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/bt-on-fatal-signal.c | 22 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/bt-on-fatal-signal.exp | 134 | ||||
-rw-r--r-- | gdb/ui-file.h | 9 |
9 files changed, 398 insertions, 11 deletions
@@ -3,6 +3,13 @@ *** Changes since GDB 11 +maint set backtrace-on-fatal-signal on|off +maint show backtrace-on-fatal-signal + This setting is 'on' by default. When 'on' GDB will print a limited + backtrace to stderr in the situation where GDB terminates with a + fatal signal. This only supported on some platforms where the + backtrace and backtrace_symbols_fd functions are available. + *** Changes in GDB 11 * The 'set disassembler-options' command now supports specifying options diff --git a/gdb/config.in b/gdb/config.in index 2c30504..af3680c 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -162,6 +162,12 @@ /* Define to 1 if your system has the etext variable. */ #undef HAVE_ETEXT +/* Define to 1 if execinfo.h backtrace functions are available. */ +#undef HAVE_EXECINFO_BACKTRACE + +/* Define to 1 if you have the <execinfo.h> header file. */ +#undef HAVE_EXECINFO_H + /* Define to 1 if you have the `fdwalk' function. */ #undef HAVE_FDWALK diff --git a/gdb/configure b/gdb/configure index 5d89635..f0b1af4 100755 --- a/gdb/configure +++ b/gdb/configure @@ -12315,6 +12315,18 @@ fi done +for ac_header in execinfo.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "execinfo.h" "ac_cv_header_execinfo_h" "$ac_includes_default" +if test "x$ac_cv_header_execinfo_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EXECINFO_H 1 +_ACEOF + +fi + +done + # ------------------------- # # Checks for declarations. # @@ -16484,6 +16496,45 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $found" >&5 $as_echo "$found" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether execinfo.h backtrace is available" >&5 +$as_echo_n "checking whether execinfo.h backtrace is available... " >&6; } +if ${gdb_cv_execinfo_backtrace+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <execinfo.h> + +int +main () +{ + + int f; + void *b[2]; + f = backtrace (b, 2); + backtrace_symbols_fd (b, f, 2); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gdb_cv_execinfo_backtrace=yes +else + gdb_cv_execinfo_backtrace=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_execinfo_backtrace" >&5 +$as_echo "$gdb_cv_execinfo_backtrace" >&6; } +if test "$gdb_cv_execinfo_backtrace" = yes; then + +$as_echo "#define HAVE_EXECINFO_BACKTRACE 1" >>confdefs.h + +fi + if test "${build}" = "${host}" -a "${host}" = "${target}" ; then case ${host_os} in diff --git a/gdb/configure.ac b/gdb/configure.ac index b8c79bc..93f1131 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1291,6 +1291,7 @@ AC_CHECK_HEADERS(term.h, [], [], AC_CHECK_HEADERS([sys/socket.h]) AC_CHECK_HEADERS([ws2tcpip.h]) +AC_CHECK_HEADERS([execinfo.h]) # ------------------------- # # Checks for declarations. # @@ -1682,6 +1683,27 @@ fi AC_SUBST(RDYNAMIC) AC_MSG_RESULT($found) +AC_CACHE_CHECK( + [whether execinfo.h backtrace is available], + gdb_cv_execinfo_backtrace, + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [ + #include <execinfo.h> + ], + [ + int f; + void *b[[2]]; + f = backtrace (b, 2); + backtrace_symbols_fd (b, f, 2); + ])], + [gdb_cv_execinfo_backtrace=yes], + [gdb_cv_execinfo_backtrace=no])]) +if test "$gdb_cv_execinfo_backtrace" = yes; then + AC_DEFINE(HAVE_EXECINFO_BACKTRACE, 1, + [Define to 1 if execinfo.h backtrace functions are available.]) +fi + dnl For certain native configurations, we need to check whether thread dnl support can be built in or not. dnl diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f4ce3d3..e39f993 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -39716,6 +39716,24 @@ These are representative commands for each @var{kind} of setting type @value{GDBN} supports. They are used by the testsuite for exercising the settings infrastructure. +@kindex maint set backtrace-on-fatal-signal +@kindex maint show backtrace-on-fatal-signal +@item maint set backtrace-on-fatal-signal [on|off] +@itemx maint show backtrace-on-fatal-signal +When this setting is @code{on}, if @value{GDBN} itself terminates with +a fatal signal (e.g.@: SIGSEGV), then a limited backtrace will be +printed to stderr. This backtrace can be used to help diagnose +crashes within @value{GDBN} in situations where a user is unable to +share a corefile with the @value{GDBN} developers. + +If the functionality to provide this backtrace is not available for +the platform on which GDB is running then this feature will be +@code{off} by default, and attempting to turn this feature on will +give an error. + +For platforms that do support creating the backtrace this feature is +@code{on} by default. + @kindex maint with @item maint with @var{setting} [@var{value}] [-- @var{command}] Like the @code{with} command, but works with @code{maintenance set} diff --git a/gdb/event-top.c b/gdb/event-top.c index 972b540..210440a 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -46,6 +46,10 @@ #include "readline/readline.h" #include "readline/history.h" +#ifdef HAVE_EXECINFO_H +# include <execinfo.h> +#endif /* HAVE_EXECINFO_H */ + /* readline defines this. */ #undef savestring @@ -96,6 +100,38 @@ bool exec_done_display_p = false; run again. */ int call_stdin_event_handler_again_p; +/* When true GDB will produce a minimal backtrace when a fatal signal is + reached (within GDB code). */ +static bool bt_on_fatal_signal +#ifdef HAVE_EXECINFO_BACKTRACE + = true; +#else + = false; +#endif /* HAVE_EXECINFO_BACKTRACE */ + +/* Implement 'maintenance show backtrace-on-fatal-signal'. */ + +static void +show_bt_on_fatal_signal (struct ui_file *file, int from_tty, + struct cmd_list_element *cmd, const char *value) +{ + fprintf_filtered (file, _("Backtrace on a fatal signal is %s.\n"), value); +} + +/* Implement 'maintenance set backtrace-on-fatal-signal'. */ + +static void +set_bt_on_fatal_signal (const char *args, int from_tty, cmd_list_element *c) +{ +#ifndef HAVE_EXECINFO_BACKTRACE + if (bt_on_fatal_signal) + { + bt_on_fatal_signal = false; + error (_("support for this feature is not compiled into GDB")); + } +#endif +} + /* Signal handling variables. */ /* Each of these is a pointer to a function that the event loop will invoke if the corresponding signal has received. The real signal @@ -846,6 +882,84 @@ gdb_readline_no_editing_callback (gdb_client_data client_data) } +/* Attempt to unblock signal SIG, return true if the signal was unblocked, + otherwise, return false. */ + +static bool +unblock_signal (int sig) +{ +#if HAVE_SIGPROCMASK + sigset_t sigset; + sigemptyset (&sigset); + sigaddset (&sigset, sig); + gdb_sigmask (SIG_UNBLOCK, &sigset, 0); + return true; +#endif + + return false; +} + +/* Called to handle fatal signals. SIG is the signal number. */ + +static void ATTRIBUTE_NORETURN +handle_fatal_signal (int sig) +{ +#ifdef HAVE_EXECINFO_BACKTRACE + const auto sig_write = [] (const char *msg) -> void + { + gdb_stderr->write_async_safe (msg, strlen (msg)); + }; + + if (bt_on_fatal_signal) + { + sig_write ("\n\n"); + sig_write (_("Fatal signal: ")); + sig_write (strsignal (sig)); + sig_write ("\n"); + + /* Allow up to 25 frames of backtrace. */ + void *buffer[25]; + int frames = backtrace (buffer, ARRAY_SIZE (buffer)); + sig_write (_("----- Backtrace -----\n")); + if (gdb_stderr->fd () > -1) + { + backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ()); + if (frames == ARRAY_SIZE (buffer)) + sig_write (_("Backtrace might be incomplete.\n")); + } + else + sig_write (_("Backtrace unavailable\n")); + sig_write ("---------------------\n"); + sig_write (_("A fatal error internal to GDB has been detected, " + "further\ndebugging is not possible. GDB will now " + "terminate.\n\n")); + sig_write (_("This is a bug, please report it.")); + if (REPORT_BUGS_TO[0] != '\0') + { + sig_write (_(" For instructions, see:\n")); + sig_write (REPORT_BUGS_TO); + sig_write ("."); + } + sig_write ("\n\n"); + + gdb_stderr->flush (); + } +#endif /* HAVE_EXECINF_BACKTRACE */ + + /* If possible arrange for SIG to have its default behaviour (which + should be to terminate the current process), unblock SIG, and reraise + the signal. This ensures GDB terminates with the expected signal. */ + if (signal (sig, SIG_DFL) != SIG_ERR + && unblock_signal (sig)) + raise (sig); + + /* The above failed, so try to use SIGABRT to terminate GDB. */ +#ifdef SIGABRT + signal (SIGABRT, SIG_DFL); +#endif + abort (); /* ARI: abort */ +} + /* The SIGSEGV handler for this thread, or NULL if there is none. GDB always installs a global SIGSEGV handler, and then lets threads indicate their interest in handling the signal by setting this @@ -887,7 +1001,7 @@ handle_sigsegv (int sig) install_handle_sigsegv (); if (thread_local_segv_handler == nullptr) - abort (); /* ARI: abort */ + handle_fatal_signal (sig); thread_local_segv_handler (sig); } @@ -1160,16 +1274,7 @@ async_sigtstp_handler (gdb_client_data arg) char *prompt = get_prompt (); signal (SIGTSTP, SIG_DFL); -#if HAVE_SIGPROCMASK - { - sigset_t zero; - - sigemptyset (&zero); - gdb_sigmask (SIG_SETMASK, &zero, 0); - } -#elif HAVE_SIGSETMASK - sigsetmask (0); -#endif + unblock_signal (SIGTSTP); raise (SIGTSTP); signal (SIGTSTP, handle_sigtstp); printf_unfiltered ("%s", prompt); @@ -1320,4 +1425,17 @@ Control whether to show event loop-related debug messages."), set_debug_event_loop_command, show_debug_event_loop_command, &setdebuglist, &showdebuglist); + + add_setshow_boolean_cmd ("backtrace-on-fatal-signal", class_maintenance, + &bt_on_fatal_signal, _("\ +Set whether to produce a backtrace if GDB receives a fatal signal."), _("\ +Show whether GDB will produce a backtrace if it receives a fatal signal."), _("\ +Use \"on\" to enable, \"off\" to disable.\n\ +If enabled, GDB will produce a minimal backtrace if it encounters a fatal\n\ +signal from within GDB itself. This is a mechanism to help diagnose\n\ +crashes within GDB, not a mechanism for debugging inferiors."), + set_bt_on_fatal_signal, + show_bt_on_fatal_signal, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); } diff --git a/gdb/testsuite/gdb.base/bt-on-fatal-signal.c b/gdb/testsuite/gdb.base/bt-on-fatal-signal.c new file mode 100644 index 0000000..d9d56c3 --- /dev/null +++ b/gdb/testsuite/gdb.base/bt-on-fatal-signal.c @@ -0,0 +1,22 @@ +/* Copyright 2021 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp b/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp new file mode 100644 index 0000000..7a9f8e4 --- /dev/null +++ b/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp @@ -0,0 +1,134 @@ +# Copyright 2021 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test the 'maint set backtrace-on-fatal-signal' behaviour. Start up +# GDB, turn on backtrace-on-fatal-signal, then send fatal signals to +# GDB and ensure we see the backtrace. + +standard_testfile + +# The logic for sending signals to GDB might now work when using a +# remote host (will the signal go to GDB, or the program that +# established the connection to the remote host?), so just skip this +# test for remote host setups. +if {[is_remote host]} { + untested $testfile + return -1 +} + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} { + return -1 +} + +# Check we can run to main. If this works this time then we just +# assume that it will work later on (when we repeatedly restart GDB). +if ![runto_main] then { + untested $testfile + return -1 +} + +# Check that the backtrace-on-fatal-signal feature is supported. If +# this target doesn't have the backtrace function available then +# trying to turn this on will give an error, in which case we just +# skip this test. +gdb_test_multiple "maint set backtrace-on-fatal-signal on" "" { + -re "support for this feature is not compiled into GDB" { + untested $testfile + return -1 + } + -re "$gdb_prompt $" { + pass $gdb_test_name + } +} + +# Now the actual test loop. +foreach test_data {{SEGV "Segmentation fault"}} { + set sig [lindex ${test_data} 0] + set msg [lindex ${test_data} 1] + with_test_prefix ${sig} { + + # Restart GDB. + clean_restart $binfile + + # Capture the pid of GDB. + set testpid [spawn_id_get_pid $gdb_spawn_id] + + # Start the inferior. + runto_main + + # Turn on the backtrace-on-fatal-signal feature. + gdb_test_no_output "maint set backtrace-on-fatal-signal on" + + # Flags for various bits of the output we expect to see, we + # check for these in the gdb_test_multiple below. + set saw_fatal_msg false + set saw_bt_start false + set saw_bt_end false + set internal_error_msg_count 0 + + # Send the fatal signal to GDB. + remote_exec host "kill -${sig} ${testpid}" + + # Scan GDB's output for the backtrace. As the output we get + # here includes the standard "internal error" message, which + # gdb_test_multiple will usually handle, we are forced to make + # extensive use of the "-early" flag here so that all our + # patterns are applied before gdb_test_multiple can check for + # the internal error pattern. + gdb_test_multiple "" "scan for backtrace" { + -early -re "^\r\n" { + exp_continue + } + -early -re "^Fatal signal: ${msg}\r\n" { + set saw_fatal_msg true + exp_continue + } + -early -re "^----- Backtrace -----\r\n" { + set saw_bt_start true + exp_continue + } + -early -re ".+\r\n---------------------\r\n" { + set saw_bt_end true + exp_continue + } + -early -re "^A fatal error internal to GDB has been detected, further\r\n" { + incr internal_error_msg_count + exp_continue + } + -early -re "^debugging is not possible. GDB will now terminate\\.\r\n" { + incr internal_error_msg_count + exp_continue + } + eof { + # Catch the eof case as this indicates that GDB has + # gone away, which in this case, is what we expect to + # happen. + gdb_assert { $saw_fatal_msg } + gdb_assert { $saw_bt_start } + gdb_assert { $saw_bt_end } + gdb_assert { [expr $internal_error_msg_count == 2] } + } + -re "$gdb_prompt $" { + # GDB should terminate, we should never get back to + # the prompt. + fail $gdb_test_name + } + } + + # GDB should be dead and gone by this point, but just to be + # sure, force an exit. + gdb_exit + } +} diff --git a/gdb/ui-file.h b/gdb/ui-file.h index 6150973..9593c94 100644 --- a/gdb/ui-file.h +++ b/gdb/ui-file.h @@ -83,6 +83,11 @@ public: virtual void flush () {} + + /* If this object has an underlying file descriptor, then return it. + Otherwise, return -1. */ + virtual int fd () const + { return -1; } }; typedef std::unique_ptr<ui_file> ui_file_up; @@ -195,6 +200,10 @@ public: bool can_emit_style_escape () override; + /* Return the underlying file descriptor. */ + int fd () const override + { return m_fd; } + private: /* Sets the internal stream to FILE, and saves the FILE's file descriptor in M_FD. */ |