/* Top level stuff for GDB, the GNU debugger.
- Copyright (C) 1999-2017 Free Software Foundation, Inc.
+ Copyright (C) 1999-2021 Free Software Foundation, Inc.
Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions.
#include "inferior.h"
#include "infrun.h"
#include "target.h"
-#include "terminal.h" /* for job_control */
-#include "event-loop.h"
+#include "terminal.h"
+#include "gdbsupport/event-loop.h"
#include "event-top.h"
#include "interps.h"
#include <signal.h>
#include "cli/cli-script.h" /* for reset_command_nest_depth */
#include "main.h"
#include "gdbthread.h"
-#include "observer.h"
+#include "observable.h"
#include "continuations.h"
#include "gdbcmd.h" /* for dont_repeat() */
#include "annotate.h"
#include "maint.h"
-#include "buffer.h"
+#include "gdbsupport/buffer.h"
#include "ser-event.h"
-#include "gdb_select.h"
+#include "gdbsupport/gdb_select.h"
+#include "gdbsupport/gdb-sigmask.h"
+#include "async-event.h"
/* readline include files. */
#include "readline/readline.h"
/* readline defines this. */
#undef savestring
-static char *top_level_prompt (void);
+static std::string top_level_prompt ();
/* Signal handlers. */
#ifdef SIGQUIT
static void async_disconnect (gdb_client_data);
#endif
static void async_float_handler (gdb_client_data);
-#ifdef STOP_SIGNAL
-static void async_stop_sig (gdb_client_data);
+#ifdef SIGTSTP
+static void async_sigtstp_handler (gdb_client_data);
#endif
static void async_sigterm_handler (gdb_client_data arg);
ezannoni: as of 1999-04-29 I expect that this
variable will not be used after gdb is changed to use the event
loop as default engine, and event-top.c is merged into top.c. */
-int set_editing_cmd_var;
+bool set_editing_cmd_var;
/* This is used to display the notification of the completion of an
asynchronous execution command. */
-int exec_done_display_p = 0;
+bool exec_done_display_p = false;
/* Used by the stdin event handler to compensate for missed stdin events.
Setting this to a non-zero value inside an stdin callback makes the callback
static struct async_signal_handler *sigquit_token;
#endif
static struct async_signal_handler *sigfpe_token;
-#ifdef STOP_SIGNAL
+#ifdef SIGTSTP
static struct async_signal_handler *sigtstp_token;
#endif
static struct async_signal_handler *async_sigterm_token;
static struct gdb_exception
gdb_rl_callback_read_char_wrapper_noexcept () noexcept
{
- struct gdb_exception gdb_expt = exception_none;
+ struct gdb_exception gdb_expt;
/* C++ exceptions can't normally be thrown across readline (unless
it is built with -fexceptions, but it won't by default on many
}
CATCH_SJLJ (ex, RETURN_MASK_ALL)
{
- gdb_expt = ex;
+ gdb_expt = std::move (ex);
}
END_CATCH_SJLJ
/* Rethrow using the normal EH mechanism. */
if (gdb_expt.reason < 0)
- throw_exception (gdb_expt);
+ throw_exception (std::move (gdb_expt));
}
/* GDB's readline callback handler. Calls the current INPUT_HANDLER,
static void
gdb_rl_callback_handler (char *rl) noexcept
{
- struct gdb_exception gdb_rl_expt = exception_none;
+ /* This is static to avoid undefined behavior when calling longjmp
+ -- gdb_exception has a destructor with side effects. */
+ static struct gdb_exception gdb_rl_expt;
struct ui *ui = current_ui;
- TRY
+ try
{
- ui->input_handler (rl);
+ /* Ensure the exception is reset on each call. */
+ gdb_rl_expt = {};
+ ui->input_handler (gdb::unique_xmalloc_ptr<char> (rl));
}
- CATCH (ex, RETURN_MASK_ALL)
+ catch (gdb_exception &ex)
{
- gdb_rl_expt = ex;
+ gdb_rl_expt = std::move (ex);
}
- END_CATCH
/* If we caught a GDB exception, longjmp out of the readline
callback. There's no other way for the callback to signal to
void
display_gdb_prompt (const char *new_prompt)
{
- char *actual_gdb_prompt = NULL;
- struct cleanup *old_chain;
+ std::string actual_gdb_prompt;
annotate_display_prompt ();
/* Reset the nesting depth used when trace-commands is set. */
reset_command_nest_depth ();
- old_chain = make_cleanup (free_current_contents, &actual_gdb_prompt);
-
/* Do not call the python hook on an explicit prompt change as
passed to this function, as this forms a secondary/local prompt,
IE, displayed but not set. */
if (current_ui->command_editing)
gdb_rl_callback_handler_remove ();
- do_cleanups (old_chain);
return;
}
else if (ui->prompt_state == PROMPT_NEEDED)
}
}
else
- actual_gdb_prompt = xstrdup (new_prompt);
+ actual_gdb_prompt = new_prompt;
if (current_ui->command_editing)
{
gdb_rl_callback_handler_remove ();
- gdb_rl_callback_handler_install (actual_gdb_prompt);
+ gdb_rl_callback_handler_install (actual_gdb_prompt.c_str ());
}
/* new_prompt at this point can be the top of the stack or the one
passed in. It can't be NULL. */
else
{
/* 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. */
- fputs_unfiltered (actual_gdb_prompt, gdb_stdout);
+ 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 ());
gdb_flush (gdb_stdout);
}
-
- do_cleanups (old_chain);
}
/* Return the top level prompt, as specified by "set prompt", possibly
- overriden by the python gdb.prompt_hook hook, and then composed
- with the prompt prefix and suffix (annotations). The caller is
- responsible for freeing the returned string. */
+ overridden by the python gdb.prompt_hook hook, and then composed
+ with the prompt prefix and suffix (annotations). */
-static char *
+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. */
- observer_notify_before_prompt (get_prompt ());
+ gdb::observers::before_prompt.notify (get_prompt ());
prompt = get_prompt ();
beginning. */
const char suffix[] = "\n\032\032prompt\n";
- return concat (prefix, prompt, suffix, (char *) NULL);
+ return std::string (prefix) + prompt + suffix;
}
- return xstrdup (prompt);
+ return prompt;
}
/* See top.h. */
else
{
/* Simply delete the UI. */
- delete_ui (ui);
+ delete ui;
}
}
else
void
ui_register_input_event_handler (struct ui *ui)
{
- add_file_handler (ui->input_fd, stdin_event_handler, ui);
+ add_file_handler (ui->input_fd, stdin_event_handler, ui,
+ string_printf ("ui-%d", ui->num), true);
}
/* See top.h. */
a whole command. */
void
-command_handler (char *command)
+command_handler (const char *command)
{
struct ui *ui = current_ui;
- char *c;
+ const char *c;
if (ui->instream == ui->stdin_stream)
reinitialize_more_filter ();
emulations, to CMD_LINE_BUFFER. Returns the command line if we
have a whole command line ready to be processed by the command
interpreter or NULL if the command line isn't complete yet (input
- line ends in a backslash). Takes ownership of RL. */
+ line ends in a backslash). */
static char *
-command_line_append_input_line (struct buffer *cmd_line_buffer, char *rl)
+command_line_append_input_line (struct buffer *cmd_line_buffer, const char *rl)
{
char *cmd;
size_t len;
cmd = cmd_line_buffer->buffer;
}
- /* Allocated in readline. */
- xfree (rl);
-
return cmd;
}
If REPEAT, handle command repetitions:
- If the input command line is NOT empty, the command returned is
- copied into the global 'saved_command_line' var so that it can
- be repeated later.
+ saved using save_command_line () so that it can be repeated later.
- - OTOH, if the input command line IS empty, return the previously
- saved command instead of the empty input line.
+ - OTOH, if the input command line IS empty, return the saved
+ command instead of the empty input line.
*/
char *
handle_line_of_input (struct buffer *cmd_line_buffer,
- char *rl, int repeat, const char *annotation_suffix)
+ const char *rl, int repeat,
+ const char *annotation_suffix)
{
struct ui *ui = current_ui;
int from_tty = ui->instream == ui->stdin_stream;
}
#define SERVER_COMMAND_PREFIX "server "
- if (startswith (cmd, SERVER_COMMAND_PREFIX))
+ server_command = startswith (cmd, SERVER_COMMAND_PREFIX);
+ if (server_command)
{
- /* Note that we don't set `saved_command_line'. Between this
- and the check in dont_repeat, this insures that repeating
- will still do the right thing. */
+ /* Note that we don't call `save_command_line'. Between this
+ and the check in dont_repeat, this insures that repeating
+ will still do the right thing. */
return cmd + strlen (SERVER_COMMAND_PREFIX);
}
/* Do history expansion if that is wished. */
if (history_expansion_p && from_tty && input_interactive_p (current_ui))
{
- char *history_value;
+ char *cmd_expansion;
int expanded;
- expanded = history_expand (cmd, &history_value);
+ expanded = history_expand (cmd, &cmd_expansion);
+ gdb::unique_xmalloc_ptr<char> history_value (cmd_expansion);
if (expanded)
{
size_t len;
/* Print the changes. */
- printf_unfiltered ("%s\n", history_value);
+ printf_unfiltered ("%s\n", history_value.get ());
/* If there was an error, call this function again. */
if (expanded < 0)
- {
- xfree (history_value);
- return cmd;
- }
+ return cmd;
/* history_expand returns an allocated string. Just replace
our buffer with it. */
- len = strlen (history_value);
+ len = strlen (history_value.get ());
xfree (buffer_finish (cmd_line_buffer));
- cmd_line_buffer->buffer = history_value;
+ cmd_line_buffer->buffer = history_value.get ();
cmd_line_buffer->buffer_size = len + 1;
- cmd = history_value;
+ cmd = history_value.release ();
}
}
for (p1 = cmd; *p1 == ' ' || *p1 == '\t'; p1++)
;
if (repeat && *p1 == '\0')
- return saved_command_line;
+ return get_saved_command_line ();
/* Add command to history if appropriate. Note: lines consisting
solely of comments are also added to the command history. This
/* Save into global buffer if appropriate. */
if (repeat)
{
- xfree (saved_command_line);
- saved_command_line = xstrdup (cmd);
- return saved_command_line;
+ save_command_line (cmd);
+ return get_saved_command_line ();
}
else
return cmd;
function. */
void
-command_line_handler (char *rl)
+command_line_handler (gdb::unique_xmalloc_ptr<char> &&rl)
{
struct buffer *line_buffer = get_command_line_buffer ();
struct ui *ui = current_ui;
char *cmd;
- cmd = handle_line_of_input (line_buffer, rl, 1, "prompt");
+ cmd = handle_line_of_input (line_buffer, rl.get (), 1, "prompt");
if (cmd == (char *) EOF)
{
/* stdin closed. The connection with the terminal is gone.
hung up but GDB is still alive. In such a case, we just quit
gdb killing the inferior program too. */
printf_unfiltered ("quit\n");
- execute_command ((char *) "quit", 1);
+ execute_command ("quit", 1);
}
else if (cmd == NULL)
{
while (1)
{
/* Read from stdin if we are executing a user defined command.
- This is the right thing for prompt_for_continue, at least. */
+ This is the right thing for prompt_for_continue, at least. */
c = fgetc (ui->instream != NULL ? ui->instream : ui->stdin_stream);
if (c == EOF)
buffer_grow_char (&line_buffer, '\0');
result = buffer_finish (&line_buffer);
- ui->input_handler (result);
+ ui->input_handler (gdb::unique_xmalloc_ptr<char> (result));
}
\f
+/* See event-top.h. */
+
+thread_local void (*thread_local_segv_handler) (int);
+
+static void handle_sigsegv (int sig);
+
+/* Install the SIGSEGV handler. */
+static void
+install_handle_sigsegv ()
+{
+#if defined (HAVE_SIGACTION)
+ struct sigaction sa;
+ sa.sa_handler = handle_sigsegv;
+ sigemptyset (&sa.sa_mask);
+#ifdef HAVE_SIGALTSTACK
+ sa.sa_flags = SA_ONSTACK;
+#else
+ sa.sa_flags = 0;
+#endif
+ sigaction (SIGSEGV, &sa, nullptr);
+#else
+ signal (SIGSEGV, handle_sigsegv);
+#endif
+}
+
+/* Handler for SIGSEGV. */
+
+static void
+handle_sigsegv (int sig)
+{
+ install_handle_sigsegv ();
+
+ if (thread_local_segv_handler == nullptr)
+ abort (); /* ARI: abort */
+ thread_local_segv_handler (sig);
+}
+
+\f
+
/* The serial event associated with the QUIT flag. set_quit_flag sets
this, and check_quit_flag clears it. Used by interruptible_select
to be able to do interruptible I/O with no race with the SIGINT
signal (SIGINT, handle_sigint);
sigint_token =
- create_async_signal_handler (async_request_quit, NULL);
+ create_async_signal_handler (async_request_quit, NULL, "sigint");
signal (SIGTERM, handle_sigterm);
async_sigterm_token
- = create_async_signal_handler (async_sigterm_handler, NULL);
+ = create_async_signal_handler (async_sigterm_handler, NULL, "sigterm");
/* If SIGTRAP was set to SIG_IGN, then the SIG_IGN will get passed
to the inferior and breakpoints will be ignored. */
to SIG_DFL for us. */
signal (SIGQUIT, handle_sigquit);
sigquit_token =
- create_async_signal_handler (async_do_nothing, NULL);
+ create_async_signal_handler (async_do_nothing, NULL, "sigquit");
#endif
#ifdef SIGHUP
if (signal (SIGHUP, handle_sighup) != SIG_IGN)
sighup_token =
- create_async_signal_handler (async_disconnect, NULL);
+ create_async_signal_handler (async_disconnect, NULL, "sighup");
else
sighup_token =
- create_async_signal_handler (async_do_nothing, NULL);
+ create_async_signal_handler (async_do_nothing, NULL, "sighup");
#endif
signal (SIGFPE, handle_sigfpe);
sigfpe_token =
- create_async_signal_handler (async_float_handler, NULL);
+ create_async_signal_handler (async_float_handler, NULL, "sigfpe");
-#ifdef STOP_SIGNAL
+#ifdef SIGTSTP
sigtstp_token =
- create_async_signal_handler (async_stop_sig, NULL);
+ create_async_signal_handler (async_sigtstp_handler, NULL, "sigtstp");
#endif
+
+ install_handle_sigsegv ();
}
/* See defs.h. */
async_disconnect (gdb_client_data arg)
{
- TRY
+ try
{
quit_cover ();
}
- CATCH (exception, RETURN_MASK_ALL)
+ catch (const gdb_exception &exception)
{
fputs_filtered ("Could not kill the program being debugged",
gdb_stderr);
exception_print (gdb_stderr, exception);
}
- END_CATCH
- TRY
- {
- pop_all_targets ();
- }
- CATCH (exception, RETURN_MASK_ALL)
+ for (inferior *inf : all_inferiors ())
{
+ switch_to_inferior_no_thread (inf);
+ try
+ {
+ pop_all_targets ();
+ }
+ catch (const gdb_exception &exception)
+ {
+ }
}
- END_CATCH
signal (SIGHUP, SIG_DFL); /*FIXME: ??????????? */
raise (SIGHUP);
}
#endif
-#ifdef STOP_SIGNAL
+#ifdef SIGTSTP
void
-handle_stop_sig (int sig)
+handle_sigtstp (int sig)
{
mark_async_signal_handler (sigtstp_token);
- signal (sig, handle_stop_sig);
+ signal (sig, handle_sigtstp);
}
static void
-async_stop_sig (gdb_client_data arg)
+async_sigtstp_handler (gdb_client_data arg)
{
char *prompt = get_prompt ();
-#if STOP_SIGNAL == SIGTSTP
signal (SIGTSTP, SIG_DFL);
#if HAVE_SIGPROCMASK
{
sigset_t zero;
sigemptyset (&zero);
- sigprocmask (SIG_SETMASK, &zero, 0);
+ gdb_sigmask (SIG_SETMASK, &zero, 0);
}
#elif HAVE_SIGSETMASK
sigsetmask (0);
#endif
raise (SIGTSTP);
- signal (SIGTSTP, handle_stop_sig);
-#else
- signal (STOP_SIGNAL, handle_stop_sig);
-#endif
+ signal (SIGTSTP, handle_sigtstp);
printf_unfiltered ("%s", prompt);
gdb_flush (gdb_stdout);
nothing. */
dont_repeat ();
}
-#endif /* STOP_SIGNAL */
+#endif /* SIGTSTP */
/* Tell the event loop what to do if SIGFPE is received.
See event-signal.c. */
signal (sig, handle_sigfpe);
}
-/* Event loop will call this functin to process a SIGFPE. */
+/* Event loop will call this function to process a SIGFPE. */
static void
async_float_handler (gdb_client_data arg)
{
gdb_rl_callback_handler_remove ();
delete_file_handler (ui->input_fd);
}
+
+static const char debug_event_loop_off[] = "off";
+static const char debug_event_loop_all_except_ui[] = "all-except-ui";
+static const char debug_event_loop_all[] = "all";
+
+static const char *debug_event_loop_enum[] = {
+ debug_event_loop_off,
+ debug_event_loop_all_except_ui,
+ debug_event_loop_all,
+ nullptr
+};
+
+static const char *debug_event_loop_value = debug_event_loop_off;
+
+static void
+set_debug_event_loop_command (const char *args, int from_tty,
+ cmd_list_element *c)
+{
+ if (debug_event_loop_value == debug_event_loop_off)
+ debug_event_loop = debug_event_loop_kind::OFF;
+ else if (debug_event_loop_value == debug_event_loop_all_except_ui)
+ debug_event_loop = debug_event_loop_kind::ALL_EXCEPT_UI;
+ else if (debug_event_loop_value == debug_event_loop_all)
+ debug_event_loop = debug_event_loop_kind::ALL;
+ else
+ gdb_assert_not_reached ("Invalid debug event look kind value.");
+}
+
+static void
+show_debug_event_loop_command (struct ui_file *file, int from_tty,
+ struct cmd_list_element *cmd, const char *value)
+{
+ fprintf_filtered (file, _("Event loop debugging is %s.\n"), value);
+}
+
+void _initialize_event_top ();
+void
+_initialize_event_top ()
+{
+ add_setshow_enum_cmd ("event-loop", class_maintenance,
+ debug_event_loop_enum,
+ &debug_event_loop_value,
+ _("Set event-loop debugging."),
+ _("Show event-loop debugging."),
+ _("\
+Control whether to show event loop-related debug messages."),
+ set_debug_event_loop_command,
+ show_debug_event_loop_command,
+ &setdebuglist, &showdebuglist);
+}