/* MI Command Set.
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Cygnus Solutions (a Red Hat company).
/* Work in progress. */
#include "defs.h"
+#include "arch-utils.h"
#include "target.h"
#include "inferior.h"
#include "gdb_string.h"
#include "gdb.h"
#include "frame.h"
#include "mi-main.h"
+#include "mi-common.h"
+#include "language.h"
+#include "valprint.h"
+#include "inferior.h"
+#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
FROM_TTY = 0
};
-/* Enumerations of the actions that may result from calling
- captured_mi_execute_command. */
-
-enum captured_mi_execute_command_actions
- {
- EXECUTE_COMMAND_DISPLAY_PROMPT,
- EXECUTE_COMMAND_SUPPRESS_PROMPT
- };
-
-/* This structure is used to pass information from captured_mi_execute_command
- to mi_execute_command. */
-struct captured_mi_execute_command_args
-{
- /* This return result of the MI command (output). */
- enum mi_cmd_result rc;
-
- /* What action to perform when the call is finished (output). */
- enum captured_mi_execute_command_actions action;
-
- /* The command context to be executed (input). */
- struct mi_parse *command;
-};
-
int mi_debug_p;
struct ui_file *raw_stdout;
static int do_timings = 0;
-/* The token of the last asynchronous command. */
-static char *last_async_command;
-static char *previous_async_command;
+char *current_token;
+/* Few commands would like to know if options like --thread-group
+ were explicitly specified. This variable keeps the current
+ parsed command including all option, and make it possible. */
+static struct mi_parse *current_context;
+
+int running_result_record_printed = 1;
+
+/* Flag indicating that the target has proceeded since the last
+ command was issued. */
+int mi_proceeded;
extern void _initialize_mi_main (void);
-static enum mi_cmd_result mi_cmd_execute (struct mi_parse *parse);
+static void mi_cmd_execute (struct mi_parse *parse);
static void mi_execute_cli_command (const char *cmd, int args_p,
const char *args);
-static enum mi_cmd_result mi_execute_async_cli_command (char *mi, char *args, int from_tty);
-
-static void mi_exec_async_cli_cmd_continuation (struct continuation_arg *arg);
-
+static void mi_execute_async_cli_command (char *cli_command,
+ char **argv, int argc);
static int register_changed_p (int regnum, struct regcache *,
struct regcache *);
-static void get_register (int regnum, int format);
+static void get_register (struct frame_info *, int regnum, int format);
/* Command implementations. FIXME: Is this libgdb? No. This is the MI
layer that calls libgdb. Any operation used in the below should be
static void print_diff_now (struct mi_timestamp *start);
static void print_diff (struct mi_timestamp *start, struct mi_timestamp *end);
-enum mi_cmd_result
+void
mi_cmd_gdb_exit (char *command, char **argv, int argc)
{
/* We have to print everything right here because we never return. */
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
+ if (current_token)
+ fputs_unfiltered (current_token, raw_stdout);
fputs_unfiltered ("^exit\n", raw_stdout);
mi_out_put (uiout, raw_stdout);
+ gdb_flush (raw_stdout);
/* FIXME: The function called is not yet a formal libgdb function. */
quit_force (NULL, FROM_TTY);
- return MI_CMD_DONE;
-}
-
-enum mi_cmd_result
-mi_cmd_exec_run (char *args, int from_tty)
-{
- /* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("run", args, from_tty);
-}
-
-enum mi_cmd_result
-mi_cmd_exec_next (char *args, int from_tty)
-{
- /* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("next", args, from_tty);
}
-enum mi_cmd_result
-mi_cmd_exec_next_instruction (char *args, int from_tty)
+void
+mi_cmd_exec_next (char *command, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("nexti", args, from_tty);
+ if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
+ mi_execute_async_cli_command ("reverse-next", argv + 1, argc - 1);
+ else
+ mi_execute_async_cli_command ("next", argv, argc);
}
-enum mi_cmd_result
-mi_cmd_exec_step (char *args, int from_tty)
+void
+mi_cmd_exec_next_instruction (char *command, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("step", args, from_tty);
+ if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
+ mi_execute_async_cli_command ("reverse-nexti", argv + 1, argc - 1);
+ else
+ mi_execute_async_cli_command ("nexti", argv, argc);
}
-enum mi_cmd_result
-mi_cmd_exec_step_instruction (char *args, int from_tty)
+void
+mi_cmd_exec_step (char *command, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("stepi", args, from_tty);
+ if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
+ mi_execute_async_cli_command ("reverse-step", argv + 1, argc - 1);
+ else
+ mi_execute_async_cli_command ("step", argv, argc);
}
-enum mi_cmd_result
-mi_cmd_exec_finish (char *args, int from_tty)
+void
+mi_cmd_exec_step_instruction (char *command, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("finish", args, from_tty);
+ if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
+ mi_execute_async_cli_command ("reverse-stepi", argv + 1, argc - 1);
+ else
+ mi_execute_async_cli_command ("stepi", argv, argc);
}
-enum mi_cmd_result
-mi_cmd_exec_until (char *args, int from_tty)
+void
+mi_cmd_exec_finish (char *command, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("until", args, from_tty);
+ if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
+ mi_execute_async_cli_command ("reverse-finish", argv + 1, argc - 1);
+ else
+ mi_execute_async_cli_command ("finish", argv, argc);
}
-enum mi_cmd_result
-mi_cmd_exec_return (char *args, int from_tty)
+void
+mi_cmd_exec_return (char *command, char **argv, int argc)
{
/* This command doesn't really execute the target, it just pops the
specified number of frames. */
- if (*args)
+ if (argc)
/* Call return_command with from_tty argument equal to 0 so as to
avoid being queried. */
- return_command (args, 0);
+ return_command (*argv, 0);
else
/* Call return_command with from_tty argument equal to 0 so as to
avoid being queried. */
/* Because we have called return_command with from_tty = 0, we need
to print the frame here. */
print_stack_frame (get_selected_frame (NULL), 1, LOC_AND_ADDRESS);
-
- return MI_CMD_DONE;
}
-enum mi_cmd_result
-mi_cmd_exec_continue (char *args, int from_tty)
+void
+mi_cmd_exec_jump (char *args, char **argv, int argc)
{
/* FIXME: Should call a libgdb function, not a cli wrapper. */
- return mi_execute_async_cli_command ("continue", args, from_tty);
+ mi_execute_async_cli_command ("jump", argv, argc);
+}
+
+static void
+proceed_thread (struct thread_info *thread, int pid)
+{
+ if (!is_stopped (thread->ptid))
+ return;
+
+ if (pid != 0 && PIDGET (thread->ptid) != pid)
+ return;
+
+ switch_to_thread (thread->ptid);
+ clear_proceed_status ();
+ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+}
+
+
+static int
+proceed_thread_callback (struct thread_info *thread, void *arg)
+{
+ int pid = *(int *)arg;
+ proceed_thread (thread, pid);
+ return 0;
+}
+
+static void
+exec_continue (char **argv, int argc)
+{
+ if (non_stop)
+ {
+ /* In non-stop mode, 'resume' always resumes a single thread. Therefore,
+ to resume all threads of the current inferior, or all threads in all
+ inferiors, we need to iterate over threads.
+
+ See comment on infcmd.c:proceed_thread_callback for rationale. */
+ if (current_context->all || current_context->thread_group != -1)
+ {
+ int pid = 0;
+ struct cleanup *back_to = make_cleanup_restore_current_thread ();
+
+ if (!current_context->all)
+ {
+ struct inferior *inf = find_inferior_id (current_context->thread_group);
+ pid = inf->pid;
+ }
+ iterate_over_threads (proceed_thread_callback, &pid);
+ do_cleanups (back_to);
+ }
+ else
+ {
+ continue_1 (0);
+ }
+ }
+ else
+ {
+ struct cleanup *back_to = make_cleanup_restore_integer (&sched_multi);
+ if (current_context->all)
+ {
+ sched_multi = 1;
+ continue_1 (0);
+ }
+ else
+ {
+ /* In all-stop mode, -exec-continue traditionally resumed either
+ all threads, or one thread, depending on the 'scheduler-locking'
+ variable. Let's continue to do the same. */
+ continue_1 (1);
+ }
+ do_cleanups (back_to);
+ }
+}
+
+static void
+exec_direction_forward (void *notused)
+{
+ execution_direction = EXEC_FORWARD;
+}
+
+static void
+exec_reverse_continue (char **argv, int argc)
+{
+ enum exec_direction_kind dir = execution_direction;
+ struct cleanup *old_chain;
+
+ if (dir == EXEC_ERROR)
+ error (_("Target %s does not support this command."), target_shortname);
+
+ if (dir == EXEC_REVERSE)
+ error (_("Already in reverse mode."));
+
+ if (!target_can_execute_reverse)
+ error (_("Target %s does not support this command."), target_shortname);
+
+ old_chain = make_cleanup (exec_direction_forward, NULL);
+ execution_direction = EXEC_REVERSE;
+ exec_continue (argv, argc);
+ do_cleanups (old_chain);
+}
+
+void
+mi_cmd_exec_continue (char *command, char **argv, int argc)
+{
+ if (argc > 0 && strcmp (argv[0], "--reverse") == 0)
+ exec_reverse_continue (argv + 1, argc - 1);
+ else
+ exec_continue (argv, argc);
+}
+
+static int
+interrupt_thread_callback (struct thread_info *thread, void *arg)
+{
+ int pid = *(int *)arg;
+
+ if (!is_running (thread->ptid))
+ return 0;
+
+ if (PIDGET (thread->ptid) != pid)
+ return 0;
+
+ target_stop (thread->ptid);
+ return 0;
}
/* Interrupt the execution of the target. Note how we must play around
the result of the interrupt command, and the previous execution
token when the target finally stops. See comments in
mi_cmd_execute. */
-enum mi_cmd_result
-mi_cmd_exec_interrupt (char *args, int from_tty)
+void
+mi_cmd_exec_interrupt (char *command, char **argv, int argc)
{
- if (!target_executing)
- error ("mi_cmd_exec_interrupt: Inferior not executing.");
-
- interrupt_target_command (args, from_tty);
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- fputs_unfiltered ("^done", raw_stdout);
- xfree (last_async_command);
- if (previous_async_command)
- last_async_command = xstrdup (previous_async_command);
- xfree (previous_async_command);
- previous_async_command = NULL;
- mi_out_put (uiout, raw_stdout);
- mi_out_rewind (uiout);
- fputs_unfiltered ("\n", raw_stdout);
- return MI_CMD_QUIET;
+ /* In all-stop mode, everything stops, so we don't need to try
+ anything specific. */
+ if (!non_stop)
+ {
+ interrupt_target_1 (0);
+ return;
+ }
+
+ if (current_context->all)
+ {
+ /* This will interrupt all threads in all inferiors. */
+ interrupt_target_1 (1);
+ }
+ else if (current_context->thread_group != -1)
+ {
+ struct inferior *inf = find_inferior_id (current_context->thread_group);
+ iterate_over_threads (interrupt_thread_callback, &inf->pid);
+ }
+ else
+ {
+ /* Interrupt just the current thread -- either explicitly
+ specified via --thread or whatever was current before
+ MI command was sent. */
+ interrupt_target_1 (0);
+ }
}
-enum mi_cmd_result
+static int
+run_one_inferior (struct inferior *inf, void *arg)
+{
+ struct thread_info *tp = 0;
+
+ if (inf->pid != 0)
+ {
+ if (inf->pid != ptid_get_pid (inferior_ptid))
+ {
+ struct thread_info *tp;
+
+ tp = any_thread_of_process (inf->pid);
+ if (!tp)
+ error (_("Inferior has no threads."));
+
+ switch_to_thread (tp->ptid);
+ }
+ }
+ else
+ {
+ set_current_inferior (inf);
+ switch_to_thread (null_ptid);
+ set_current_program_space (inf->pspace);
+ }
+ mi_execute_cli_command ("run", target_can_async_p (),
+ target_can_async_p () ? "&" : NULL);
+ return 0;
+}
+
+void
+mi_cmd_exec_run (char *command, char **argv, int argc)
+{
+ if (current_context->all)
+ {
+ struct cleanup *back_to = save_current_space_and_thread ();
+ iterate_over_inferiors (run_one_inferior, NULL);
+ do_cleanups (back_to);
+ }
+ else
+ {
+ mi_execute_cli_command ("run", target_can_async_p (),
+ target_can_async_p () ? "&" : NULL);
+ }
+}
+
+
+static int
+find_thread_of_process (struct thread_info *ti, void *p)
+{
+ int pid = *(int *)p;
+ if (PIDGET (ti->ptid) == pid && !is_exited (ti->ptid))
+ return 1;
+
+ return 0;
+}
+
+void
+mi_cmd_target_detach (char *command, char **argv, int argc)
+{
+ if (argc != 0 && argc != 1)
+ error ("Usage: -target-detach [thread-group]");
+
+ if (argc == 1)
+ {
+ struct thread_info *tp;
+ char *end = argv[0];
+ int pid = strtol (argv[0], &end, 10);
+ if (*end != '\0')
+ error (_("Cannot parse thread group id '%s'"), argv[0]);
+
+ /* Pick any thread in the desired process. Current
+ target_detach deteches from the parent of inferior_ptid. */
+ tp = iterate_over_threads (find_thread_of_process, &pid);
+ if (!tp)
+ error (_("Thread group is empty"));
+
+ switch_to_thread (tp->ptid);
+ }
+
+ detach_command (NULL, 0);
+}
+
+void
mi_cmd_thread_select (char *command, char **argv, int argc)
{
enum gdb_rc rc;
make_cleanup (xfree, mi_error_message);
error ("%s", mi_error_message);
}
-
- return MI_CMD_DONE;
}
-enum mi_cmd_result
+void
mi_cmd_thread_list_ids (char *command, char **argv, int argc)
{
enum gdb_rc rc;
make_cleanup (xfree, mi_error_message);
error ("%s", mi_error_message);
}
-
- return MI_CMD_DONE;
}
-enum mi_cmd_result
+void
mi_cmd_thread_info (char *command, char **argv, int argc)
{
int thread = -1;
if (argc == 1)
thread = atoi (argv[0]);
- print_thread_info (uiout, thread);
- return MI_CMD_DONE;
+ print_thread_info (uiout, thread, -1);
+}
+
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
+static int
+print_one_inferior (struct inferior *inferior, void *xdata)
+{
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_empty (int, top_data->inferiors)
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
+ {
+ struct collect_cores_data data;
+ struct cleanup *back_to
+ = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ ui_out_field_fmt (uiout, "id", "i%d", inferior->num);
+ ui_out_field_string (uiout, "type", "process");
+ if (inferior->pid != 0)
+ ui_out_field_int (uiout, "pid", inferior->pid);
+
+ if (inferior->pspace->ebfd)
+ {
+ ui_out_field_string (uiout, "executable",
+ bfd_get_filename (inferior->pspace->ebfd));
+ }
+
+ data.cores = 0;
+ if (inferior->pid != 0)
+ {
+ data.pid = inferior->pid;
+ iterate_over_threads (collect_cores, &data);
+ }
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
+ do_cleanups (back_to);
+ }
+
+ return 0;
+}
+
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
+{
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
+
+ make_cleanup (xfree, cores);
+
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
+
+ do_cleanups (back_to);
+}
+
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
+
+static void
+do_nothing (splay_tree_key k)
+{
+}
+
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
}
-enum mi_cmd_result
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given pid.
+ This is assigned an initial value to avoid "may be used uninitialized"
+ warning from gcc. */
+ splay_tree tree = NULL;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
+
+ for (ix_items = 0;
+ VEC_iterate (osdata_item_s, threads->items,
+ ix_items, item);
+ ix_items++)
+ {
+ const char *pid = get_osdata_column (item, "pid");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ make_cleanup_ui_out_list_begin_end (uiout, "groups");
+
+ for (ix_items = 0;
+ VEC_iterate (osdata_item_s, data->items,
+ ix_items, item);
+ ix_items++)
+ {
+ struct cleanup *back_to;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
+
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ ui_out_field_fmt (uiout, "id", "%s", pid);
+ ui_out_field_string (uiout, "type", "process");
+ if (cmd)
+ ui_out_field_string (uiout, "description", cmd);
+ if (user)
+ ui_out_field_string (uiout, "user", user);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
+ }
+
+ do_cleanups (back_to);
+ }
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
+ {
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ int optind = 0;
+ char *optarg;
+
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ {
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ if (VEC_length (int, ids) > 1)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
+ if (!in_inferior_list (pid))
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
+ }
+ else
+ {
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
+ make_cleanup_ui_out_list_begin_end (uiout, "groups");
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
+ }
+
+ do_cleanups (back_to);
+}
+
+void
mi_cmd_data_list_register_names (char *command, char **argv, int argc)
{
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
int regnum, numregs;
int i;
struct cleanup *cleanup;
In this case, some entries of gdbarch_register_name will change depending
upon the particular processor being debugged. */
- numregs = gdbarch_num_regs (current_gdbarch)
- + gdbarch_num_pseudo_regs (current_gdbarch);
+ frame = get_selected_frame (NULL);
+ gdbarch = get_frame_arch (frame);
+ numregs = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
cleanup = make_cleanup_ui_out_list_begin_end (uiout, "register-names");
regnum < numregs;
regnum++)
{
- if (gdbarch_register_name (current_gdbarch, regnum) == NULL
- || *(gdbarch_register_name (current_gdbarch, regnum)) == '\0')
+ if (gdbarch_register_name (gdbarch, regnum) == NULL
+ || *(gdbarch_register_name (gdbarch, regnum)) == '\0')
ui_out_field_string (uiout, NULL, "");
else
ui_out_field_string (uiout, NULL,
- gdbarch_register_name
- (current_gdbarch, regnum));
+ gdbarch_register_name (gdbarch, regnum));
}
}
if (regnum < 0 || regnum >= numregs)
error ("bad register number");
- if (gdbarch_register_name (current_gdbarch, regnum) == NULL
- || *(gdbarch_register_name (current_gdbarch, regnum)) == '\0')
+ if (gdbarch_register_name (gdbarch, regnum) == NULL
+ || *(gdbarch_register_name (gdbarch, regnum)) == '\0')
ui_out_field_string (uiout, NULL, "");
else
ui_out_field_string (uiout, NULL,
- gdbarch_register_name (current_gdbarch, regnum));
+ gdbarch_register_name (gdbarch, regnum));
}
do_cleanups (cleanup);
- return MI_CMD_DONE;
}
-enum mi_cmd_result
+void
mi_cmd_data_list_changed_registers (char *command, char **argv, int argc)
{
static struct regcache *this_regs = NULL;
struct regcache *prev_regs;
+ struct gdbarch *gdbarch;
int regnum, numregs, changed;
int i;
struct cleanup *cleanup;
In this case, some entries of gdbarch_register_name will change depending
upon the particular processor being debugged. */
- numregs = gdbarch_num_regs (current_gdbarch)
- + gdbarch_num_pseudo_regs (current_gdbarch);
+ gdbarch = get_regcache_arch (this_regs);
+ numregs = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
make_cleanup_ui_out_list_begin_end (uiout, "changed-registers");
regnum < numregs;
regnum++)
{
- if (gdbarch_register_name (current_gdbarch, regnum) == NULL
- || *(gdbarch_register_name (current_gdbarch, regnum)) == '\0')
+ if (gdbarch_register_name (gdbarch, regnum) == NULL
+ || *(gdbarch_register_name (gdbarch, regnum)) == '\0')
continue;
changed = register_changed_p (regnum, prev_regs, this_regs);
if (changed < 0)
if (regnum >= 0
&& regnum < numregs
- && gdbarch_register_name (current_gdbarch, regnum) != NULL
- && *gdbarch_register_name (current_gdbarch, regnum) != '\000')
+ && gdbarch_register_name (gdbarch, regnum) != NULL
+ && *gdbarch_register_name (gdbarch, regnum) != '\000')
{
changed = register_changed_p (regnum, prev_regs, this_regs);
if (changed < 0)
error ("bad register number");
}
do_cleanups (cleanup);
- return MI_CMD_DONE;
}
static int
format argumetn there can be a sequence of numbers, indicating which
registers to fetch the content of. If the format is the only argument,
a list of all the registers with their values is returned. */
-enum mi_cmd_result
+void
mi_cmd_data_list_register_values (char *command, char **argv, int argc)
{
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
int regnum, numregs, format;
int i;
struct cleanup *list_cleanup, *tuple_cleanup;
In this case, some entries of gdbarch_register_name will change depending
upon the particular processor being debugged. */
- numregs = gdbarch_num_regs (current_gdbarch)
- + gdbarch_num_pseudo_regs (current_gdbarch);
-
if (argc == 0)
error ("mi_cmd_data_list_register_values: Usage: -data-list-register-values <format> [<regnum1>...<regnumN>]");
format = (int) argv[0][0];
+ frame = get_selected_frame (NULL);
+ gdbarch = get_frame_arch (frame);
+ numregs = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
+
list_cleanup = make_cleanup_ui_out_list_begin_end (uiout, "register-values");
if (argc == 1) /* No args, beside the format: do all the regs. */
regnum < numregs;
regnum++)
{
- if (gdbarch_register_name (current_gdbarch, regnum) == NULL
- || *(gdbarch_register_name (current_gdbarch, regnum)) == '\0')
+ if (gdbarch_register_name (gdbarch, regnum) == NULL
+ || *(gdbarch_register_name (gdbarch, regnum)) == '\0')
continue;
tuple_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_int (uiout, "number", regnum);
- get_register (regnum, format);
+ get_register (frame, regnum, format);
do_cleanups (tuple_cleanup);
}
}
if (regnum >= 0
&& regnum < numregs
- && gdbarch_register_name (current_gdbarch, regnum) != NULL
- && *gdbarch_register_name (current_gdbarch, regnum) != '\000')
+ && gdbarch_register_name (gdbarch, regnum) != NULL
+ && *gdbarch_register_name (gdbarch, regnum) != '\000')
{
tuple_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_int (uiout, "number", regnum);
- get_register (regnum, format);
+ get_register (frame, regnum, format);
do_cleanups (tuple_cleanup);
}
else
error ("bad register number");
}
do_cleanups (list_cleanup);
- return MI_CMD_DONE;
}
/* Output one register's contents in the desired format. */
static void
-get_register (int regnum, int format)
+get_register (struct frame_info *frame, int regnum, int format)
{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
gdb_byte buffer[MAX_REGISTER_SIZE];
int optim;
int realnum;
if (format == 'N')
format = 0;
- frame_register (get_selected_frame (NULL), regnum, &optim, &lval, &addr,
- &realnum, buffer);
+ frame_register (frame, regnum, &optim, &lval, &addr, &realnum, buffer);
if (optim)
error ("Optimized out");
strcpy (buf, "0x");
ptr = buf + 2;
- for (j = 0; j < register_size (current_gdbarch, regnum); j++)
+ for (j = 0; j < register_size (gdbarch, regnum); j++)
{
- int idx = gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG ? j
- : register_size (current_gdbarch, regnum) - 1 - j;
+ int idx = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG ?
+ j : register_size (gdbarch, regnum) - 1 - j;
sprintf (ptr, "%02x", (unsigned char) buffer[idx]);
ptr += 2;
}
}
else
{
- val_print (register_type (current_gdbarch, regnum), buffer, 0, 0,
- stb->stream, format, 1, 0, Val_pretty_default);
+ struct value_print_options opts;
+ get_formatted_print_options (&opts, format);
+ opts.deref_ref = 1;
+ val_print (register_type (gdbarch, regnum), buffer, 0, 0,
+ stb->stream, 0, &opts, current_language);
ui_out_field_stream (uiout, "value", stb);
ui_out_stream_delete (stb);
}
/* Write given values into registers. The registers and values are
given as pairs. The corresponding MI command is
-data-write-register-values <format> [<regnum1> <value1>...<regnumN> <valueN>]*/
-enum mi_cmd_result
+void
mi_cmd_data_write_register_values (char *command, char **argv, int argc)
{
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
int numregs, i;
char format;
In this case, some entries of gdbarch_register_name will change depending
upon the particular processor being debugged. */
- numregs = gdbarch_num_regs (current_gdbarch)
- + gdbarch_num_pseudo_regs (current_gdbarch);
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+ numregs = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
if (argc == 0)
error ("mi_cmd_data_write_register_values: Usage: -data-write-register-values <format> [<regnum1> <value1>...<regnumN> <valueN>]");
int regnum = atoi (argv[i]);
if (regnum >= 0 && regnum < numregs
- && gdbarch_register_name (current_gdbarch, regnum)
- && *gdbarch_register_name (current_gdbarch, regnum))
+ && gdbarch_register_name (gdbarch, regnum)
+ && *gdbarch_register_name (gdbarch, regnum))
{
LONGEST value;
value = parse_and_eval_address (argv[i + 1]);
/* Write it down. */
- regcache_cooked_write_signed (get_current_regcache (), regnum, value);
+ regcache_cooked_write_signed (regcache, regnum, value);
}
else
error ("bad register number");
}
- return MI_CMD_DONE;
}
/* Evaluate the value of the argument. The argument is an
expression. If the expression contains spaces it needs to be
included in double quotes. */
-enum mi_cmd_result
+void
mi_cmd_data_evaluate_expression (char *command, char **argv, int argc)
{
struct expression *expr;
struct cleanup *old_chain = NULL;
struct value *val;
struct ui_stream *stb = NULL;
+ struct value_print_options opts;
stb = ui_out_stream_new (uiout);
val = evaluate_expression (expr);
/* Print the result of the expression evaluation. */
+ get_user_print_options (&opts);
+ opts.deref_ref = 0;
val_print (value_type (val), value_contents (val),
- value_embedded_offset (val), VALUE_ADDRESS (val),
- stb->stream, 0, 0, 0, 0);
+ value_embedded_offset (val), value_address (val),
+ stb->stream, 0, &opts, current_language);
ui_out_field_stream (uiout, "value", stb);
ui_out_stream_delete (stb);
do_cleanups (old_chain);
-
- return MI_CMD_DONE;
-}
-
-enum mi_cmd_result
-mi_cmd_target_download (char *args, int from_tty)
-{
- char *run;
- struct cleanup *old_cleanups = NULL;
-
- run = xstrprintf ("load %s", args);
- old_cleanups = make_cleanup (xfree, run);
- execute_command (run, from_tty);
-
- do_cleanups (old_cleanups);
- return MI_CMD_DONE;
-}
-
-/* Connect to the remote target. */
-enum mi_cmd_result
-mi_cmd_target_select (char *args, int from_tty)
-{
- char *run;
- struct cleanup *old_cleanups = NULL;
-
- run = xstrprintf ("target %s", args);
- old_cleanups = make_cleanup (xfree, run);
-
- /* target-select is always synchronous. Once the call has returned
- we know that we are connected. */
- /* NOTE: At present all targets that are connected are also
- (implicitly) talking to a halted target. In the future this may
- change. */
- execute_command (run, from_tty);
-
- do_cleanups (old_cleanups);
-
- /* Issue the completion message here. */
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- fputs_unfiltered ("^connected", raw_stdout);
- mi_out_put (uiout, raw_stdout);
- mi_out_rewind (uiout);
- fputs_unfiltered ("\n", raw_stdout);
- do_exec_cleanups (ALL_CLEANUPS);
- return MI_CMD_QUIET;
}
/* DATA-MEMORY-READ:
Returns:
The number of bytes read is SIZE*ROW*COL. */
-enum mi_cmd_result
+void
mi_cmd_data_read_memory (char *command, char **argv, int argc)
{
+ struct gdbarch *gdbarch = get_current_arch ();
struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
CORE_ADDR addr;
long total_bytes;
switch (word_size)
{
case 1:
- word_type = builtin_type_int8;
+ word_type = builtin_type (gdbarch)->builtin_int8;
word_asize = 'b';
break;
case 2:
- word_type = builtin_type_int16;
+ word_type = builtin_type (gdbarch)->builtin_int16;
word_asize = 'h';
break;
case 4:
- word_type = builtin_type_int32;
+ word_type = builtin_type (gdbarch)->builtin_int32;
word_asize = 'w';
break;
case 8:
- word_type = builtin_type_int64;
+ word_type = builtin_type (gdbarch)->builtin_int64;
word_asize = 'g';
break;
default:
- word_type = builtin_type_int8;
+ word_type = builtin_type (gdbarch)->builtin_int8;
word_asize = 'b';
}
/* The number of rows. */
mbuf = xcalloc (total_bytes, 1);
make_cleanup (xfree, mbuf);
- nr_bytes = target_read (¤t_target, TARGET_OBJECT_MEMORY, NULL,
- mbuf, addr, total_bytes);
+ /* Dispatch memory reads to the topmost target, not the flattened
+ current_target. */
+ nr_bytes = target_read_until_error (current_target.beneath,
+ TARGET_OBJECT_MEMORY, NULL, mbuf,
+ addr, total_bytes);
if (nr_bytes <= 0)
error ("Unable to read memory.");
/* Output the header information. */
- ui_out_field_core_addr (uiout, "addr", addr);
+ ui_out_field_core_addr (uiout, "addr", gdbarch, addr);
ui_out_field_int (uiout, "nr-bytes", nr_bytes);
ui_out_field_int (uiout, "total-bytes", total_bytes);
- ui_out_field_core_addr (uiout, "next-row", addr + word_size * nr_cols);
- ui_out_field_core_addr (uiout, "prev-row", addr - word_size * nr_cols);
- ui_out_field_core_addr (uiout, "next-page", addr + total_bytes);
- ui_out_field_core_addr (uiout, "prev-page", addr - total_bytes);
+ ui_out_field_core_addr (uiout, "next-row",
+ gdbarch, addr + word_size * nr_cols);
+ ui_out_field_core_addr (uiout, "prev-row",
+ gdbarch, addr - word_size * nr_cols);
+ ui_out_field_core_addr (uiout, "next-page", gdbarch, addr + total_bytes);
+ ui_out_field_core_addr (uiout, "prev-page", gdbarch, addr - total_bytes);
/* Build the result as a two dimentional table. */
{
int col_byte;
struct cleanup *cleanup_tuple;
struct cleanup *cleanup_list_data;
+ struct value_print_options opts;
+
cleanup_tuple = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
- ui_out_field_core_addr (uiout, "addr", addr + row_byte);
+ ui_out_field_core_addr (uiout, "addr", gdbarch, addr + row_byte);
/* ui_out_field_core_addr_symbolic (uiout, "saddr", addr + row_byte); */
cleanup_list_data = make_cleanup_ui_out_list_begin_end (uiout, "data");
+ get_formatted_print_options (&opts, word_format);
for (col = 0, col_byte = row_byte;
col < nr_cols;
col++, col_byte += word_size)
else
{
ui_file_rewind (stream->stream);
- print_scalar_formatted (mbuf + col_byte, word_type, word_format,
+ print_scalar_formatted (mbuf + col_byte, word_type, &opts,
word_asize, stream->stream);
ui_out_field_stream (uiout, NULL, stream);
}
do_cleanups (cleanup_list_memory);
}
do_cleanups (cleanups);
- return MI_CMD_DONE;
}
/* DATA-MEMORY-WRITE:
Writes VALUE into ADDR + (COLUMN_OFFSET * WORD_SIZE).
Prints nothing. */
-enum mi_cmd_result
+void
mi_cmd_data_write_memory (char *command, char **argv, int argc)
{
+ struct gdbarch *gdbarch = get_current_arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR addr;
char word_format;
long word_size;
/* Get the value into an array. */
buffer = xmalloc (word_size);
old_chain = make_cleanup (xfree, buffer);
- store_signed_integer (buffer, word_size, value);
+ store_signed_integer (buffer, word_size, byte_order, value);
/* Write it down to memory. */
write_memory (addr, buffer, word_size);
/* Free the buffer. */
do_cleanups (old_chain);
-
- return MI_CMD_DONE;
}
-enum mi_cmd_result
+void
mi_cmd_enable_timings (char *command, char **argv, int argc)
{
if (argc == 0)
else
goto usage_error;
- return MI_CMD_DONE;
+ return;
usage_error:
error ("mi_cmd_enable_timings: Usage: %s {yes|no}", command);
- return MI_CMD_DONE;
}
-enum mi_cmd_result
+void
mi_cmd_list_features (char *command, char **argv, int argc)
{
if (argc == 0)
ui_out_field_string (uiout, NULL, "pending-breakpoints");
ui_out_field_string (uiout, NULL, "thread-info");
+#if HAVE_PYTHON
+ ui_out_field_string (uiout, NULL, "python");
+#endif
+
do_cleanups (cleanup);
-
- return MI_CMD_DONE;
+ return;
}
error ("-list-features should be passed no arguments");
- return MI_CMD_DONE;
}
-
+
+void
+mi_cmd_list_target_features (char *command, char **argv, int argc)
+{
+ if (argc == 0)
+ {
+ struct cleanup *cleanup = NULL;
+ cleanup = make_cleanup_ui_out_list_begin_end (uiout, "features");
+
+ if (target_can_async_p ())
+ ui_out_field_string (uiout, NULL, "async");
+
+ do_cleanups (cleanup);
+ return;
+ }
+
+ error ("-list-target-features should be passed no arguments");
+}
+
+void
+mi_cmd_add_inferior (char *command, char **argv, int argc)
+{
+ struct inferior *inf;
+
+ if (argc != 0)
+ error (_("-add-inferior should be passed no arguments"));
+
+ inf = add_inferior_with_spaces ();
+
+ ui_out_field_fmt (uiout, "inferior", "i%d", inf->num);
+}
+
+void
+mi_cmd_remove_inferior (char *command, char **argv, int argc)
+{
+ int id;
+ struct inferior *inf;
+
+ if (argc != 1)
+ error ("-remove-inferior should be passed a single argument");
+
+ if (sscanf (argv[1], "i%d", &id) != 1)
+ error ("the thread group id is syntactically invalid");
+
+ inf = find_inferior_id (id);
+ if (!inf)
+ error ("the specified thread group does not exist");
+
+ delete_inferior_1 (inf, 1 /* silent */);
+}
+
+\f
+
/* Execute a command within a safe environment.
Return <0 for error; >=0 for ok.
static void
captured_mi_execute_command (struct ui_out *uiout, void *data)
{
- struct captured_mi_execute_command_args *args =
- (struct captured_mi_execute_command_args *) data;
- struct mi_parse *context = args->command;
+ struct cleanup *cleanup;
+ struct mi_parse *context = (struct mi_parse *) data;
+
+ if (do_timings)
+ current_command_ts = context->cmd_start;
- struct mi_timestamp cmd_finished;
+ current_token = xstrdup (context->token);
+ cleanup = make_cleanup (free_current_contents, ¤t_token);
+ running_result_record_printed = 0;
+ mi_proceeded = 0;
switch (context->op)
{
-
case MI_COMMAND:
/* A MI command was read from the input stream. */
if (mi_debug_p)
/* FIXME: gdb_???? */
fprintf_unfiltered (raw_stdout, " token=`%s' command=`%s' args=`%s'\n",
context->token, context->command, context->args);
- /* FIXME: cagney/1999-09-25: Rather than this convoluted
- condition expression, each function should return an
- indication of what action is required and then switch on
- that. */
- args->action = EXECUTE_COMMAND_DISPLAY_PROMPT;
-
- if (do_timings)
- current_command_ts = context->cmd_start;
- args->rc = mi_cmd_execute (context);
- if (do_timings)
- timestamp (&cmd_finished);
+ mi_cmd_execute (context);
- if (!target_can_async_p () || !target_executing)
- {
- /* Print the result if there were no errors.
+ /* Print the result if there were no errors.
- Remember that on the way out of executing a command, you have
- to directly use the mi_interp's uiout, since the command could
- have reset the interpreter, in which case the current uiout
- will most likely crash in the mi_out_* routines. */
- if (args->rc == MI_CMD_DONE)
- {
- fputs_unfiltered (context->token, raw_stdout);
- fputs_unfiltered ("^done", raw_stdout);
- mi_out_put (uiout, raw_stdout);
- mi_out_rewind (uiout);
- /* Have to check cmd_start, since the command could be
- -enable-timings. */
- if (do_timings && context->cmd_start)
- print_diff (context->cmd_start, &cmd_finished);
- fputs_unfiltered ("\n", raw_stdout);
- }
- else
- mi_out_rewind (uiout);
- }
- else if (sync_execution)
+ Remember that on the way out of executing a command, you have
+ to directly use the mi_interp's uiout, since the command could
+ have reset the interpreter, in which case the current uiout
+ will most likely crash in the mi_out_* routines. */
+ if (!running_result_record_printed)
{
- /* Don't print the prompt. We are executing the target in
- synchronous mode. */
- args->action = EXECUTE_COMMAND_SUPPRESS_PROMPT;
- return;
+ fputs_unfiltered (context->token, raw_stdout);
+ /* There's no particularly good reason why target-connect results
+ in not ^done. Should kill ^connected for MI3. */
+ fputs_unfiltered (strcmp (context->command, "target-select") == 0
+ ? "^connected" : "^done", raw_stdout);
+ mi_out_put (uiout, raw_stdout);
+ mi_out_rewind (uiout);
+ mi_print_timing_maybe ();
+ fputs_unfiltered ("\n", raw_stdout);
}
+ else
+ /* The command does not want anything to be printed. In that
+ case, the command probably should not have written anything
+ to uiout, but in case it has written something, discard it. */
+ mi_out_rewind (uiout);
break;
case CLI_COMMAND:
/* Call the "console" interpreter. */
argv[0] = "console";
argv[1] = context->command;
- args->rc = mi_cmd_interpreter_exec ("-interpreter-exec", argv, 2);
+ mi_cmd_interpreter_exec ("-interpreter-exec", argv, 2);
/* If we changed interpreters, DON'T print out anything. */
if (current_interp_named_p (INTERP_MI)
|| current_interp_named_p (INTERP_MI2)
|| current_interp_named_p (INTERP_MI3))
{
- if (args->rc == MI_CMD_DONE)
+ if (!running_result_record_printed)
{
fputs_unfiltered (context->token, raw_stdout);
fputs_unfiltered ("^done", raw_stdout);
mi_out_put (uiout, raw_stdout);
mi_out_rewind (uiout);
- fputs_unfiltered ("\n", raw_stdout);
- args->action = EXECUTE_COMMAND_DISPLAY_PROMPT;
+ mi_print_timing_maybe ();
+ fputs_unfiltered ("\n", raw_stdout);
}
else
mi_out_rewind (uiout);
}
+ do_cleanups (cleanup);
+
return;
}
mi_execute_command (char *cmd, int from_tty)
{
struct mi_parse *command;
- struct captured_mi_execute_command_args args;
struct ui_out *saved_uiout = uiout;
/* This is to handle EOF (^D). We just quit gdb. */
if (cmd == 0)
quit_force (NULL, from_tty);
+ target_log_command (cmd);
+
command = mi_parse (cmd);
if (command != NULL)
{
struct gdb_exception result;
+ ptid_t previous_ptid = inferior_ptid;
if (do_timings)
{
timestamp (command->cmd_start);
}
- args.command = command;
- result = catch_exception (uiout, captured_mi_execute_command, &args,
+ result = catch_exception (uiout, captured_mi_execute_command, command,
RETURN_MASK_ALL);
if (result.reason < 0)
{
mi_out_rewind (uiout);
}
- mi_parse_free (command);
+ bpstat_do_actions ();
+
+ if (/* The notifications are only output when the top-level
+ interpreter (specified on the command line) is MI. */
+ ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ()))
+ /* Don't try report anything if there are no threads --
+ the program is dead. */
+ && thread_count () != 0
+ /* -thread-select explicitly changes thread. If frontend uses that
+ internally, we don't want to emit =thread-selected, since
+ =thread-selected is supposed to indicate user's intentions. */
+ && strcmp (command->command, "thread-select") != 0)
+ {
+ struct mi_interp *mi = top_level_interpreter_data ();
+ int report_change = 0;
+
+ if (command->thread == -1)
+ {
+ report_change = (!ptid_equal (previous_ptid, null_ptid)
+ && !ptid_equal (inferior_ptid, previous_ptid)
+ && !ptid_equal (inferior_ptid, null_ptid));
+ }
+ else if (!ptid_equal (inferior_ptid, null_ptid))
+ {
+ struct thread_info *ti = inferior_thread ();
+ report_change = (ti->num != command->thread);
+ }
+
+ if (report_change)
+ {
+ struct thread_info *ti = inferior_thread ();
+ target_terminal_ours ();
+ fprintf_unfiltered (mi->event_channel,
+ "thread-selected,id=\"%d\"",
+ ti->num);
+ gdb_flush (mi->event_channel);
+ }
+ }
- if (args.action == EXECUTE_COMMAND_SUPPRESS_PROMPT)
- /* The command is executing synchronously. Bail out early
- suppressing the finished prompt. */
- return;
+ mi_parse_free (command);
}
fputs_unfiltered ("(gdb) \n", raw_stdout);
/* ..... */
}
-static enum mi_cmd_result
+static void
mi_cmd_execute (struct mi_parse *parse)
{
- free_all_values ();
+ struct cleanup *cleanup;
+ int i;
+
+ prepare_execute_command ();
+
+ cleanup = make_cleanup (null_cleanup, NULL);
- if (parse->cmd->argv_func != NULL
- || parse->cmd->args_func != NULL)
+ if (parse->all && parse->thread_group != -1)
+ error (_("Cannot specify --thread-group together with --all"));
+
+ if (parse->all && parse->thread != -1)
+ error (_("Cannot specify --thread together with --all"));
+
+ if (parse->thread_group != -1 && parse->thread != -1)
+ error (_("Cannot specify --thread together with --thread-group"));
+
+ if (parse->frame != -1 && parse->thread == -1)
+ error (_("Cannot specify --frame without --thread"));
+
+ if (parse->thread_group != -1)
{
- /* FIXME: We need to save the token because the command executed
- may be asynchronous and need to print the token again.
- In the future we can pass the token down to the func
- and get rid of the last_async_command. */
- /* The problem here is to keep the token around when we launch
- the target, and we want to interrupt it later on. The
- interrupt command will have its own token, but when the
- target stops, we must display the token corresponding to the
- last execution command given. So we have another string where
- we copy the token (previous_async_command), if this was
- indeed the token of an execution command, and when we stop we
- print that one. This is possible because the interrupt
- command, when over, will copy that token back into the
- default token string (last_async_command). */
-
- if (target_executing)
- {
- if (!previous_async_command)
- previous_async_command = xstrdup (last_async_command);
- if (strcmp (parse->command, "exec-interrupt"))
- {
- struct ui_file *stb;
- stb = mem_fileopen ();
+ struct inferior *inf = find_inferior_id (parse->thread_group);
+ struct thread_info *tp = 0;
+
+ if (!inf)
+ error (_("Invalid thread group for the --tread-group option"));
+
+ set_current_inferior (inf);
+ /* This behaviour means that if --thread-group option identifies
+ an inferior with multiple threads, then a random one will be picked.
+ This is not a problem -- frontend should always provide --thread if
+ it wishes to operate on a specific thread. */
+ if (inf->pid != 0)
+ tp = any_thread_of_process (inf->pid);
+ switch_to_thread (tp ? tp->ptid : null_ptid);
+ set_current_program_space (inf->pspace);
+ }
- fputs_unfiltered ("Cannot execute command ", stb);
- fputstr_unfiltered (parse->command, '"', stb);
- fputs_unfiltered (" while target running", stb);
+ if (parse->thread != -1)
+ {
+ struct thread_info *tp = find_thread_id (parse->thread);
+ if (!tp)
+ error (_("Invalid thread id: %d"), parse->thread);
- make_cleanup_ui_file_delete (stb);
- error_stream (stb);
- }
- }
- last_async_command = xstrdup (parse->token);
- make_exec_cleanup (free_current_contents, &last_async_command);
- /* FIXME: DELETE THIS! */
- if (parse->cmd->args_func != NULL)
- return parse->cmd->args_func (parse->args, 0 /*from_tty */ );
- return parse->cmd->argv_func (parse->command, parse->argv, parse->argc);
+ if (is_exited (tp->ptid))
+ error (_("Thread id: %d has terminated"), parse->thread);
+
+ switch_to_thread (tp->ptid);
+ }
+
+ if (parse->frame != -1)
+ {
+ struct frame_info *fid;
+ int frame = parse->frame;
+ fid = find_relative_frame (get_current_frame (), &frame);
+ if (frame == 0)
+ /* find_relative_frame was successful */
+ select_frame (fid);
+ else
+ error (_("Invalid frame id: %d"), frame);
}
+
+ current_context = parse;
+
+ if (parse->cmd->argv_func != NULL)
+ parse->cmd->argv_func (parse->command, parse->argv, parse->argc);
else if (parse->cmd->cli.cmd != 0)
{
/* FIXME: DELETE THIS. */
/* Must be a synchronous one. */
mi_execute_cli_command (parse->cmd->cli.cmd, parse->cmd->cli.args_p,
parse->args);
- return MI_CMD_DONE;
}
else
{
make_cleanup_ui_file_delete (stb);
error_stream (stb);
-
- /* unreacheable */
- return MI_CMD_DONE;
}
+ do_cleanups (cleanup);
}
/* FIXME: This is just a hack so we can get some extra commands going.
}
}
-enum mi_cmd_result
-mi_execute_async_cli_command (char *mi, char *args, int from_tty)
+void
+mi_execute_async_cli_command (char *cli_command, char **argv, int argc)
{
struct cleanup *old_cleanups;
char *run;
- char *async_args;
if (target_can_async_p ())
- {
- async_args = (char *) xmalloc (strlen (args) + 2);
- make_exec_cleanup (free, async_args);
- strcpy (async_args, args);
- strcat (async_args, "&");
- run = xstrprintf ("%s %s", mi, async_args);
- make_exec_cleanup (free, run);
- add_continuation (mi_exec_async_cli_cmd_continuation, NULL);
- old_cleanups = NULL;
- }
+ run = xstrprintf ("%s %s&", cli_command, argc ? *argv : "");
else
- {
- run = xstrprintf ("%s %s", mi, args);
- old_cleanups = make_cleanup (xfree, run);
- }
+ run = xstrprintf ("%s %s", cli_command, argc ? *argv : "");
+ old_cleanups = make_cleanup (xfree, run);
- if (!target_can_async_p ())
+ execute_command ( /*ui */ run, 0 /*from_tty */ );
+
+ if (target_can_async_p ())
{
- /* NOTE: For synchronous targets asynchronous behavour is faked by
- printing out the GDB prompt before we even try to execute the
- command. */
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- fputs_unfiltered ("^running\n", raw_stdout);
- fputs_unfiltered ("(gdb) \n", raw_stdout);
- gdb_flush (raw_stdout);
+ /* If we're not executing, an exception should have been throw. */
+ gdb_assert (is_running (inferior_ptid));
+ do_cleanups (old_cleanups);
}
else
- {
- /* FIXME: cagney/1999-11-29: Printing this message before
- calling execute_command is wrong. It should only be printed
- once gdb has confirmed that it really has managed to send a
- run command to the target. */
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- fputs_unfiltered ("^running\n", raw_stdout);
- }
-
- execute_command ( /*ui */ run, 0 /*from_tty */ );
-
- if (!target_can_async_p ())
{
/* Do this before doing any printing. It would appear that some
print code leaves garbage around in the buffer. */
do_cleanups (old_cleanups);
- /* If the target was doing the operation synchronously we fake
- the stopped message. */
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- fputs_unfiltered ("*stopped", raw_stdout);
- mi_out_put (uiout, raw_stdout);
- mi_out_rewind (uiout);
- if (do_timings)
- print_diff_now (current_command_ts);
- fputs_unfiltered ("\n", raw_stdout);
- return MI_CMD_QUIET;
}
- return MI_CMD_DONE;
-}
-
-void
-mi_exec_async_cli_cmd_continuation (struct continuation_arg *arg)
-{
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
- 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);
}
void
xfree (previous_sect_name);
previous_sect_name = xstrdup (section_name);
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
+ if (current_token)
+ fputs_unfiltered (current_token, raw_stdout);
fputs_unfiltered ("+download", raw_stdout);
cleanup_tuple = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_string (uiout, "section", section_name);
struct cleanup *cleanup_tuple;
last_update.tv_sec = time_now.tv_sec;
last_update.tv_usec = time_now.tv_usec;
- if (last_async_command)
- fputs_unfiltered (last_async_command, raw_stdout);
+ if (current_token)
+ fputs_unfiltered (current_token, raw_stdout);
fputs_unfiltered ("+download", raw_stdout);
cleanup_tuple = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_string (uiout, "section", section_name);
print_diff (start, &now);
}
+void
+mi_print_timing_maybe (void)
+{
+ /* If the command is -enable-timing then do_timings may be
+ true whilst current_command_ts is not initialized. */
+ if (do_timings && current_command_ts)
+ print_diff_now (current_command_ts);
+}
+
static long
timeval_diff (struct timeval start, struct timeval end)
{