X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fmi%2Fmi-interp.c;h=b075e33c16f8bd9f47bf7e86357335afcd2f82b8;hb=268a13a5a3f7c6b9b6ffc5ac2d1b24eb41f3fbdc;hp=08201ca5bb20878dbcaf4c7baa1b0a8f8e86b809;hpb=c6f0559b48289f9b295ab3fac1bb5d78f88e5d91;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 08201ca5bb..ab1f913887 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -1,12 +1,12 @@ /* MI Interpreter Definitions and Commands for GDB, the GNU debugger. - Copyright 2002, 2003, 2003 Free Software Foundation, Inc. + Copyright (C) 2002-2019 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 2 of the License, or + 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, @@ -15,392 +15,1358 @@ 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + along with this program. If not, see . */ #include "defs.h" -#include "gdb_string.h" #include "interps.h" #include "event-top.h" #include "event-loop.h" #include "inferior.h" +#include "infrun.h" #include "ui-out.h" #include "top.h" - #include "mi-main.h" #include "mi-cmds.h" #include "mi-out.h" #include "mi-console.h" +#include "mi-common.h" +#include "observable.h" +#include "gdbthread.h" +#include "solist.h" +#include "objfiles.h" +#include "tracepoint.h" +#include "cli-out.h" +#include "thread-fsm.h" +#include "cli/cli-interp.h" +#include "gdbsupport/scope-exit.h" -struct mi_interp -{ - /* MI's output channels */ - struct ui_file *out; - struct ui_file *err; - struct ui_file *log; - struct ui_file *targ; - struct ui_file *event_channel; - - /* This is the interpreter for the mi... */ - struct interp *mi2_interp; - struct interp *mi1_interp; - struct interp *mi_interp; -}; +/* These are the interpreter setup, etc. functions for the MI + interpreter. */ -/* These are the interpreter setup, etc. functions for the MI interpreter */ -static void mi_execute_command_wrapper (char *cmd); -static void mi_command_loop (int mi_version); -static char *mi_input (char *); +static void mi_execute_command_wrapper (const char *cmd); +static void mi_execute_command_input_handler + (gdb::unique_xmalloc_ptr &&cmd); /* These are hooks that we put in place while doing interpreter_exec - so we can report interesting things that happened "behind the mi's - back" in this command */ -static int mi_interp_query_hook (const char *ctlstr, va_list ap); + so we can report interesting things that happened "behind the MI's + back" in this command. */ -static void mi3_command_loop (void); -static void mi2_command_loop (void); -static void mi1_command_loop (void); +static int mi_interp_query_hook (const char *ctlstr, va_list ap) + ATTRIBUTE_PRINTF (1, 0); static void mi_insert_notify_hooks (void); static void mi_remove_notify_hooks (void); -static void * -mi_interpreter_init (void) -{ - struct mi_interp *mi = XMALLOC (struct mi_interp); +static void mi_on_signal_received (enum gdb_signal siggnal); +static void mi_on_end_stepping_range (void); +static void mi_on_signal_exited (enum gdb_signal siggnal); +static void mi_on_exited (int exitstatus); +static void mi_on_normal_stop (struct bpstats *bs, int print_frame); +static void mi_on_no_history (void); - /* Why is this a part of the mi architecture? */ +static void mi_new_thread (struct thread_info *t); +static void mi_thread_exit (struct thread_info *t, int silent); +static void mi_record_changed (struct inferior*, int, const char *, + const char *); +static void mi_inferior_added (struct inferior *inf); +static void mi_inferior_appeared (struct inferior *inf); +static void mi_inferior_exit (struct inferior *inf); +static void mi_inferior_removed (struct inferior *inf); +static void mi_on_resume (ptid_t ptid); +static void mi_solib_loaded (struct so_list *solib); +static void mi_solib_unloaded (struct so_list *solib); +static void mi_about_to_proceed (void); +static void mi_traceframe_changed (int tfnum, int tpnum); +static void mi_tsv_created (const struct trace_state_variable *tsv); +static void mi_tsv_deleted (const struct trace_state_variable *tsv); +static void mi_tsv_modified (const struct trace_state_variable *tsv); +static void mi_breakpoint_created (struct breakpoint *b); +static void mi_breakpoint_deleted (struct breakpoint *b); +static void mi_breakpoint_modified (struct breakpoint *b); +static void mi_command_param_changed (const char *param, const char *value); +static void mi_memory_changed (struct inferior *inf, CORE_ADDR memaddr, + ssize_t len, const bfd_byte *myaddr); +static void mi_on_sync_execution_done (void); - mi_setup_architecture_data (); +static int report_initial_inferior (struct inferior *inf, void *closure); - /* HACK: We need to force stdout/stderr to point at the console. This avoids - any potential side effects caused by legacy code that is still - using the TUI / fputs_unfiltered_hook. So we set up output channels for - this now, and swap them in when we are run. */ +/* Display the MI prompt. */ - raw_stdout = stdio_fileopen (stdout); +static void +display_mi_prompt (struct mi_interp *mi) +{ + struct ui *ui = current_ui; - /* Create MI channels */ - mi->out = mi_console_file_new (raw_stdout, "~", '"'); - mi->err = mi_console_file_new (raw_stdout, "&", '"'); - mi->log = mi->err; - mi->targ = mi_console_file_new (raw_stdout, "@", '"'); - mi->event_channel = mi_console_file_new (raw_stdout, "=", 0); + fputs_unfiltered ("(gdb) \n", mi->raw_stdout); + gdb_flush (mi->raw_stdout); + ui->prompt_state = PROMPTED; +} - return mi; +/* Returns the INTERP's data cast as mi_interp if INTERP is an MI, and + returns NULL otherwise. */ + +static struct mi_interp * +as_mi_interp (struct interp *interp) +{ + return dynamic_cast (interp); } -static int -mi_interpreter_resume (void *data) +void +mi_interp::init (bool top_level) { - struct mi_interp *mi = data; - /* As per hack note in mi_interpreter_init, swap in the output channels... */ + mi_interp *mi = this; - gdb_setup_readline (); + /* Store the current output channel, so that we can create a console + channel that encapsulates and prefixes all gdb_output-type bits + coming from the rest of the debugger. */ + mi->raw_stdout = gdb_stdout; - if (event_loop_p) + /* Create MI console channels, each with a different prefix so they + can be distinguished. */ + mi->out = new mi_console_file (mi->raw_stdout, "~", '"'); + mi->err = new mi_console_file (mi->raw_stdout, "&", '"'); + mi->log = mi->err; + mi->targ = new mi_console_file (mi->raw_stdout, "@", '"'); + mi->event_channel = new mi_console_file (mi->raw_stdout, "=", 0); + mi->mi_uiout = mi_out_new (name ()); + gdb_assert (mi->mi_uiout != nullptr); + mi->cli_uiout = cli_out_new (mi->out); + + if (top_level) { - /* These overwrite some of the initialization done in - _intialize_event_loop. */ - call_readline = gdb_readline2; - input_handler = mi_execute_command_wrapper; - add_file_handler (input_fd, stdin_event_handler, 0); - async_command_editing_p = 0; - /* FIXME: This is a total hack for now. PB's use of the MI implicitly - relies on a bug in the async support which allows asynchronous - commands to leak through the commmand loop. The bug involves - (but is not limited to) the fact that sync_execution was - erroneously initialized to 0. Duplicate by initializing it - thus here... */ - sync_execution = 0; + /* The initial inferior is created before this function is + called, so we need to report it explicitly. Use iteration in + case future version of GDB creates more than one inferior + up-front. */ + iterate_over_inferiors (report_initial_inferior, mi); } +} + +void +mi_interp::resume () +{ + struct mi_interp *mi = this; + struct ui *ui = current_ui; + + /* As per hack note in mi_interpreter_init, swap in the output + channels... */ + gdb_setup_readline (0); + + ui->call_readline = gdb_readline_no_editing_callback; + ui->input_handler = mi_execute_command_input_handler; gdb_stdout = mi->out; - /* Route error and log output through the MI */ + /* Route error and log output through the MI. */ gdb_stderr = mi->err; gdb_stdlog = mi->log; - /* Route target output through the MI. */ + /* Route target output through the MI. */ gdb_stdtarg = mi->targ; + /* Route target error through the MI as well. */ + gdb_stdtargerr = mi->targ; /* Replace all the hooks that we know about. There really needs to be a better way of doing this... */ clear_interpreter_hooks (); - show_load_progress = mi_load_progress; + deprecated_show_load_progress = mi_load_progress; +} - /* If we're _the_ interpreter, take control. */ - if (current_interp_named_p (INTERP_MI1)) - command_loop_hook = mi1_command_loop; - else if (current_interp_named_p (INTERP_MI2)) - command_loop_hook = mi2_command_loop; - else if (current_interp_named_p (INTERP_MI3)) - command_loop_hook = mi3_command_loop; - else - command_loop_hook = mi2_command_loop; +void +mi_interp::suspend () +{ + gdb_disable_readline (); +} - return 1; +gdb_exception +mi_interp::exec (const char *command) +{ + mi_execute_command_wrapper (command); + return gdb_exception (); } -static int -mi_interpreter_suspend (void *data) +void +mi_cmd_interpreter_exec (const char *command, char **argv, int argc) { - gdb_disable_readline (); - return 1; + struct interp *interp_to_use; + int i; + + if (argc < 2) + error (_("-interpreter-exec: " + "Usage: -interpreter-exec interp command")); + + interp_to_use = interp_lookup (current_ui, argv[0]); + if (interp_to_use == NULL) + error (_("-interpreter-exec: could not find interpreter \"%s\""), + argv[0]); + + /* Note that unlike the CLI version of this command, we don't + actually set INTERP_TO_USE as the current interpreter, as we + still want gdb_stdout, etc. to point at MI streams. */ + + /* Insert the MI out hooks, making sure to also call the + interpreter's hooks if it has any. */ + /* KRS: We shouldn't need this... Events should be installed and + they should just ALWAYS fire something out down the MI + channel. */ + mi_insert_notify_hooks (); + + /* Now run the code. */ + + SCOPE_EXIT + { + mi_remove_notify_hooks (); + }; + + for (i = 1; i < argc; i++) + { + struct gdb_exception e = interp_exec (interp_to_use, argv[i]); + + if (e.reason < 0) + error ("%s", e.what ()); + } +} + +/* This inserts a number of hooks that are meant to produce + async-notify ("=") MI messages while running commands in another + interpreter using mi_interpreter_exec. The canonical use for this + is to allow access to the gdb CLI interpreter from within the MI, + while still producing MI style output when actions in the CLI + command change GDB's state. */ + +static void +mi_insert_notify_hooks (void) +{ + deprecated_query_hook = mi_interp_query_hook; +} + +static void +mi_remove_notify_hooks (void) +{ + deprecated_query_hook = NULL; } static int -mi_interpreter_exec (void *data, const char *command) +mi_interp_query_hook (const char *ctlstr, va_list ap) { - char *tmp = alloca (strlen (command) + 1); - strcpy (tmp, command); - mi_execute_command_wrapper (tmp); return 1; } -/* Never display the default gdb prompt in mi case. */ -static int -mi_interpreter_prompt_p (void *data) +static void +mi_execute_command_wrapper (const char *cmd) { - return 0; + struct ui *ui = current_ui; + + mi_execute_command (cmd, ui->instream == ui->stdin_stream); +} + +/* Observer for the synchronous_command_done notification. */ + +static void +mi_on_sync_execution_done (void) +{ + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + return; + + /* If MI is sync, then output the MI prompt now, indicating we're + ready for further input. */ + if (!mi_async_p ()) + display_mi_prompt (mi); +} + +/* mi_execute_command_wrapper wrapper suitable for INPUT_HANDLER. */ + +static void +mi_execute_command_input_handler (gdb::unique_xmalloc_ptr &&cmd) +{ + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui *ui = current_ui; + + ui->prompt_state = PROMPT_NEEDED; + + mi_execute_command_wrapper (cmd.get ()); + + /* Print a prompt, indicating we're ready for further input, unless + we just started a synchronous command. In that case, we're about + to go back to the event loop and will output the prompt in the + 'synchronous_command_done' observer when the target next + stops. */ + if (ui->prompt_state == PROMPT_NEEDED) + display_mi_prompt (mi); +} + +void +mi_interp::pre_command_loop () +{ + struct mi_interp *mi = this; + + /* Turn off 8 bit strings in quoted output. Any character with the + high bit set is printed using C's octal format. */ + sevenbit_strings = 1; + + /* Tell the world that we're alive. */ + display_mi_prompt (mi); } static void -mi_interpreter_exec_continuation (struct continuation_arg *arg) +mi_new_thread (struct thread_info *t) { - bpstat_do_actions (&stop_bpstat); - if (!target_executing) + SWITCH_THRU_ALL_UIS () { - fputs_unfiltered ("*stopped", raw_stdout); - mi_out_put (uiout, raw_stdout); - fputs_unfiltered ("\n", raw_stdout); - fputs_unfiltered ("(gdb) \n", raw_stdout); - gdb_flush (raw_stdout); - do_exec_cleanups (ALL_CLEANUPS); + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "thread-created,id=\"%d\",group-id=\"i%d\"", + t->global_num, t->inf->num); + gdb_flush (mi->event_channel); } - else if (target_can_async_p ()) +} + +static void +mi_thread_exit (struct thread_info *t, int silent) +{ + if (silent) + return; + + SWITCH_THRU_ALL_UIS () { - add_continuation (mi_interpreter_exec_continuation, NULL); + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + fprintf_unfiltered (mi->event_channel, + "thread-exited,id=\"%d\",group-id=\"i%d\"", + t->global_num, t->inf->num); + gdb_flush (mi->event_channel); } } -enum mi_cmd_result -mi_cmd_interpreter_exec (char *command, char **argv, int argc) +/* Emit notification on changing the state of record. */ + +static void +mi_record_changed (struct inferior *inferior, int started, const char *method, + const char *format) { - struct interp *interp_to_use; - enum mi_cmd_result result = MI_CMD_DONE; - int i; - struct interp_procs *procs; + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); - if (argc < 2) + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + if (started) + { + if (format != NULL) + { + fprintf_unfiltered (mi->event_channel, + "record-started,thread-group=\"i%d\"," + "method=\"%s\",format=\"%s\"", + inferior->num, method, format); + } + else + { + fprintf_unfiltered (mi->event_channel, + "record-started,thread-group=\"i%d\"," + "method=\"%s\"", + inferior->num, method); + } + } + else + { + fprintf_unfiltered (mi->event_channel, + "record-stopped,thread-group=\"i%d\"", + inferior->num); + } + + gdb_flush (mi->event_channel); + } +} + +static void +mi_inferior_added (struct inferior *inf) +{ + SWITCH_THRU_ALL_UIS () { - xasprintf (&mi_error_message, - "mi_cmd_interpreter_exec: Usage: -interpreter-exec interp command"); - return MI_CMD_ERROR; + struct interp *interp; + struct mi_interp *mi; + + /* We'll be called once for the initial inferior, before the top + level interpreter is set. */ + interp = top_level_interpreter (); + if (interp == NULL) + continue; + + mi = as_mi_interp (interp); + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "thread-group-added,id=\"i%d\"", + inf->num); + gdb_flush (mi->event_channel); } +} - interp_to_use = interp_lookup (argv[0]); - if (interp_to_use == NULL) +static void +mi_inferior_appeared (struct inferior *inf) +{ + SWITCH_THRU_ALL_UIS () { - xasprintf (&mi_error_message, - "mi_cmd_interpreter_exec: could not find interpreter \"%s\"", - argv[0]); - return MI_CMD_ERROR; + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "thread-group-started,id=\"i%d\",pid=\"%d\"", + inf->num, inf->pid); + gdb_flush (mi->event_channel); } +} - if (!interp_exec_p (interp_to_use)) +static void +mi_inferior_exit (struct inferior *inf) +{ + SWITCH_THRU_ALL_UIS () { - xasprintf (&mi_error_message, - "mi_cmd_interpreter_exec: interpreter \"%s\" does not support command execution", - argv[0]); - return MI_CMD_ERROR; + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + if (inf->has_exit_code) + fprintf_unfiltered (mi->event_channel, + "thread-group-exited,id=\"i%d\",exit-code=\"%s\"", + inf->num, int_string (inf->exit_code, 8, 0, 0, 1)); + else + fprintf_unfiltered (mi->event_channel, + "thread-group-exited,id=\"i%d\"", inf->num); + + gdb_flush (mi->event_channel); } +} - /* Insert the MI out hooks, making sure to also call the interpreter's hooks - if it has any. */ - /* KRS: We shouldn't need this... Events should be installed and they should - just ALWAYS fire something out down the MI channel... */ - mi_insert_notify_hooks (); +static void +mi_inferior_removed (struct inferior *inf) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); - /* Now run the code... */ + if (mi == NULL) + continue; - for (i = 1; i < argc; i++) + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "thread-group-removed,id=\"i%d\"", + inf->num); + gdb_flush (mi->event_channel); + } +} + +/* Return the MI interpreter, if it is active -- either because it's + the top-level interpreter or the interpreter executing the current + command. Returns NULL if the MI interpreter is not being used. */ + +static struct mi_interp * +find_mi_interp (void) +{ + struct mi_interp *mi; + + mi = as_mi_interp (top_level_interpreter ()); + if (mi != NULL) + return mi; + + mi = as_mi_interp (command_interp ()); + if (mi != NULL) + return mi; + + return NULL; +} + +/* Observers for several run control events that print why the + inferior has stopped to both the MI event channel and to the MI + console. If the MI interpreter is not active, print nothing. */ + +/* Observer for the signal_received notification. */ + +static void +mi_on_signal_received (enum gdb_signal siggnal) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = find_mi_interp (); + + if (mi == NULL) + continue; + + print_signal_received_reason (mi->mi_uiout, siggnal); + print_signal_received_reason (mi->cli_uiout, siggnal); + } +} + +/* Observer for the end_stepping_range notification. */ + +static void +mi_on_end_stepping_range (void) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = find_mi_interp (); + + if (mi == NULL) + continue; + + print_end_stepping_range_reason (mi->mi_uiout); + print_end_stepping_range_reason (mi->cli_uiout); + } +} + +/* Observer for the signal_exited notification. */ + +static void +mi_on_signal_exited (enum gdb_signal siggnal) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = find_mi_interp (); + + if (mi == NULL) + continue; + + print_signal_exited_reason (mi->mi_uiout, siggnal); + print_signal_exited_reason (mi->cli_uiout, siggnal); + } +} + +/* Observer for the exited notification. */ + +static void +mi_on_exited (int exitstatus) +{ + SWITCH_THRU_ALL_UIS () { - char *buff = NULL; - /* Do this in a cleaner way... We want to force execution to be - asynchronous for commands that run the target. */ - if (target_can_async_p () && (strcmp (argv[0], "console") == 0)) + struct mi_interp *mi = find_mi_interp (); + + if (mi == NULL) + continue; + + print_exited_reason (mi->mi_uiout, exitstatus); + print_exited_reason (mi->cli_uiout, exitstatus); + } +} + +/* Observer for the no_history notification. */ + +static void +mi_on_no_history (void) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = find_mi_interp (); + + if (mi == NULL) + continue; + + print_no_history_reason (mi->mi_uiout); + print_no_history_reason (mi->cli_uiout); + } +} + +static void +mi_on_normal_stop_1 (struct bpstats *bs, int print_frame) +{ + /* Since this can be called when CLI command is executing, + using cli interpreter, be sure to use MI uiout for output, + not the current one. */ + struct ui_out *mi_uiout = top_level_interpreter ()->interp_ui_out (); + struct mi_interp *mi = (struct mi_interp *) top_level_interpreter (); + + if (print_frame) + { + struct thread_info *tp; + int core; + struct interp *console_interp; + + tp = inferior_thread (); + + if (tp->thread_fsm != NULL + && tp->thread_fsm->finished_p ()) { - int len = strlen (argv[i]); - buff = xmalloc (len + 2); - memcpy (buff, argv[i], len); - buff[len] = '&'; - buff[len + 1] = '\0'; + enum async_reply_reason reason; + + reason = tp->thread_fsm->async_reply_reason (); + mi_uiout->field_string ("reason", async_reason_lookup (reason)); } - /* We had to set sync_execution = 0 for the mi (well really for Project - Builder's use of the mi - particularly so interrupting would work. - But for console commands to work, we need to initialize it to 1 - - since that is what the cli expects - before running the command, - and then set it back to 0 when we are done. */ - sync_execution = 1; - if (interp_exec (interp_to_use, argv[i]) < 0) + console_interp = interp_lookup (current_ui, INTERP_CONSOLE); + /* We only want to print the displays once, and we want it to + look just how it would on the console, so we use this to + decide whether the MI stop should include them. */ + bool console_print = should_print_stop_to_console (console_interp, tp); + print_stop_event (mi_uiout, !console_print); + + if (console_print) + print_stop_event (mi->cli_uiout); + + mi_uiout->field_int ("thread-id", tp->global_num); + if (non_stop) { - mi_error_last_message (); - result = MI_CMD_ERROR; - break; + ui_out_emit_list list_emitter (mi_uiout, "stopped-threads"); + + mi_uiout->field_int (NULL, tp->global_num); } - xfree (buff); - do_exec_error_cleanups (ALL_CLEANUPS); - sync_execution = 0; + else + mi_uiout->field_string ("stopped-threads", "all"); + + core = target_core_of_thread (tp->ptid); + if (core != -1) + mi_uiout->field_int ("core", core); } + + fputs_unfiltered ("*stopped", mi->raw_stdout); + mi_out_put (mi_uiout, mi->raw_stdout); + mi_out_rewind (mi_uiout); + mi_print_timing_maybe (mi->raw_stdout); + fputs_unfiltered ("\n", mi->raw_stdout); + gdb_flush (mi->raw_stdout); +} - mi_remove_notify_hooks (); +static void +mi_on_normal_stop (struct bpstats *bs, int print_frame) +{ + SWITCH_THRU_ALL_UIS () + { + if (as_mi_interp (top_level_interpreter ()) == NULL) + continue; - /* Okay, now let's see if the command set the inferior going... - Tricky point - have to do this AFTER resetting the interpreter, since - changing the interpreter will clear out all the continuations for - that interpreter... */ + mi_on_normal_stop_1 (bs, print_frame); + } +} + +static void +mi_about_to_proceed (void) +{ + /* Suppress output while calling an inferior function. */ - if (target_can_async_p () && target_executing) + if (inferior_ptid != null_ptid) { - fputs_unfiltered ("^running\n", raw_stdout); - add_continuation (mi_interpreter_exec_continuation, NULL); + struct thread_info *tp = inferior_thread (); + + if (tp->control.in_infcall) + return; } - return result; + mi_proceeded = 1; } -/* - * mi_insert_notify_hooks - This inserts a number of hooks that are meant to produce - * async-notify ("=") MI messages while running commands in another interpreter - * using mi_interpreter_exec. The canonical use for this is to allow access to - * the gdb CLI interpreter from within the MI, while still producing MI style output - * when actions in the CLI command change gdb's state. -*/ +/* When the element is non-zero, no MI notifications will be emitted in + response to the corresponding observers. */ + +struct mi_suppress_notification mi_suppress_notification = + { + 0, + 0, + 0, + 0, + }; + +/* Emit notification on changing a traceframe. */ static void -mi_insert_notify_hooks (void) +mi_traceframe_changed (int tfnum, int tpnum) { - query_hook = mi_interp_query_hook; + if (mi_suppress_notification.traceframe) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + if (tfnum >= 0) + fprintf_unfiltered (mi->event_channel, "traceframe-changed," + "num=\"%d\",tracepoint=\"%d\"\n", + tfnum, tpnum); + else + fprintf_unfiltered (mi->event_channel, "traceframe-changed,end"); + + gdb_flush (mi->event_channel); + } } +/* Emit notification on creating a trace state variable. */ + static void -mi_remove_notify_hooks (void) +mi_tsv_created (const struct trace_state_variable *tsv) { - query_hook = NULL; + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "tsv-created," + "name=\"%s\",initial=\"%s\"\n", + tsv->name.c_str (), plongest (tsv->initial_value)); + + gdb_flush (mi->event_channel); + } } -static int -mi_interp_query_hook (const char *ctlstr, va_list ap) +/* Emit notification on deleting a trace state variable. */ + +static void +mi_tsv_deleted (const struct trace_state_variable *tsv) { - return 1; + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + if (tsv != NULL) + fprintf_unfiltered (mi->event_channel, "tsv-deleted," + "name=\"%s\"\n", tsv->name.c_str ()); + else + fprintf_unfiltered (mi->event_channel, "tsv-deleted\n"); + + gdb_flush (mi->event_channel); + } } +/* Emit notification on modifying a trace state variable. */ + static void -mi_execute_command_wrapper (char *cmd) +mi_tsv_modified (const struct trace_state_variable *tsv) { - mi_execute_command (cmd, stdin == instream); + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *mi_uiout; + + if (mi == NULL) + continue; + + mi_uiout = top_level_interpreter ()->interp_ui_out (); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "tsv-modified"); + + mi_uiout->redirect (mi->event_channel); + + mi_uiout->field_string ("name", tsv->name); + mi_uiout->field_string ("initial", + plongest (tsv->initial_value)); + if (tsv->value_known) + mi_uiout->field_string ("current", plongest (tsv->value)); + + mi_uiout->redirect (NULL); + + gdb_flush (mi->event_channel); + } } +/* Print breakpoint BP on MI's event channel. */ + static void -mi1_command_loop (void) +mi_print_breakpoint_for_event (struct mi_interp *mi, breakpoint *bp) { - mi_command_loop (1); + ui_out *mi_uiout = mi->interp_ui_out (); + + /* We want the output from print_breakpoint to go to + mi->event_channel. One approach would be to just call + print_breakpoint, and then use mi_out_put to send the current + content of mi_uiout into mi->event_channel. However, that will + break if anything is output to mi_uiout prior to calling the + breakpoint_created notifications. So, we use + ui_out_redirect. */ + mi_uiout->redirect (mi->event_channel); + + try + { + scoped_restore restore_uiout + = make_scoped_restore (¤t_uiout, mi_uiout); + + print_breakpoint (bp); + } + catch (const gdb_exception &ex) + { + exception_print (gdb_stderr, ex); + } + + mi_uiout->redirect (NULL); } +/* Emit notification about a created breakpoint. */ + static void -mi2_command_loop (void) +mi_breakpoint_created (struct breakpoint *b) { - mi_command_loop (2); + if (mi_suppress_notification.breakpoint) + return; + + if (b->number <= 0) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "breakpoint-created"); + mi_print_breakpoint_for_event (mi, b); + + gdb_flush (mi->event_channel); + } } +/* Emit notification about deleted breakpoint. */ + static void -mi3_command_loop (void) +mi_breakpoint_deleted (struct breakpoint *b) { - mi_command_loop (3); + if (mi_suppress_notification.breakpoint) + return; + + if (b->number <= 0) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "breakpoint-deleted,id=\"%d\"", + b->number); + + gdb_flush (mi->event_channel); + } } +/* Emit notification about modified breakpoint. */ + static void -mi_command_loop (int mi_version) +mi_breakpoint_modified (struct breakpoint *b) { -#if 0 - /* HACK: Force stdout/stderr to point at the console. This avoids - any potential side effects caused by legacy code that is still - using the TUI / fputs_unfiltered_hook */ - raw_stdout = stdio_fileopen (stdout); - /* Route normal output through the MIx */ - gdb_stdout = mi_console_file_new (raw_stdout, "~", '"'); - /* Route error and log output through the MI */ - gdb_stderr = mi_console_file_new (raw_stdout, "&", '"'); - gdb_stdlog = gdb_stderr; - /* Route target output through the MI. */ - gdb_stdtarg = mi_console_file_new (raw_stdout, "@", '"'); - /* HACK: Poke the ui_out table directly. Should we be creating a - mi_out object wired up to the above gdb_stdout / gdb_stderr? */ - uiout = mi_out_new (mi_version); - /* HACK: Override any other interpreter hooks. We need to create a - real event table and pass in that. */ - init_ui_hook = 0; - /* command_loop_hook = 0; */ - print_frame_info_listing_hook = 0; - query_hook = 0; - warning_hook = 0; - create_breakpoint_hook = 0; - delete_breakpoint_hook = 0; - modify_breakpoint_hook = 0; - interactive_hook = 0; - registers_changed_hook = 0; - readline_begin_hook = 0; - readline_hook = 0; - readline_end_hook = 0; - register_changed_hook = 0; - memory_changed_hook = 0; - context_hook = 0; - target_wait_hook = 0; - call_command_hook = 0; - error_hook = 0; - error_begin_hook = 0; - show_load_progress = mi_load_progress; -#endif - /* Turn off 8 bit strings in quoted output. Any character with the - high bit set is printed using C's octal format. */ - sevenbit_strings = 1; - /* Tell the world that we're alive */ - fputs_unfiltered ("(gdb) \n", raw_stdout); - gdb_flush (raw_stdout); - if (!event_loop_p) - simplified_command_loop (mi_input, mi_execute_command); + if (mi_suppress_notification.breakpoint) + return; + + if (b->number <= 0) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + fprintf_unfiltered (mi->event_channel, + "breakpoint-modified"); + mi_print_breakpoint_for_event (mi, b); + + gdb_flush (mi->event_channel); + } +} + +static void +mi_output_running (struct thread_info *thread) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + fprintf_unfiltered (mi->raw_stdout, + "*running,thread-id=\"%d\"\n", + thread->global_num); + } +} + +/* Return true if there are multiple inferiors loaded. This is used + for backwards compatibility -- if there's only one inferior, output + "all", otherwise, output each resumed thread individually. */ + +static bool +multiple_inferiors_p () +{ + int count = 0; + for (inferior *inf ATTRIBUTE_UNUSED : all_non_exited_inferiors ()) + { + count++; + if (count > 1) + return true; + } + + return false; +} + +static void +mi_on_resume_1 (struct mi_interp *mi, ptid_t ptid) +{ + /* To cater for older frontends, emit ^running, but do it only once + per each command. We do it here, since at this point we know + that the target was successfully resumed, and in non-async mode, + we won't return back to MI interpreter code until the target + is done running, so delaying the output of "^running" until then + will make it impossible for frontend to know what's going on. + + In future (MI3), we'll be outputting "^done" here. */ + if (!running_result_record_printed && mi_proceeded) + { + fprintf_unfiltered (mi->raw_stdout, "%s^running\n", + current_token ? current_token : ""); + } + + /* Backwards compatibility. If doing a wildcard resume and there's + only one inferior, output "all", otherwise, output each resumed + thread individually. */ + if ((ptid == minus_one_ptid || ptid.is_pid ()) + && !multiple_inferiors_p ()) + fprintf_unfiltered (mi->raw_stdout, "*running,thread-id=\"all\"\n"); else - start_event_loop (); + for (thread_info *tp : all_non_exited_threads (ptid)) + mi_output_running (tp); + + if (!running_result_record_printed && mi_proceeded) + { + running_result_record_printed = 1; + /* This is what gdb used to do historically -- printing prompt + even if it cannot actually accept any input. This will be + surely removed for MI3, and may be removed even earlier. */ + if (current_ui->prompt_state == PROMPT_BLOCKED) + fputs_unfiltered ("(gdb) \n", mi->raw_stdout); + } + gdb_flush (mi->raw_stdout); } -static char * -mi_input (char *buf) +static void +mi_on_resume (ptid_t ptid) { - return gdb_readline (NULL); + struct thread_info *tp = NULL; + + if (ptid == minus_one_ptid || ptid.is_pid ()) + tp = inferior_thread (); + else + tp = find_thread_ptid (ptid); + + /* Suppress output while calling an inferior function. */ + if (tp->control.in_infcall) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + + if (mi == NULL) + continue; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + mi_on_resume_1 (mi, ptid); + } } -extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */ +/* See mi-interp.h. */ void -_initialize_mi_interp (void) +mi_output_solib_attribs (ui_out *uiout, struct so_list *solib) { - static const struct interp_procs procs = - { - mi_interpreter_init, /* init_proc */ - mi_interpreter_resume, /* resume_proc */ - mi_interpreter_suspend, /* suspend_proc */ - mi_interpreter_exec, /* exec_proc */ - mi_interpreter_prompt_p /* prompt_proc_p */ - }; + struct gdbarch *gdbarch = target_gdbarch (); + + uiout->field_string ("id", solib->so_original_name); + uiout->field_string ("target-name", solib->so_original_name); + uiout->field_string ("host-name", solib->so_name); + uiout->field_int ("symbols-loaded", solib->symbols_loaded); + if (!gdbarch_has_global_solist (target_gdbarch ())) + uiout->field_fmt ("thread-group", "i%d", current_inferior ()->num); + + ui_out_emit_list list_emitter (uiout, "ranges"); + ui_out_emit_tuple tuple_emitter (uiout, NULL); + if (solib->addr_high != 0) + { + uiout->field_core_addr ("from", gdbarch, solib->addr_low); + uiout->field_core_addr ("to", gdbarch, solib->addr_high); + } +} + +static void +mi_solib_loaded (struct so_list *solib) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *uiout; + + if (mi == NULL) + continue; + + uiout = top_level_interpreter ()->interp_ui_out (); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "library-loaded"); + + uiout->redirect (mi->event_channel); + mi_output_solib_attribs (uiout, solib); + + uiout->redirect (NULL); + + gdb_flush (mi->event_channel); + } +} + +static void +mi_solib_unloaded (struct so_list *solib) +{ + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *uiout; + + if (mi == NULL) + continue; + + uiout = top_level_interpreter ()->interp_ui_out (); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "library-unloaded"); + + uiout->redirect (mi->event_channel); + + uiout->field_string ("id", solib->so_original_name); + uiout->field_string ("target-name", solib->so_original_name); + uiout->field_string ("host-name", solib->so_name); + if (!gdbarch_has_global_solist (target_gdbarch ())) + { + uiout->field_fmt ("thread-group", "i%d", current_inferior ()->num); + } + + uiout->redirect (NULL); + + gdb_flush (mi->event_channel); + } +} + +/* Emit notification about the command parameter change. */ + +static void +mi_command_param_changed (const char *param, const char *value) +{ + if (mi_suppress_notification.cmd_param_changed) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *mi_uiout; + + if (mi == NULL) + continue; + + mi_uiout = top_level_interpreter ()->interp_ui_out (); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "cmd-param-changed"); + + mi_uiout->redirect (mi->event_channel); + + mi_uiout->field_string ("param", param); + mi_uiout->field_string ("value", value); + + mi_uiout->redirect (NULL); + + gdb_flush (mi->event_channel); + } +} + +/* Emit notification about the target memory change. */ + +static void +mi_memory_changed (struct inferior *inferior, CORE_ADDR memaddr, + ssize_t len, const bfd_byte *myaddr) +{ + if (mi_suppress_notification.memory) + return; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *mi_uiout; + struct obj_section *sec; + + if (mi == NULL) + continue; + + mi_uiout = top_level_interpreter ()->interp_ui_out (); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, "memory-changed"); + + mi_uiout->redirect (mi->event_channel); + + mi_uiout->field_fmt ("thread-group", "i%d", inferior->num); + mi_uiout->field_core_addr ("addr", target_gdbarch (), memaddr); + mi_uiout->field_fmt ("len", "%s", hex_string (len)); + + /* Append 'type=code' into notification if MEMADDR falls in the range of + sections contain code. */ + sec = find_pc_section (memaddr); + if (sec != NULL && sec->objfile != NULL) + { + flagword flags = bfd_get_section_flags (sec->objfile->obfd, + sec->the_bfd_section); + + if (flags & SEC_CODE) + mi_uiout->field_string ("type", "code"); + } + + mi_uiout->redirect (NULL); + + gdb_flush (mi->event_channel); + } +} + +/* Emit an event when the selection context (inferior, thread, frame) + changed. */ + +static void +mi_user_selected_context_changed (user_selected_what selection) +{ + struct thread_info *tp; + + /* Don't send an event if we're responding to an MI command. */ + if (mi_suppress_notification.user_selected_context) + return; + + if (inferior_ptid != null_ptid) + tp = inferior_thread (); + else + tp = NULL; + + SWITCH_THRU_ALL_UIS () + { + struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui_out *mi_uiout; + + if (mi == NULL) + continue; + + mi_uiout = top_level_interpreter ()->interp_ui_out (); + + mi_uiout->redirect (mi->event_channel); + ui_out_redirect_pop redirect_popper (mi_uiout); + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + if (selection & USER_SELECTED_INFERIOR) + print_selected_inferior (mi->cli_uiout); + + if (tp != NULL + && (selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))) + { + print_selected_thread_frame (mi->cli_uiout, selection); + + fprintf_unfiltered (mi->event_channel, + "thread-selected,id=\"%d\"", + tp->global_num); + + if (tp->state != THREAD_RUNNING) + { + if (has_stack_frames ()) + print_stack_frame_to_uiout (mi_uiout, get_selected_frame (NULL), + 1, SRC_AND_LOC, 1); + } + } + + gdb_flush (mi->event_channel); + } +} + +static int +report_initial_inferior (struct inferior *inf, void *closure) +{ + /* This function is called from mi_interpreter_init, and since + mi_inferior_added assumes that inferior is fully initialized + and top_level_interpreter_data is set, we cannot call + it here. */ + struct mi_interp *mi = (struct mi_interp *) closure; + + target_terminal::scoped_restore_terminal_state term_state; + target_terminal::ours_for_output (); + + fprintf_unfiltered (mi->event_channel, + "thread-group-added,id=\"i%d\"", + inf->num); + gdb_flush (mi->event_channel); + + return 0; +} + +ui_out * +mi_interp::interp_ui_out () +{ + return this->mi_uiout; +} + +/* Do MI-specific logging actions; save raw_stdout, and change all + the consoles to use the supplied ui-file(s). */ + +void +mi_interp::set_logging (ui_file_up logfile, bool logging_redirect, + bool debug_redirect) +{ + struct mi_interp *mi = this; + + if (logfile != NULL) + { + mi->saved_raw_stdout = mi->raw_stdout; + + /* If something is being redirected, then grab logfile. */ + ui_file *logfile_p = nullptr; + if (logging_redirect || debug_redirect) + { + logfile_p = logfile.get (); + mi->saved_raw_file_to_delete = logfile_p; + } + + /* If something is not being redirected, then a tee containing both the + logfile and stdout. */ + ui_file *tee = nullptr; + if (!logging_redirect || !debug_redirect) + { + tee = new tee_file (mi->raw_stdout, std::move (logfile)); + mi->saved_raw_file_to_delete = tee; + } + + mi->raw_stdout = logging_redirect ? logfile_p : tee; + mi->raw_stdlog = debug_redirect ? logfile_p : tee; + } + else + { + delete mi->saved_raw_file_to_delete; + mi->raw_stdout = mi->saved_raw_stdout; + mi->saved_raw_stdout = nullptr; + mi->saved_raw_file_to_delete = nullptr; + } + + mi->out->set_raw (mi->raw_stdout); + mi->err->set_raw (mi->raw_stdout); + mi->log->set_raw (mi->raw_stdout); + mi->targ->set_raw (mi->raw_stdout); + mi->event_channel->set_raw (mi->raw_stdout); +} + +/* Factory for MI interpreters. */ + +static struct interp * +mi_interp_factory (const char *name) +{ + return new mi_interp (name); +} + +void +_initialize_mi_interp (void) +{ /* The various interpreter levels. */ - interp_add (interp_new (INTERP_MI1, NULL, mi_out_new (1), &procs)); - interp_add (interp_new (INTERP_MI2, NULL, mi_out_new (2), &procs)); - interp_add (interp_new (INTERP_MI3, NULL, mi_out_new (3), &procs)); + interp_factory_register (INTERP_MI1, mi_interp_factory); + interp_factory_register (INTERP_MI2, mi_interp_factory); + interp_factory_register (INTERP_MI3, mi_interp_factory); + interp_factory_register (INTERP_MI, mi_interp_factory); - /* "mi" selects the most recent released version. "mi2" was - released as part of GDB 6.0. */ - interp_add (interp_new (INTERP_MI, NULL, mi_out_new (2), &procs)); + gdb::observers::signal_received.attach (mi_on_signal_received); + gdb::observers::end_stepping_range.attach (mi_on_end_stepping_range); + gdb::observers::signal_exited.attach (mi_on_signal_exited); + gdb::observers::exited.attach (mi_on_exited); + gdb::observers::no_history.attach (mi_on_no_history); + gdb::observers::new_thread.attach (mi_new_thread); + gdb::observers::thread_exit.attach (mi_thread_exit); + gdb::observers::inferior_added.attach (mi_inferior_added); + gdb::observers::inferior_appeared.attach (mi_inferior_appeared); + gdb::observers::inferior_exit.attach (mi_inferior_exit); + gdb::observers::inferior_removed.attach (mi_inferior_removed); + gdb::observers::record_changed.attach (mi_record_changed); + gdb::observers::normal_stop.attach (mi_on_normal_stop); + gdb::observers::target_resumed.attach (mi_on_resume); + gdb::observers::solib_loaded.attach (mi_solib_loaded); + gdb::observers::solib_unloaded.attach (mi_solib_unloaded); + gdb::observers::about_to_proceed.attach (mi_about_to_proceed); + gdb::observers::traceframe_changed.attach (mi_traceframe_changed); + gdb::observers::tsv_created.attach (mi_tsv_created); + gdb::observers::tsv_deleted.attach (mi_tsv_deleted); + gdb::observers::tsv_modified.attach (mi_tsv_modified); + gdb::observers::breakpoint_created.attach (mi_breakpoint_created); + gdb::observers::breakpoint_deleted.attach (mi_breakpoint_deleted); + gdb::observers::breakpoint_modified.attach (mi_breakpoint_modified); + gdb::observers::command_param_changed.attach (mi_command_param_changed); + gdb::observers::memory_changed.attach (mi_memory_changed); + gdb::observers::sync_execution_done.attach (mi_on_sync_execution_done); + gdb::observers::user_selected_context_changed.attach + (mi_user_selected_context_changed); }