aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS7
-rw-r--r--gdb/config.in6
-rwxr-xr-xgdb/configure51
-rw-r--r--gdb/configure.ac22
-rw-r--r--gdb/doc/gdb.texinfo18
-rw-r--r--gdb/event-top.c140
-rw-r--r--gdb/testsuite/gdb.base/bt-on-fatal-signal.c22
-rw-r--r--gdb/testsuite/gdb.base/bt-on-fatal-signal.exp134
-rw-r--r--gdb/ui-file.h9
9 files changed, 398 insertions, 11 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index 51b276c..ec3058e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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. */