/* Top level stuff for GDB, the GNU debugger.
- Copyright (C) 1999-2021 Free Software Foundation, Inc.
+ Copyright (C) 1999-2022 Free Software Foundation, Inc.
Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions.
#include "gdbsupport/gdb_select.h"
#include "gdbsupport/gdb-sigmask.h"
#include "async-event.h"
+#include "bt-utils.h"
/* readline include files. */
#include "readline/readline.h"
run again. */
int call_stdin_event_handler_again_p;
+/* When true GDB will produce a minimal backtrace when a fatal signal is
+ reached (within GDB code). */
+static bool bt_on_fatal_signal = GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON;
+
+/* Implement 'maintenance show backtrace-on-fatal-signal'. */
+
+static void
+show_bt_on_fatal_signal (struct ui_file *file, int from_tty,
+ struct cmd_list_element *cmd, const char *value)
+{
+ fprintf_filtered (file, _("Backtrace on a fatal signal is %s.\n"), value);
+}
+
/* Signal handling variables. */
/* Each of these is a pointer to a function that the event loop will
invoke if the corresponding signal has received. The real signal
is typing would lose input. */
/* Whether we've registered a callback handler with readline. */
-static int callback_handler_installed;
+static bool callback_handler_installed;
/* See event-top.h, and above. */
gdb_assert (current_ui == main_ui);
rl_callback_handler_remove ();
- callback_handler_installed = 0;
+ callback_handler_installed = false;
}
/* See event-top.h, and above. Note this wrapper doesn't have an
gdb_assert (!callback_handler_installed);
rl_callback_handler_install (prompt, gdb_rl_callback_handler);
- callback_handler_installed = 1;
+ callback_handler_installed = true;
}
/* See event-top.h, and above. */
/* Don't use a _filtered function here. It causes the assumed
character position to be off, since the newline we read from
the user is not accounted for. */
- fprintf_unfiltered (gdb_stdout, "%s", actual_gdb_prompt.c_str ());
+ printf_unfiltered ("%s", actual_gdb_prompt.c_str ());
gdb_flush (gdb_stdout);
}
}
static std::string
top_level_prompt (void)
{
- char *prompt;
-
/* Give observers a chance of changing the prompt. E.g., the python
`gdb.prompt_hook' is installed as an observer. */
- gdb::observers::before_prompt.notify (get_prompt ());
+ gdb::observers::before_prompt.notify (get_prompt ().c_str ());
- prompt = get_prompt ();
+ const std::string &prompt = get_prompt ();
if (annotation_level >= 2)
{
beginning. */
const char suffix[] = "\n\032\032prompt\n";
- return std::string (prefix) + prompt + suffix;
+ return std::string (prefix) + prompt.c_str () + suffix;
}
return prompt;
if (main_ui == ui)
{
/* If stdin died, we may as well kill gdb. */
- printf_unfiltered (_("error detected on stdin\n"));
+ fprintf_unfiltered (gdb_stderr, _("error detected on stdin\n"));
quit_command ((char *) 0, 0);
}
else
int c;
char *result;
struct buffer line_buffer;
- static int done_once = 0;
struct ui *ui = current_ui;
buffer_init (&line_buffer);
+ FILE *stream = ui->instream != nullptr ? ui->instream : ui->stdin_stream;
+ gdb_assert (stream != nullptr);
+
/* Unbuffer the input stream, so that, later on, the calls to fgetc
fetch only one char at the time from the stream. The fgetc's will
get up to the first newline, but there may be more chars in the
stream after '\n'. If we buffer the input and fgetc drains the
stream, getting stuff beyond the newline as well, a select, done
- afterwards will not trigger. */
- if (!done_once && !ISATTY (ui->instream))
- {
- setbuf (ui->instream, NULL);
- done_once = 1;
- }
+ afterwards will not trigger.
+
+ This unbuffering was, at one point, not applied if the input stream
+ was a tty, however, the buffering can cause problems, even for a tty,
+ in some cases. Please ensure that any changes in this area run the MI
+ tests with the FORCE_SEPARATE_MI_TTY=1 flag being passed. */
+ setbuf (stream, NULL);
/* We still need the while loop here, even though it would seem
obvious to invoke gdb_readline_no_editing_callback at every
{
/* Read from stdin if we are executing a user defined command.
This is the right thing for prompt_for_continue, at least. */
- c = fgetc (ui->instream != NULL ? ui->instream : ui->stdin_stream);
+ c = fgetc (stream);
if (c == EOF)
{
}
\f
+/* Attempt to unblock signal SIG, return true if the signal was unblocked,
+ otherwise, return false. */
+
+static bool
+unblock_signal (int sig)
+{
+#if HAVE_SIGPROCMASK
+ sigset_t sigset;
+ sigemptyset (&sigset);
+ sigaddset (&sigset, sig);
+ gdb_sigmask (SIG_UNBLOCK, &sigset, 0);
+ return true;
+#endif
+
+ return false;
+}
+
+/* Called to handle fatal signals. SIG is the signal number. */
+
+static void ATTRIBUTE_NORETURN
+handle_fatal_signal (int sig)
+{
+#ifdef GDB_PRINT_INTERNAL_BACKTRACE
+ const auto sig_write = [] (const char *msg) -> void
+ {
+ gdb_stderr->write_async_safe (msg, strlen (msg));
+ };
+
+ if (bt_on_fatal_signal)
+ {
+ sig_write ("\n\n");
+ sig_write (_("Fatal signal: "));
+ sig_write (strsignal (sig));
+ sig_write ("\n");
+
+ gdb_internal_backtrace ();
+
+ sig_write (_("A fatal error internal to GDB has been detected, "
+ "further\ndebugging is not possible. GDB will now "
+ "terminate.\n\n"));
+ sig_write (_("This is a bug, please report it."));
+ if (REPORT_BUGS_TO[0] != '\0')
+ {
+ sig_write (_(" For instructions, see:\n"));
+ sig_write (REPORT_BUGS_TO);
+ sig_write (".");
+ }
+ sig_write ("\n\n");
+
+ gdb_stderr->flush ();
+ }
+#endif
+
+ /* If possible arrange for SIG to have its default behaviour (which
+ should be to terminate the current process), unblock SIG, and reraise
+ the signal. This ensures GDB terminates with the expected signal. */
+ if (signal (sig, SIG_DFL) != SIG_ERR
+ && unblock_signal (sig))
+ raise (sig);
+
+ /* The above failed, so try to use SIGABRT to terminate GDB. */
+#ifdef SIGABRT
+ signal (SIGABRT, SIG_DFL);
+#endif
+ abort (); /* ARI: abort */
+}
+
/* The SIGSEGV handler for this thread, or NULL if there is none. GDB
always installs a global SIGSEGV handler, and then lets threads
indicate their interest in handling the signal by setting this
install_handle_sigsegv ();
if (thread_local_segv_handler == nullptr)
- abort (); /* ARI: abort */
+ handle_fatal_signal (sig);
thread_local_segv_handler (sig);
}
with the reception of the signal.
For SIGSEGV the handle_sig* function does all the work for handling this
- signal. */
+ signal.
+
+ For SIGFPE, SIGBUS, and SIGABRT, these signals will all cause GDB to
+ terminate immediately. */
void
gdb_init_signals (void)
{
create_async_signal_handler (async_sigtstp_handler, NULL, "sigtstp");
#endif
+#ifdef SIGFPE
+ signal (SIGFPE, handle_fatal_signal);
+#endif
+
+#ifdef SIGBUS
+ signal (SIGBUS, handle_fatal_signal);
+#endif
+
+#ifdef SIGABRT
+ signal (SIGABRT, handle_fatal_signal);
+#endif
+
install_handle_sigsegv ();
}
static void
async_sigtstp_handler (gdb_client_data arg)
{
- char *prompt = get_prompt ();
+ const std::string &prompt = get_prompt ();
signal (SIGTSTP, SIG_DFL);
-#if HAVE_SIGPROCMASK
- {
- sigset_t zero;
-
- sigemptyset (&zero);
- gdb_sigmask (SIG_SETMASK, &zero, 0);
- }
-#elif HAVE_SIGSETMASK
- sigsetmask (0);
-#endif
+ unblock_signal (SIGTSTP);
raise (SIGTSTP);
signal (SIGTSTP, handle_sigtstp);
- printf_unfiltered ("%s", prompt);
+ printf_unfiltered ("%s", prompt.c_str ());
gdb_flush (gdb_stdout);
/* Forget about any previous command -- null line now will do
set_debug_event_loop_command,
show_debug_event_loop_command,
&setdebuglist, &showdebuglist);
+
+ add_setshow_boolean_cmd ("backtrace-on-fatal-signal", class_maintenance,
+ &bt_on_fatal_signal, _("\
+Set whether to produce a backtrace if GDB receives a fatal signal."), _("\
+Show whether GDB will produce a backtrace if it receives a fatal signal."), _("\
+Use \"on\" to enable, \"off\" to disable.\n\
+If enabled, GDB will produce a minimal backtrace if it encounters a fatal\n\
+signal from within GDB itself. This is a mechanism to help diagnose\n\
+crashes within GDB, not a mechanism for debugging inferiors."),
+ gdb_internal_backtrace_set_cmd,
+ show_bt_on_fatal_signal,
+ &maintenance_set_cmdlist,
+ &maintenance_show_cmdlist);
}