| /* Copyright (C) 2021-2022 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.has_value ()); | |
| gdb_assert (c->var->type () == var_boolean); | |
| #ifndef GDB_PRINT_INTERNAL_BACKTRACE | |
| if (c->var->get<bool> ()) | |
| { | |
| c->var->set<bool> (false); | |
| error (_("support for this feature is not compiled into GDB")); | |
| } | |
| #endif | |
| } | |
| #ifdef GDB_PRINT_INTERNAL_BACKTRACE | |
| #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%" PRIxPTR " ", 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 manner. 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")); | |
| } | |
| #else | |
| #error "unexpected internal backtrace policy" | |
| #endif | |
| #endif /* GDB_PRINT_INTERNAL_BACKTRACE */ | |
| /* See bt-utils.h. */ | |
| void | |
| gdb_internal_backtrace () | |
| { | |
| if (current_ui == nullptr) | |
| return; | |
| #ifdef GDB_PRINT_INTERNAL_BACKTRACE | |
| 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"); | |
| #endif | |
| } |