diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 320d3326a81..5a3bb952279 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -248,6 +248,10 @@ GDBFLAGS = GNULIB_PARENT_DIR = .. include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc +# For libbacktrace. +LIBBACKTRACE_INC=@LIBBACKTRACE_INC@ +LIBBACKTRACE_LIB=@LIBBACKTRACE_LIB@ + SUPPORT = ../gdbsupport LIBSUPPORT = $(SUPPORT)/libgdbsupport.a INCSUPPORT = -I$(srcdir)/.. -I.. @@ -614,9 +618,9 @@ INTERNAL_CFLAGS_BASE = \ $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \ $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) $(ZLIBINC) \ $(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \ - $(INTL_CFLAGS) $(INCGNU) $(INCSUPPORT) $(ENABLE_CFLAGS) \ - $(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) $(TOP_CFLAGS) $(PTHREAD_CFLAGS) \ - $(DEBUGINFOD_CFLAGS) + $(INTL_CFLAGS) $(INCGNU) $(INCSUPPORT) $(LIBBACKTRACE_INC) \ + $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) \ + $(TOP_CFLAGS) $(PTHREAD_CFLAGS) $(DEBUGINFOD_CFLAGS) INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS) INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS) @@ -637,12 +641,12 @@ INTERNAL_LDFLAGS = \ # LIBIBERTY appears twice on purpose. CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \ $(LIBSUPPORT) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \ - $(XM_CLIBS) $(GDBTKLIBS) \ + $(XM_CLIBS) $(GDBTKLIBS) $(LIBBACKTRACE_LIB) \ @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \ $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \ $(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \ $(LIBMPFR) $(LIBGMP) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \ - $(DEBUGINFOD_LIBS) + $(DEBUGINFOD_LIBS) $(LIBBABELTRACE_LIB) CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \ $(LIBSUPPORT) @@ -995,6 +999,7 @@ COMMON_SFILES = \ break-catch-syscall.c \ break-catch-throw.c \ breakpoint.c \ + bt-utils.c \ btrace.c \ build-id.c \ buildsym-legacy.c \ @@ -1256,6 +1261,7 @@ HFILES_NO_SRCDIR = \ breakpoint.h \ bsd-kvm.h \ bsd-uthread.h \ + bt-utils.h \ build-id.h \ buildsym-legacy.h \ buildsym.h \ diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c new file mode 100644 index 00000000000..b5e0a0ed004 --- /dev/null +++ b/gdb/bt-utils.c @@ -0,0 +1,170 @@ +/* Copyright (C) 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 . */ + +#include "defs.h" +#include "bt-utils.h" +#include "command.h" +#include "gdbcmd.h" +#include "top.h" +#include "cli/cli-decode.h" + +/* See bt-utils.h. */ + +void +gdb_internal_backtrace_set_cmd (const char *args, int from_tty, + cmd_list_element *c) +{ + gdb_assert (c->type == set_cmd); + gdb_assert (c->var_type == var_boolean); + gdb_assert (c->var != nullptr); + +#ifndef GDB_PRINT_INTERNAL_BACKTRACE + bool *var_ptr = (bool *) c->var; + + if (*var_ptr) + { + *var_ptr = false; + error (_("support for this feature is not compiled into GDB")); + } +#endif +} + +#ifdef GDB_PRINT_INTERNAL_BACKTRACE_USING_LIBBACKTRACE + +/* Callback used by libbacktrace if it encounters an error. */ + +static void +libbacktrace_error (void *data, const char *errmsg, int errnum) +{ + /* A negative errnum indicates no debug info was available, just + skip printing a backtrace in this case. */ + if (errnum < 0) + return; + + const auto sig_write = [] (const char *msg) -> void + { + gdb_stderr->write_async_safe (msg, strlen (msg)); + }; + + sig_write ("error creating backtrace: "); + sig_write (errmsg); + if (errnum > 0) + { + char buf[20]; + snprintf (buf, sizeof (buf), ": %d", errnum); + buf[sizeof (buf) - 1] = '\0'; + + sig_write (buf); + } + sig_write ("\n"); +} + +/* Callback used by libbacktrace to print a single stack frame. */ + +static int +libbacktrace_print (void *data, uintptr_t pc, const char *filename, + int lineno, const char *function) +{ + const auto sig_write = [] (const char *msg) -> void + { + gdb_stderr->write_async_safe (msg, strlen (msg)); + }; + + /* Buffer to print addresses and line numbers into. An 8-byte address + with '0x' prefix and a null terminator requires 20 characters. This + also feels like it should be enough to represent line numbers in most + files. We are also careful to ensure we don't overflow this buffer. */ + char buf[20]; + + snprintf (buf, sizeof (buf), "0x%lx ", pc); + buf[sizeof (buf) - 1] = '\0'; + sig_write (buf); + sig_write (function == nullptr ? "???" : function); + if (filename != nullptr) + { + sig_write ("\n\t"); + sig_write (filename); + sig_write (":"); + snprintf (buf, sizeof (buf), "%d", lineno); + buf[sizeof (buf) - 1] = '\0'; + sig_write (buf); + } + sig_write ("\n"); + + return function != nullptr && strcmp (function, "main") == 0; +} + +/* Write a backtrace to GDB's stderr in an async safe manor. This is a + backtrace of GDB, not any running inferior, and is to be used when GDB + crashes or hits some other error condition. */ + +static void +gdb_internal_backtrace_1 () +{ + static struct backtrace_state *state = nullptr; + + if (state == nullptr) + state = backtrace_create_state (nullptr, 0, libbacktrace_error, nullptr); + + backtrace_full (state, 0, libbacktrace_print, libbacktrace_error, nullptr); +} + +#elif defined GDB_PRINT_INTERNAL_BACKTRACE_USING_EXECINFO + +/* See the comment on previous version of this function. */ + +static void +gdb_internal_backtrace_1 () +{ + const auto sig_write = [] (const char *msg) -> void + { + gdb_stderr->write_async_safe (msg, strlen (msg)); + }; + + /* Allow up to 25 frames of backtrace. */ + void *buffer[25]; + int frames = backtrace (buffer, ARRAY_SIZE (buffer)); + + backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ()); + if (frames == ARRAY_SIZE (buffer)) + sig_write (_("Backtrace might be incomplete.\n")); +} + +#endif + +/* See bt-utils.h. */ + +void +gdb_internal_backtrace () +{ + if (current_ui == nullptr) + return; + + const auto sig_write = [] (const char *msg) -> void + { + gdb_stderr->write_async_safe (msg, strlen (msg)); + }; + + sig_write (_("----- Backtrace -----\n")); + + if (gdb_stderr->fd () > -1) + gdb_internal_backtrace_1 (); + else + sig_write (_("Backtrace unavailable\n")); + + sig_write ("---------------------\n"); +} diff --git a/gdb/bt-utils.h b/gdb/bt-utils.h new file mode 100644 index 00000000000..433aa23614b --- /dev/null +++ b/gdb/bt-utils.h @@ -0,0 +1,69 @@ +/* Copyright (C) 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 . */ + +/* Support for printing a backtrace when GDB hits an error. This is not + for printing backtraces of the inferior, but backtraces of GDB itself. */ + +#ifdef HAVE_LIBBACKTRACE +# include "backtrace.h" +# include "backtrace-supported.h" +# if BACKTRACE_SUPPORTED && (! BACKTRACE_USES_MALLOC) +# define GDB_PRINT_INTERNAL_BACKTRACE +# define GDB_PRINT_INTERNAL_BACKTRACE_USING_LIBBACKTRACE +# endif +#endif + +#if defined HAVE_EXECINFO_H \ + && defined HAVE_EXECINFO_BACKTRACE \ + && !defined PRINT_BACKTRACE_ON_FATAL_SIGNAL +# include +# define GDB_PRINT_INTERNAL_BACKTRACE +# define GDB_PRINT_INTERNAL_BACKTRACE_USING_EXECINFO +#endif + +/* Define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON. This is a boolean value + that can be used as an initial value for a set/show user setting, where + the setting controls printing a GDB internal backtrace. + + If backtrace printing is supported then this will have the value true, + but if backtrace printing is not supported then this has the value + false. */ +#ifdef GDB_PRINT_INTERNAL_BACKTRACE +# define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON true +#else +# define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON false +#endif + +/* Print a backtrace of the current GDB process to the current + gdb_stderr. The output is done in a signal async manor, so it is safe + to call from signal handlers. */ + +extern void gdb_internal_backtrace (); + +/* A generic function that can be used as the set function for any set + command that enables printing of an internal backtrace. Command C must + be a boolean set command. + + If GDB doesn't support printing a backtrace, and the user has tried to + set the variable associated with command C to true, then the associated + variable will be set back to false, and an error thrown. + + If GDB does support printing a backtrace then this function does + nothing. */ + +extern void gdb_internal_backtrace_set_cmd (const char *args, int from_tty, + cmd_list_element *c); diff --git a/gdb/config.in b/gdb/config.in index af3680c6d95..c61f7a91352 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -228,6 +228,9 @@ /* Define if you have the babeltrace library. */ #undef HAVE_LIBBABELTRACE +/* Define if libbacktrace is being used. */ +#undef HAVE_LIBBACKTRACE + /* Define to 1 if debuginfod is enabled. */ #undef HAVE_LIBDEBUGINFOD diff --git a/gdb/configure b/gdb/configure index f0b1af4a6ea..7c8335f2576 100755 --- a/gdb/configure +++ b/gdb/configure @@ -636,6 +636,8 @@ LIBCTF LTLIBBABELTRACE LIBBABELTRACE HAVE_LIBBABELTRACE +LIBBACKTRACE_LIB +LIBBACKTRACE_INC HAVE_NATIVE_GCORE_HOST NAT_GENERATED_FILES XM_CLIBS @@ -925,6 +927,7 @@ with_tcl with_tk with_x enable_sim +enable_libbacktrace with_babeltrace with_libbabeltrace_prefix with_libbabeltrace_type @@ -1601,6 +1604,8 @@ Optional Features: gcc is used --enable-ubsan enable undefined behavior sanitizer (auto/yes/no) --enable-sim link gdb with simulator + --enable-libbacktrace use libbacktrace to write a backtrace after a fatal + signal. --enable-libctf Handle .ctf type-info sections [default=yes] --enable-unit-tests Enable the inclusion of unit tests when compiling GDB @@ -18659,6 +18664,33 @@ _ACEOF fi +# Setup possible use of libbacktrace. +# Check whether --enable-libbacktrace was given. +if test "${enable_libbacktrace+set}" = set; then : + enableval=$enable_libbacktrace; case "${enableval}" in + yes) enable_libbacktrace=yes ;; + no) enable_libbacktrace=no ;; + *) as_fn_error $? "bad value ${enableval} for --enable-libbacktrace option" "$LINENO" 5 ;; +esac +else + enable_libbacktrace=yes +fi + + +if test "${enable_libbacktrace}" == "yes"; then + LIBBACKTRACE_INC="-I$srcdir/../libbacktrace/ -I../libbacktrace/" + LIBBACKTRACE_LIB=../libbacktrace/.libs/libbacktrace.a + +$as_echo "#define HAVE_LIBBACKTRACE 1" >>confdefs.h + +else + LIBBACKTRACE_INC= + LIBBACKTRACE_LIB= +fi + + + + # Check for babeltrace and babeltrace-ctf # Check whether --with-babeltrace was given. diff --git a/gdb/configure.ac b/gdb/configure.ac index 93f11316a14..0d91be59cd6 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -2131,6 +2131,29 @@ if test x"${gdb_osabi}" != x ; then [Define to the default OS ABI for this configuration.]) fi +# Setup possible use of libbacktrace. +AC_ARG_ENABLE([libbacktrace], +[AS_HELP_STRING([--enable-libbacktrace], + [use libbacktrace to write a backtrace after a fatal signal.])], +[case "${enableval}" in + yes) enable_libbacktrace=yes ;; + no) enable_libbacktrace=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-libbacktrace option) ;; +esac], +enable_libbacktrace=yes) + +if test "${enable_libbacktrace}" == "yes"; then + LIBBACKTRACE_INC="-I$srcdir/../libbacktrace/ -I../libbacktrace/" + LIBBACKTRACE_LIB=../libbacktrace/.libs/libbacktrace.a + AC_DEFINE(HAVE_LIBBACKTRACE, 1, [Define if libbacktrace is being used.]) +else + LIBBACKTRACE_INC= + LIBBACKTRACE_LIB= +fi + +AC_SUBST(LIBBACKTRACE_INC) +AC_SUBST(LIBBACKTRACE_LIB) + # Check for babeltrace and babeltrace-ctf AC_ARG_WITH(babeltrace, AS_HELP_STRING([--with-babeltrace], [include babeltrace support (auto/yes/no)]), diff --git a/gdb/event-top.c b/gdb/event-top.c index 9233a3650ac..64c624ce4d7 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -41,15 +41,12 @@ #include "gdbsupport/gdb_select.h" #include "gdbsupport/gdb-sigmask.h" #include "async-event.h" +#include "bt-utils.h" /* readline include files. */ #include "readline/readline.h" #include "readline/history.h" -#ifdef HAVE_EXECINFO_H -# include -#endif /* HAVE_EXECINFO_H */ - /* readline defines this. */ #undef savestring @@ -102,12 +99,7 @@ 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 */ +static bool bt_on_fatal_signal = GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON; /* Implement 'maintenance show backtrace-on-fatal-signal'. */ @@ -118,20 +110,6 @@ show_bt_on_fatal_signal (struct ui_file *file, int from_tty, 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 @@ -904,7 +882,7 @@ unblock_signal (int sig) static void ATTRIBUTE_NORETURN handle_fatal_signal (int sig) { -#ifdef HAVE_EXECINFO_BACKTRACE +#ifdef GDB_PRINT_INTERNAL_BACKTRACE const auto sig_write = [] (const char *msg) -> void { gdb_stderr->write_async_safe (msg, strlen (msg)); @@ -917,19 +895,8 @@ handle_fatal_signal (int sig) 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"); + gdb_internal_backtrace (); + sig_write (_("A fatal error internal to GDB has been detected, " "further\ndebugging is not possible. GDB will now " "terminate.\n\n")); @@ -944,7 +911,7 @@ handle_fatal_signal (int sig) gdb_stderr->flush (); } -#endif /* HAVE_EXECINF_BACKTRACE */ +#endif /* If possible arrange for SIG to have its default behaviour (which should be to terminate the current process), unblock SIG, and reraise @@ -1449,7 +1416,7 @@ 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, + gdb_internal_backtrace_set_cmd, show_bt_on_fatal_signal, &maintenance_set_cmdlist, &maintenance_show_cmdlist);