binutils-gdb/gdb/bt-utils.c
Andrew Burgess abbbd4a3e0 gdb: use libbacktrace to create a better backtrace for fatal signals
GDB recently gained the ability to print a backtrace when a fatal
signal is encountered.  This backtrace is produced using the backtrace
and backtrace_symbols_fd API available in glibc.

However, in order for this API to actually map addresses to symbol
names it is required that the application (GDB) be compiled with
-rdynamic, which GDB is not by default.

As a result, the backtrace produced often looks like this:

  Fatal signal: Bus error
  ----- Backtrace -----
  ./gdb/gdb[0x80ec00]
  ./gdb/gdb[0x80ed56]
  /lib64/libc.so.6(+0x3c6b0)[0x7fc2ce1936b0]
  /lib64/libc.so.6(__poll+0x4f)[0x7fc2ce24da5f]
  ./gdb/gdb[0x15495ba]
  ./gdb/gdb[0x15489b8]
  ./gdb/gdb[0x9b794d]
  ./gdb/gdb[0x9b7a6d]
  ./gdb/gdb[0x9b943b]
  ./gdb/gdb[0x9b94a1]
  ./gdb/gdb[0x4175dd]
  /lib64/libc.so.6(__libc_start_main+0xf3)[0x7fc2ce17e1a3]
  ./gdb/gdb[0x4174de]
  ---------------------

This is OK if you have access to the exact same build of GDB, you can
manually map the addresses back to symbols, however, it is next to
useless if all you have is a backtrace copied into a bug report.

GCC uses libbacktrace for printing a backtrace when it encounters an
error.  In recent commits I added this library into the binutils-gdb
repository, and in this commit I allow this library to be used by
GDB.  Now (when GDB is compiled with debug information) the backtrace
looks like this:

  ----- Backtrace -----
  0x80ee08 gdb_internal_backtrace
  	../../src/gdb/event-top.c:989
  0x80ef0b handle_fatal_signal
  	../../src/gdb/event-top.c:1036
  0x7f24539dd6af ???
  0x7f2453a97a5f ???
  0x154976f gdb_wait_for_event
  	../../src/gdbsupport/event-loop.cc:613
  0x1548b6d _Z16gdb_do_one_eventv
  	../../src/gdbsupport/event-loop.cc:237
  0x9b7b02 start_event_loop
  	../../src/gdb/main.c:421
  0x9b7c22 captured_command_loop
  	../../src/gdb/main.c:481
  0x9b95f0 captured_main
  	../../src/gdb/main.c:1353
  0x9b9656 _Z8gdb_mainP18captured_main_args
  	../../src/gdb/main.c:1368
  0x4175ec main
  	../../src/gdb/gdb.c:32
  ---------------------

Which seems much more useful.

Use of libbacktrace is optional.  If GDB is configured with
--disable-libbacktrace then the libbacktrace directory will not be
built, and GDB will not try to use this library.  In this case GDB
would try to use the old backtrace and backtrace_symbols_fd API.

All of the functions related to writing the backtrace of GDB itself
have been moved into the new files gdb/by-utils.{c,h}.
2021-09-28 12:21:22 +01:00

170 lines
4.5 KiB
C

/* 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 <http://www.gnu.org/licenses/>. */
#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");
}