| /* Copyright (C) 2021-2024 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 "bt-utils.h" |
| #include "command.h" |
| #include "cli/cli-cmds.h" |
| #include "ui.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 |
| } |