From 91f2597bd24d171c1337a4629f8237aa47c59082 Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Thu, 12 Aug 2021 18:24:59 +0100 Subject: [PATCH] gdb: print backtrace for internal error/warning This commit builds on previous work to allow GDB to print a backtrace of itself when GDB encounters an internal-error or internal-warning. This fixes PR gdb/26377. There's not many places where we call internal_warning, and I guess in most cases the user would probably continue their debug session. And so, in order to avoid cluttering up the output, by default, printing of a backtrace is off for internal-warnings. In contrast, printing of a backtrace is on by default for internal-errors, as I figure that in most cases hitting an internal-error is going to be the end of the debug session. Whether a backtrace is printed or not can be controlled with the new settings: maintenance set internal-error backtrace on|off maintenance show internal-error backtrace maintenance set internal-warning backtrace on|off maintenance show internal-warning backtrace Here is an example of what an internal-error now looks like with the backtrace included: (gdb) maintenance internal-error blah ../../src.dev-3/gdb/maint.c:82: internal-error: blah A problem internal to GDB has been detected, further debugging may prove unreliable. ----- Backtrace ----- 0x5c61ca gdb_internal_backtrace_1 ../../src.dev-3/gdb/bt-utils.c:123 0x5c626d _Z22gdb_internal_backtracev ../../src.dev-3/gdb/bt-utils.c:165 0xe33237 internal_vproblem ../../src.dev-3/gdb/utils.c:393 0xe33539 _Z15internal_verrorPKciS0_P13__va_list_tag ../../src.dev-3/gdb/utils.c:470 0x1549652 _Z14internal_errorPKciS0_z ../../src.dev-3/gdbsupport/errors.cc:55 0x9c7982 maintenance_internal_error ../../src.dev-3/gdb/maint.c:82 0x636f57 do_simple_func ../../src.dev-3/gdb/cli/cli-decode.c:97 .... snip, lots more backtrace lines .... --------------------- ../../src.dev-3/gdb/maint.c:82: internal-error: blah A problem internal to GDB has been detected, further debugging may prove unreliable. Quit this debugging session? (y or n) y This is a bug, please report it. For instructions, see: . ../../src.dev-3/gdb/maint.c:82: internal-error: blah A problem internal to GDB has been detected, further debugging may prove unreliable. Create a core file of GDB? (y or n) n My hope is that this backtrace might make it slightly easier to diagnose GDB issues if all that is provided is the console output, I find that we frequently get reports of an assert being hit that is located in pretty generic code (frame.c, value.c, etc) and it is not always obvious how we might have arrived at the assert. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=26377 --- gdb/NEWS | 8 ++ gdb/doc/gdb.texinfo | 13 ++ .../gdb.base/bt-on-error-and-warning.exp | 118 ++++++++++++++++++ gdb/testsuite/gdb.base/bt-on-fatal-signal.exp | 36 ------ gdb/utils.c | 36 +++++- 5 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 gdb/testsuite/gdb.base/bt-on-error-and-warning.exp diff --git a/gdb/NEWS b/gdb/NEWS index d7c29c82edb..1e25cb8cc36 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -19,6 +19,14 @@ show source open to open and read source code files, which can be useful if the files are located over a slow network connection. +maint set internal-error backtrace on|off +maint show internal-error backtrace +maint set internal-warning backtrace on|off +maint show internal-warning backtrace + GDB can now print a backtrace of itself when it encounters either an + internal-error, or an internal-warning. This is on by default for + internal-error and off by default for internal-warning. + * Python API ** New function gdb.add_history(), which takes a gdb.Value object diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index fcfdc26ac1a..d4e4174be5d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -39257,6 +39257,19 @@ demangler warnings always create a core file and this cannot be disabled. @end table +@kindex maint set internal-error +@kindex maint show internal-error +@kindex maint set internal-warning +@kindex maint show internal-warning +@item maint set internal-error backtrace @r{[}on|off@r{]} +@itemx maint show internal-error backtrace +@itemx maint set internal-warning backtrace @r{[}on|off@r{]} +@itemx maint show internal-warning backtrace +When @value{GDBN} reports an internal problem (error or warning) it is +possible to have a backtrace of @value{GDBN} printed to stderr. This +is @samp{on} by default for @code{internal-error} and @samp{off} by +default for @code{internal-warning}. + @kindex maint packet @item maint packet @var{text} If @value{GDBN} is talking to an inferior via the serial protocol, diff --git a/gdb/testsuite/gdb.base/bt-on-error-and-warning.exp b/gdb/testsuite/gdb.base/bt-on-error-and-warning.exp new file mode 100644 index 00000000000..d988cf742b4 --- /dev/null +++ b/gdb/testsuite/gdb.base/bt-on-error-and-warning.exp @@ -0,0 +1,118 @@ +# 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 . + +# Test that GDB can print a backtrace when it encounters an internal +# error or an internal warning, and that this functionality can be +# switched off. + +standard_testfile bt-on-fatal-signal.c + +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 "run to main" + 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 internal-error backtrace on" \ + "check backtrace is supported" { + -re "support for this feature is not compiled into GDB" { + untested "feature not supported" + return -1 + } + -re "$gdb_prompt $" { + pass $gdb_test_name + } +} + +# MODE should be either 'on' or 'off', while PROBLEM_TYPE should be +# 'internal-error' or 'internal-warning'. This proc sets the +# backtrace printing for PROBLEM_TYPE to MODE, then uses 'maint +# PROBLEM_TYPE foobar' to raise a fake error or warning. +# +# We then check that a backtrace either is, or isn't printed, inline +# with MODE. +proc run_test {problem_type mode} { + + with_test_prefix "problem=${problem_type}, mode=${mode}" { + gdb_test_no_output "maint set ${problem_type} backtrace ${mode}" + + set header_lines 0 + set bt_lines 0 + + gdb_test_multiple "maint ${problem_type} foobar" "scan for backtrace" { + -early -re "^\r\n" { + exp_continue + } + -early -re "^maint ${problem_type} foobar\r\n" { + exp_continue + } + -early -re "^\[^\r\n\]+: ${problem_type}: foobar\r\n" { + incr header_lines + exp_continue + } + -early -re "^A problem internal to GDB has been detected,\r\n" { + incr header_lines + exp_continue + } + -early -re "^further debugging may prove unreliable\\.\r\n" { + incr header_lines + exp_continue + } + -early -re "^----- Backtrace -----\r\n" { + incr bt_lines + exp_continue + } + -early -re "^\[^-\].+\r\n---------------------\r\n" { + incr bt_lines + exp_continue + } + eof { + fail ${gdb_test_name} + return + } + -re "$::gdb_prompt $" { + pass ${gdb_test_name} + } + } + + gdb_assert { ${header_lines} == 3 } + if { $mode == "on" } { + gdb_assert { ${bt_lines} == 2 } + } else { + gdb_assert { ${bt_lines} == 0 } + } + } +} + +# For each problem type (error or warning) raise a fake problem using +# the maintenance commands and check that a backtrace is (or isn't) +# printed, depending on the user setting. +foreach problem_type { internal-error internal-warning } { + gdb_test_no_output "maint set ${problem_type} corefile no" + gdb_test_no_output "maint set ${problem_type} quit no" + + foreach mode { on off } { + run_test ${problem_type} ${mode} + } +} diff --git a/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp b/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp index 1f0d61f00ed..8875d00fdb1 100644 --- a/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp +++ b/gdb/testsuite/gdb.base/bt-on-fatal-signal.exp @@ -135,39 +135,3 @@ foreach test_data {{SEGV "Segmentation fault"} \ gdb_exit } } - -# Check that when we get an internal error and choose to dump core, we -# don't print a backtrace to the console. -with_test_prefix "internal-error" { - # Restart GDB. - clean_restart $binfile - - set saw_bt_start false - - gdb_test_multiple "maint internal-error foo" "" { - -early -re "internal-error: foo\r\n" { - exp_continue - } - -early -re "^A problem internal to GDB has been detected,\r\n" { - exp_continue - } - -early -re "^further debugging may prove unreliable\\.\r\n" { - exp_continue - } - -early -re "^Quit this debugging session\\? \\(y or n\\)" { - send_gdb "y\n" - exp_continue - } - -early -re "^Create a core file of GDB\\? \\(y or n\\)" { - send_gdb "y\n" - exp_continue - } - -early -re "----- Backtrace -----\r\n" { - set saw_bt_start true - exp_continue - } - eof { - gdb_assert { [expr ! $saw_bt_start] } - } - } -} diff --git a/gdb/utils.c b/gdb/utils.c index 0a7c270b40d..f6f90d7365b 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -75,6 +75,7 @@ #include "gdbarch.h" #include "cli-out.h" #include "gdbsupport/gdb-safe-ctype.h" +#include "bt-utils.h" void (*deprecated_error_begin_hook) (void); @@ -304,6 +305,13 @@ struct internal_problem /* Like SHOULD_QUIT, but whether GDB should dump core. */ const char *should_dump_core; + + /* Like USER_SETTABLE_SHOULD_QUIT but for SHOULD_PRINT_BACKTRACE. */ + bool user_settable_should_print_backtrace; + + /* When this is true GDB will print a backtrace when a problem of this + type is encountered. */ + bool should_print_backtrace; }; /* Report a problem, internal to GDB, to the user. Once the problem @@ -377,9 +385,13 @@ internal_vproblem (struct internal_problem *problem, /* Emit the message unless query will emit it below. */ if (problem->should_quit != internal_problem_ask || !confirm - || !filtered_printing_initialized ()) + || !filtered_printing_initialized () + || problem->should_print_backtrace) fprintf_unfiltered (gdb_stderr, "%s\n", reason.c_str ()); + if (problem->should_print_backtrace) + gdb_internal_backtrace (); + if (problem->should_quit == internal_problem_ask) { /* Default (yes/batch case) is to quit GDB. When in batch mode @@ -449,6 +461,7 @@ internal_vproblem (struct internal_problem *problem, static struct internal_problem internal_error_problem = { "internal-error", true, internal_problem_ask, true, internal_problem_ask, + true, GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON }; void @@ -460,6 +473,7 @@ internal_verror (const char *file, int line, const char *fmt, va_list ap) static struct internal_problem internal_warning_problem = { "internal-warning", true, internal_problem_ask, true, internal_problem_ask, + true, false }; void @@ -470,6 +484,7 @@ internal_vwarning (const char *file, int line, const char *fmt, va_list ap) static struct internal_problem demangler_warning_problem = { "demangler-warning", true, internal_problem_ask, false, internal_problem_no, + false, false }; void @@ -571,6 +586,25 @@ add_internal_problem_command (struct internal_problem *problem) set_cmd_list, show_cmd_list); } + + if (problem->user_settable_should_print_backtrace) + { + std::string set_bt_doc + = string_printf (_("Set whether GDB should print a backtrace of " + "GDB when %s is detected."), problem->name); + std::string show_bt_doc + = string_printf (_("Show whether GDB will print a backtrace of " + "GDB when %s is detected."), problem->name); + add_setshow_boolean_cmd ("backtrace", class_maintenance, + &problem->should_print_backtrace, + set_bt_doc.c_str (), + show_bt_doc.c_str (), + NULL, /* help_doc */ + gdb_internal_backtrace_set_cmd, + NULL, /* showfunc */ + set_cmd_list, + show_cmd_list); + } } /* Return a newly allocated string, containing the PREFIX followed -- 2.30.2