diff options
author | Andrew Burgess <andrew.burgess@embecosm.com> | 2021-06-10 16:57:24 +0100 |
---|---|---|
committer | Andrew Burgess <andrew.burgess@embecosm.com> | 2021-08-11 12:35:14 +0100 |
commit | 6aa4f97c2b8a3fe3775d90c7485f4ace610fb103 (patch) | |
tree | 095dbbd8f4f9cde32549e2f8d38ab919199684c6 /gdb/testsuite | |
parent | 270135645b50a2fb8a4dac216584e8056167ffcc (diff) | |
download | gdb-6aa4f97c2b8a3fe3775d90c7485f4ace610fb103.zip gdb-6aa4f97c2b8a3fe3775d90c7485f4ace610fb103.tar.gz gdb-6aa4f97c2b8a3fe3775d90c7485f4ace610fb103.tar.bz2 |
gdb: print backtrace on fatal SIGSEGV
This commit adds a new maintenance feature, the ability to print
a (limited) backtrace if GDB dies due to a fatal signal.
The backtrace is produced using the backtrace and backtrace_symbols_fd
functions which are declared in the execinfo.h header, and both of
which are async signal safe. A configure check has been added to
check for these features, if they are not available then the new code
is not compiled into GDB and the backtrace will not be printed.
The motivation for this new feature is to aid in debugging GDB in
situations where GDB has crashed at a users site, but the user is
reluctant to share core files, possibly due to concerns about what
might be in the memory image within the core file. Such a user might
be happy to share a simple backtrace that was written to stderr.
The production of the backtrace is on by default, but can switched off
using the new commands:
maintenance set backtrace-on-fatal-signal on|off
maintenance show backtrace-on-fatal-signal
Right now, I have hooked this feature in to GDB's existing handling of
SIGSEGV only, but this will be extended to more signals in a later
commit.
One additional change I have made in this commit is that, when we
decide GDB should terminate due to the fatal signal, we now
raise the same fatal signal rather than raising SIGABRT.
Currently, this is only effecting our handling of SIGSEGV. So,
previously, if GDB hit a SEGV then we would terminate GDB with a
SIGABRT. After this commit we will terminate GDB with a SIGSEGV.
This feels like an improvement to me, we should still get a core dump,
but in many shells, the user will see a more specific message once GDB
exits, in bash for example "Segmentation fault" rather than "Aborted".
Finally then, here is an example of the output a user would see if GDB
should hit an internal SIGSEGV:
Fatal signal: Segmentation fault
----- Backtrace -----
./gdb/gdb[0x8078e6]
./gdb/gdb[0x807b20]
/lib64/libpthread.so.0(+0x14b20)[0x7f6648c92b20]
/lib64/libc.so.6(__poll+0x4f)[0x7f66484d3a5f]
./gdb/gdb[0x1540f4c]
./gdb/gdb[0x154034a]
./gdb/gdb[0x9b002d]
./gdb/gdb[0x9b014d]
./gdb/gdb[0x9b1aa6]
./gdb/gdb[0x9b1b0c]
./gdb/gdb[0x41756d]
/lib64/libc.so.6(__libc_start_main+0xf3)[0x7f66484041a3]
./gdb/gdb[0x41746e]
---------------------
A fatal error internal to GDB has been detected, further
debugging is not possible. GDB will now terminate.
This is a bug, please report it. For instructions, see:
<https://www.gnu.org/software/gdb/bugs/>.
Segmentation fault (core dumped)
It is disappointing that backtrace_symbols_fd does not actually map
the addresses back to symbols, this appears, in part, to be due to GDB
not being built with -rdynamic as the manual page for
backtrace_symbols_fd suggests, however, even when I do add -rdynamic
to the build of GDB I only see symbols for some addresses.
We could potentially look at alternative libraries to provide the
backtrace (e.g. libunwind) however, the solution presented here, which
is available as part of glibc is probably a good baseline from which
we might improve things in future.
Diffstat (limited to 'gdb/testsuite')
-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 |
2 files changed, 156 insertions, 0 deletions
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 + } +} |