/* MI Interpreter Definitions and Commands for GDB, the GNU debugger.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
-#include <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 "exceptions.h"
#include "mi-main.h"
#include "mi-cmds.h"
#include "mi-out.h"
#include "gdb.h"
#include "objfiles.h"
#include "tracepoint.h"
+#include "cli-out.h"
+#include "thread-fsm.h"
/* These are the interpreter setup, etc. functions for the MI
interpreter. */
static void mi_insert_notify_hooks (void);
static void mi_remove_notify_hooks (void);
+
+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);
static void mi_new_thread (struct thread_info *t);
static void mi_thread_exit (struct thread_info *t, int silent);
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);
static int report_initial_inferior (struct inferior *inf, void *closure);
else
gdb_assert_not_reached ("unhandled MI version");
- mi->uiout = mi_out_new (mi_version);
+ mi->mi_uiout = mi_out_new (mi_version);
+ mi->cli_uiout = cli_out_new (mi->out);
+
+ /* There are installed even if MI is not the top level interpreter.
+ The callbacks themselves decide whether to be skipped. */
+ observer_attach_signal_received (mi_on_signal_received);
+ observer_attach_end_stepping_range (mi_on_end_stepping_range);
+ observer_attach_signal_exited (mi_on_signal_exited);
+ observer_attach_exited (mi_on_exited);
+ observer_attach_no_history (mi_on_no_history);
if (top_level)
{
observer_attach_breakpoint_modified (mi_breakpoint_modified);
observer_attach_command_param_changed (mi_command_param_changed);
observer_attach_memory_changed (mi_memory_changed);
+ observer_attach_sync_execution_done (mi_on_sync_execution_done);
/* The initial inferior is created before this function is
called, so we need to report it explicitly. Use iteration in
static int
mi_interpreter_resume (void *data)
{
- struct mi_interp *mi = data;
+ struct mi_interp *mi = (struct mi_interp *) data;
/* As per hack note in mi_interpreter_init, swap in the output
channels... */
return exception_none;
}
-/* Never display the default GDB prompt in MI case. */
-
-static int
-mi_interpreter_prompt_p (void *data)
-{
- return 0;
-}
-
void
mi_cmd_interpreter_exec (char *command, char **argv, int argc)
{
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
mi_execute_command (cmd, stdin == instream);
}
+/* Observer for the synchronous_command_done notification. */
+
+static void
+mi_on_sync_execution_done (void)
+{
+ /* If MI is sync, then output the MI prompt now, indicating we're
+ ready for further input. */
+ if (!mi_async_p ())
+ {
+ fputs_unfiltered ("(gdb) \n", raw_stdout);
+ gdb_flush (raw_stdout);
+ }
+}
+
/* mi_execute_command_wrapper wrapper suitable for INPUT_HANDLER. */
static void
{
mi_execute_command_wrapper (cmd);
- fputs_unfiltered ("(gdb) \n", raw_stdout);
- gdb_flush (raw_stdout);
+ /* 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 (!sync_execution)
+ {
+ fputs_unfiltered ("(gdb) \n", raw_stdout);
+ gdb_flush (raw_stdout);
+ }
}
static void
static void
mi_new_thread (struct thread_info *t)
{
- struct mi_interp *mi = top_level_interpreter_data ();
- struct inferior *inf = find_inferior_pid (ptid_get_pid (t->ptid));
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
+ struct inferior *inf = find_inferior_ptid (t->ptid);
gdb_assert (inf);
{
struct mi_interp *mi;
struct inferior *inf;
+ struct cleanup *old_chain;
if (silent)
return;
- inf = find_inferior_pid (ptid_get_pid (t->ptid));
+ inf = find_inferior_ptid (t->ptid);
- mi = top_level_interpreter_data ();
+ mi = (struct mi_interp *) top_level_interpreter_data ();
+ old_chain = make_cleanup_restore_target_terminal ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-exited,id=\"%d\",group-id=\"i%d\"",
t->num, inf->num);
gdb_flush (mi->event_channel);
+
+ do_cleanups (old_chain);
}
/* Emit notification on changing the state of record. */
static void
mi_record_changed (struct inferior *inferior, int started)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
fprintf_unfiltered (mi->event_channel, "record-%s,thread-group=\"i%d\"",
started ? "started" : "stopped", inferior->num);
static void
mi_inferior_added (struct inferior *inf)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
static void
mi_inferior_appeared (struct inferior *inf)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
static void
mi_inferior_exit (struct inferior *inf)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
if (inf->has_exit_code)
static void
mi_inferior_removed (struct inferior *inf)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
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 interp *
+find_mi_interpreter (void)
+{
+ struct interp *interp;
+
+ interp = top_level_interpreter ();
+ if (ui_out_is_mi_like_p (interp_ui_out (interp)))
+ return interp;
+
+ interp = command_interp ();
+ if (ui_out_is_mi_like_p (interp_ui_out (interp)))
+ return interp;
+
+ return NULL;
+}
+
+/* Return the MI_INTERP structure of the active MI interpreter.
+ Returns NULL if MI is not active. */
+
+static struct mi_interp *
+mi_interp_data (void)
+{
+ struct interp *interp = find_mi_interpreter ();
+
+ if (interp != NULL)
+ return (struct mi_interp *) interp_data (interp);
+ return NULL;
+}
+
+/* Observers for several run control events that print why the
+ inferior has stopped to both the 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)
+{
+ struct mi_interp *mi = mi_interp_data ();
+
+ if (mi == NULL)
+ return;
+
+ 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)
+{
+ struct mi_interp *mi = mi_interp_data ();
+
+ if (mi == NULL)
+ return;
+
+ 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)
+{
+ struct mi_interp *mi = mi_interp_data ();
+
+ if (mi == NULL)
+ return;
+
+ 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)
+{
+ struct mi_interp *mi = mi_interp_data ();
+
+ if (mi == NULL)
+ return;
+
+ 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)
+{
+ struct mi_interp *mi = mi_interp_data ();
+
+ if (mi == NULL)
+ return;
+
+ print_no_history_reason (mi->mi_uiout);
+ print_no_history_reason (mi->cli_uiout);
+}
+
static void
mi_on_normal_stop (struct bpstats *bs, int print_frame)
{
if (print_frame)
{
+ struct thread_info *tp;
int core;
- if (current_uiout != mi_uiout)
- {
- /* The normal_stop function has printed frame information
- into CLI uiout, or some other non-MI uiout. There's no
- way we can extract proper fields from random uiout
- object, so we print the frame again. In practice, this
- can only happen when running a CLI command in MI. */
- struct ui_out *saved_uiout = current_uiout;
- struct target_waitstatus last;
- ptid_t last_ptid;
+ tp = inferior_thread ();
- current_uiout = mi_uiout;
+ if (tp->thread_fsm != NULL
+ && thread_fsm_finished_p (tp->thread_fsm))
+ {
+ enum async_reply_reason reason;
- get_last_target_status (&last_ptid, &last);
- print_stop_event (&last);
+ reason = thread_fsm_async_reply_reason (tp->thread_fsm);
+ ui_out_field_string (mi_uiout, "reason",
+ async_reason_lookup (reason));
+ }
+ print_stop_event (mi_uiout);
+
+ /* Breakpoint hits should always be mirrored to the console.
+ Deciding what to mirror to the console wrt to breakpoints and
+ random stops gets messy real fast. E.g., say "s" trips on a
+ breakpoint. We'd clearly want to mirror the event to the
+ console in this case. But what about more complicated cases
+ like "s&; thread n; s&", and one of those steps spawning a
+ new thread, and that thread hitting a breakpoint? It's
+ impossible in general to track whether the thread had any
+ relation to the commands that had been executed. So we just
+ simplify and always mirror breakpoints and random events to
+ the console.
+
+ OTOH, we should print the source line to the console when
+ stepping or other similar commands, iff the step was started
+ by a console command, but not if it was started with
+ -exec-step or similar. */
+ if ((bpstat_what (tp->control.stop_bpstat).main_action
+ == BPSTAT_WHAT_STOP_NOISY)
+ || !(tp->thread_fsm != NULL
+ && thread_fsm_finished_p (tp->thread_fsm))
+ || (tp->control.command_interp != NULL
+ && tp->control.command_interp != top_level_interpreter ()))
+ {
+ struct mi_interp *mi
+ = (struct mi_interp *) top_level_interpreter_data ();
- current_uiout = saved_uiout;
+ print_stop_event (mi->cli_uiout);
}
ui_out_field_int (mi_uiout, "thread-id",
static void
mi_traceframe_changed (int tfnum, int tpnum)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
if (mi_suppress_notification.traceframe)
return;
static void
mi_tsv_created (const struct trace_state_variable *tsv)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
static void
mi_tsv_deleted (const struct trace_state_variable *tsv)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
target_terminal_ours ();
static void
mi_tsv_modified (const struct trace_state_variable *tsv)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
target_terminal_ours ();
static void
mi_breakpoint_created (struct breakpoint *b)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
- volatile struct gdb_exception e;
if (mi_suppress_notification.breakpoint)
return;
breakpoint_created notifications. So, we use
ui_out_redirect. */
ui_out_redirect (mi_uiout, mi->event_channel);
- TRY_CATCH (e, RETURN_MASK_ERROR)
- gdb_breakpoint_query (mi_uiout, b->number, NULL);
+ TRY
+ {
+ gdb_breakpoint_query (mi_uiout, b->number, NULL);
+ }
+ CATCH (e, RETURN_MASK_ERROR)
+ {
+ }
+ END_CATCH
+
ui_out_redirect (mi_uiout, NULL);
gdb_flush (mi->event_channel);
static void
mi_breakpoint_deleted (struct breakpoint *b)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
if (mi_suppress_notification.breakpoint)
return;
static void
mi_breakpoint_modified (struct breakpoint *b)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
- volatile struct gdb_exception e;
if (mi_suppress_notification.breakpoint)
return;
breakpoint_created notifications. So, we use
ui_out_redirect. */
ui_out_redirect (mi_uiout, mi->event_channel);
- TRY_CATCH (e, RETURN_MASK_ERROR)
- gdb_breakpoint_query (mi_uiout, b->number, NULL);
+ TRY
+ {
+ gdb_breakpoint_query (mi_uiout, b->number, NULL);
+ }
+ CATCH (e, RETURN_MASK_ERROR)
+ {
+ }
+ END_CATCH
+
ui_out_redirect (mi_uiout, NULL);
gdb_flush (mi->event_channel);
static int
mi_output_running_pid (struct thread_info *info, void *arg)
{
- ptid_t *ptid = arg;
+ ptid_t *ptid = (ptid_t *) arg;
if (ptid_get_pid (*ptid) == ptid_get_pid (info->ptid))
fprintf_unfiltered (raw_stdout,
{
if (inf->pid != 0)
{
- int *count_p = arg;
+ int *count_p = (int *) arg;
(*count_p)++;
}
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 earler. */
- /* FIXME: review the use of target_is_async_p here -- is that
- what we want? */
- if (!target_is_async_p ())
+ for MI3, and may be removed even earlier. SYNC_EXECUTION is
+ checked here because we only need to emit a prompt if a
+ synchronous command was issued when the target is async. */
+ if (!target_is_async_p () || sync_execution)
fputs_unfiltered ("(gdb) \n", raw_stdout);
}
gdb_flush (raw_stdout);
static void
mi_solib_loaded (struct so_list *solib)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
+ struct ui_out *uiout = interp_ui_out (top_level_interpreter ());
target_terminal_ours ();
- if (gdbarch_has_global_solist (target_gdbarch ()))
- fprintf_unfiltered (mi->event_channel,
- "library-loaded,id=\"%s\",target-name=\"%s\","
- "host-name=\"%s\",symbols-loaded=\"%d\"",
- solib->so_original_name, solib->so_original_name,
- solib->so_name, solib->symbols_loaded);
- else
- fprintf_unfiltered (mi->event_channel,
- "library-loaded,id=\"%s\",target-name=\"%s\","
- "host-name=\"%s\",symbols-loaded=\"%d\","
- "thread-group=\"i%d\"",
- solib->so_original_name, solib->so_original_name,
- solib->so_name, solib->symbols_loaded,
- current_inferior ()->num);
+
+ fprintf_unfiltered (mi->event_channel, "library-loaded");
+
+ ui_out_redirect (uiout, mi->event_channel);
+
+ ui_out_field_string (uiout, "id", solib->so_original_name);
+ ui_out_field_string (uiout, "target-name", solib->so_original_name);
+ ui_out_field_string (uiout, "host-name", solib->so_name);
+ ui_out_field_int (uiout, "symbols-loaded", solib->symbols_loaded);
+ if (!gdbarch_has_global_solist (target_gdbarch ()))
+ {
+ ui_out_field_fmt (uiout, "thread-group", "i%d", current_inferior ()->num);
+ }
+
+ ui_out_redirect (uiout, NULL);
gdb_flush (mi->event_channel);
}
static void
mi_solib_unloaded (struct so_list *solib)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
+ struct ui_out *uiout = interp_ui_out (top_level_interpreter ());
target_terminal_ours ();
- if (gdbarch_has_global_solist (target_gdbarch ()))
- fprintf_unfiltered (mi->event_channel,
- "library-unloaded,id=\"%s\",target-name=\"%s\","
- "host-name=\"%s\"",
- solib->so_original_name, solib->so_original_name,
- solib->so_name);
- else
- fprintf_unfiltered (mi->event_channel,
- "library-unloaded,id=\"%s\",target-name=\"%s\","
- "host-name=\"%s\",thread-group=\"i%d\"",
- solib->so_original_name, solib->so_original_name,
- solib->so_name, current_inferior ()->num);
+
+ fprintf_unfiltered (mi->event_channel, "library-unloaded");
+
+ ui_out_redirect (uiout, mi->event_channel);
+
+ ui_out_field_string (uiout, "id", solib->so_original_name);
+ ui_out_field_string (uiout, "target-name", solib->so_original_name);
+ ui_out_field_string (uiout, "host-name", solib->so_name);
+ if (!gdbarch_has_global_solist (target_gdbarch ()))
+ {
+ ui_out_field_fmt (uiout, "thread-group", "i%d", current_inferior ()->num);
+ }
+
+ ui_out_redirect (uiout, NULL);
gdb_flush (mi->event_channel);
}
static void
mi_command_param_changed (const char *param, const char *value)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
if (mi_suppress_notification.cmd_param_changed)
mi_memory_changed (struct inferior *inferior, CORE_ADDR memaddr,
ssize_t len, const bfd_byte *myaddr)
{
- struct mi_interp *mi = top_level_interpreter_data ();
+ struct mi_interp *mi = (struct mi_interp *) top_level_interpreter_data ();
struct ui_out *mi_uiout = interp_ui_out (top_level_interpreter ());
struct obj_section *sec;
ui_out_field_fmt (mi_uiout, "thread-group", "i%d", inferior->num);
ui_out_field_core_addr (mi_uiout, "addr", target_gdbarch (), memaddr);
- ui_out_field_fmt (mi_uiout, "len", "0x%zx", len);
+ ui_out_field_fmt (mi_uiout, "len", "%s", hex_string (len));
/* Append 'type=code' into notification if MEMADDR falls in the range of
sections contain code. */
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 = closure;
+ struct mi_interp *mi = (struct mi_interp *) closure;
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
static struct ui_out *
mi_ui_out (struct interp *interp)
{
- struct mi_interp *mi = interp_data (interp);
+ struct mi_interp *mi = (struct mi_interp *) interp_data (interp);
- return mi->uiout;
+ return mi->mi_uiout;
}
/* Save the original value of raw_stdout here when logging, so we can
mi_set_logging (struct interp *interp, int start_log,
struct ui_file *out, struct ui_file *logfile)
{
- struct mi_interp *mi = interp_data (interp);
+ struct mi_interp *mi = (struct mi_interp *) interp_data (interp);
if (!mi)
return 0;
mi_interpreter_resume, /* resume_proc */
mi_interpreter_suspend, /* suspend_proc */
mi_interpreter_exec, /* exec_proc */
- mi_interpreter_prompt_p, /* prompt_proc_p */
mi_ui_out, /* ui_out_proc */
mi_set_logging, /* set_logging_proc */
mi_command_loop /* command_loop_proc */