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:
  <https://www.gnu.org/software/gdb/bugs/>.

  ../../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
This commit is contained in:
Andrew Burgess 2021-08-12 18:24:59 +01:00
parent abbbd4a3e0
commit 91f2597bd2
5 changed files with 174 additions and 37 deletions

View File

@ -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

View File

@ -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,

View File

@ -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 <http://www.gnu.org/licenses/>.
# 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}
}
}

View File

@ -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] }
}
}
}

View File

@ -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