/* MI Interpreter Definitions and Commands for GDB, the GNU debugger. Copyright 2002, 2003, 2003 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 (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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. */ #include #include "defs.h" #include "gdb_string.h" #include "interps.h" #include "event-top.h" #include "event-loop.h" #include "inferior.h" #include "ui-out.h" #include "top.h" #include "mi-main.h" #include "mi-cmds.h" #include "mi-out.h" #include "mi-console.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; }; /* APPLE LOCAL: These are the gdb events that we need to see when we are running the the mi interpreter. */ struct gdb_events mi_async_hooks = { NULL, /* breakpoint_create */ NULL, /* breakpoint_delete */ NULL, /* breakpoint_modify */ mi_async_breakpoint_resolve_event }; /* END APPLE LOCAL */ /* These are the interpreter setup, etc. functions for the MI interpreter */ static void mi_command_loop (int mi_version); static char *mi_input (char *); /* 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); static void mi3_command_loop (void); static void mi2_command_loop (void); static void mi1_command_loop (void); /* APPLE LOCAL: We need to preserve the mi0 interpreter, because that's what CodeWarrior 9 uses (though it just requests "mi". */ static void mi0_command_loop (void); static char * mi_interp_read_one_line_hook (char *prompt, int repeat, char *anno); static void mi_load_progress (const char *section_name, unsigned long sent_so_far, unsigned long total_section, unsigned long total_sent, unsigned long grand_total); static void * mi_interpreter_init (void) { struct mi_interp *mi = XMALLOC (struct mi_interp); /* Why is this a part of the mi architecture? */ mi_setup_architecture_data (); /* 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. */ raw_stdout = stdio_fileopen (stdout); /* 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); return mi; } static int mi_interpreter_resume (void *data) { struct mi_interp *mi = data; /* As per hack note in mi_interpreter_init, swap in the output channels... */ gdb_setup_readline (); if (event_loop_p) { /* 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; } gdb_stdout = mi->out; /* Route error and log output through the MI */ gdb_stderr = mi->err; gdb_stdlog = mi->log; /* Route target output through the MI. */ gdb_stdtarg = 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; print_frame_more_info_hook = mi_print_frame_more_info; /* 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 /* APPLE LOCAL: The default needs to be mi0, because that's what CodeWarrior expects. */ command_loop_hook = mi0_command_loop; set_gdb_event_hooks (&mi_async_hooks); return 1; } static int mi_interpreter_suspend (void *data) { gdb_disable_readline (); return 1; } static int mi_interpreter_exec (void *data, const char *command) { 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) { return 0; } enum mi_cmd_result mi_cmd_interpreter_exec (char *command, char **argv, int argc) { struct interp *interp_to_use; struct interp *old_interp; enum mi_cmd_result result = MI_CMD_DONE; int i; /* APPLE LOCAL: I am not sure why Keith left out the interp_quiet stuff. */ int old_quiet; if (argc < 2) { xasprintf (&mi_error_message, "mi_cmd_interpreter_exec: Usage: -interpreter-exec interp command"); return MI_CMD_ERROR; } interp_to_use = interp_lookup (argv[0]); if (interp_to_use == NULL) { xasprintf (&mi_error_message, "mi_cmd_interpreter_exec: could not find interpreter \"%s\"", argv[0]); return MI_CMD_ERROR; } if (!interp_exec_p (interp_to_use)) { xasprintf (&mi_error_message, "mi_cmd_interpreter_exec: interpreter \"%s\" does not support command execution", argv[0]); return MI_CMD_ERROR; } old_quiet = interp_set_quiet (interp_to_use, 1); old_interp = interp_set (interp_to_use); if (old_interp == NULL) { asprintf (&mi_error_message, "Could not switch to interpreter \"%s\".", argv[0]); return MI_CMD_ERROR; } /* Set the global mi_interp. We need this so that the hook functions can leave their results in the mi interpreter, rather than dumping them to the console. */ mi_interp = old_interp; /* 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... */ /* APPLE LOCAL: I disagree, how do we know the mi is going to always be the parent interpreter for whatever child interpreter we are running? The only reason this works in the FSF version is that they don't actually switch interpreters, they just hack the cli_exec command so it knows how to set just enough of itself not to get in the mi's way, which seems a little hacky to me. */ mi_insert_notify_hooks (); /* Now run the code... */ for (i = 1; i < argc; i++) { 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 () && (strncmp (argv[0], "console", 7) == 0)) { int len = strlen (argv[i]); buff = xmalloc (len + 2); memcpy (buff, argv[i], len); buff[len] = '&'; buff[len + 1] = '\0'; } /* 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) { mi_error_last_message (); result = MI_CMD_ERROR; break; } xfree (buff); do_exec_error_cleanups (ALL_CLEANUPS); sync_execution = 0; } /* APPLE LOCAL: Now do the switch... The FSF code was rewritten to assume the cli's execute proc would know how to run code under the cli without setting the interpreter, but this seems weak to me. So I backed out that change in cli-interp.c, and put the interp_set back here. */ interp_set (old_interp); mi_interp = NULL; mi_remove_notify_hooks (); interp_set_quiet (interp_to_use, old_quiet); /* 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... */ /* APPLE LOCAL: The FSF version leaves out the mi_dont_register_continuation. Maybe this hadn't been added yet when they adopted the code. */ if (target_can_async_p () && target_executing && !mi_dont_register_continuation) { struct mi_continuation_arg *cont_args = mi_setup_continuation_arg (NULL); if (current_command_token) fputs_unfiltered (current_command_token, raw_stdout); fputs_unfiltered ("^running\n", raw_stdout); add_continuation (mi_interpreter_exec_continuation, (void *) cont_args); } return result; } /* APPLE LOCAL - For reasons I don't really understand, the FSF version didn't take this function. I find it really useful for chasing down MI bugs, 'cause it means I don't have to work all the time in the MI. So I am putting it back... */ enum mi_cmd_result mi_cmd_interpreter_set (char *command, char **argv, int argc) { struct interp *interp; if (argc != 1) { asprintf (&mi_error_message, "mi_cmd_interpreter_set: " "wrong #of args, should be 1"); return MI_CMD_ERROR; } interp = interp_lookup (argv[0]); if (interp == NULL) { asprintf (&mi_error_message, "mi_cmd_interpreter_set: " "could not find interpreter %s", argv[0]); return MI_CMD_ERROR; } if (interp_set (interp) == NULL) { asprintf (&mi_error_message, "mi_cmd_interpreter_set: " "error setting interpreter %s", argv[0]); return MI_CMD_ERROR; } return MI_CMD_DONE; } /* This implements the "interpreter complete command" which takes an interpreter, a command string, and optionally a cursor position within the command, and completes the string based on that interpreter's completion function. */ enum mi_cmd_result mi_cmd_interpreter_complete (char *command, char **argv, int argc) { struct interp *interp_to_use; int cursor; if (argc < 2 || argc > 3) { asprintf (&mi_error_message, "Wrong # or arguments, should be \"%s interp command \".", command); return MI_CMD_ERROR; } interp_to_use = interp_lookup (argv[0]); if (interp_to_use == NULL) { asprintf (&mi_error_message, "Could not find interpreter \"%s\".", argv[0]); return MI_CMD_ERROR; } if (argc == 3) { cursor = atoi (argv[2]); } else { cursor = strlen (argv[1]); } if (interp_complete (interp_to_use, argv[1], argv[1], cursor) == 0) return MI_CMD_ERROR; else return MI_CMD_DONE; } /* APPLE LOCAL: FIXME - Keith removed all the mi hooks. The reason is he posits that all this work can be done with the gdb_events. I am going to leave them in till this is proved. SO... FIXME: See if this really can be done with events. */ /* * 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. */ void mi_insert_notify_hooks (void) { create_breakpoint_hook = mi_interp_create_breakpoint_hook; delete_breakpoint_hook = mi_interp_delete_breakpoint_hook; modify_breakpoint_hook = mi_interp_modify_breakpoint_hook; frame_changed_hook = mi_interp_frame_changed_hook; stack_changed_hook = mi_interp_stack_changed_hook; context_hook = mi_interp_context_hook; /* command_line_input_hook = mi_interp_command_line_input; */ query_hook = mi_interp_query_hook; command_line_input_hook = mi_interp_read_one_line_hook; stepping_command_hook = mi_interp_stepping_command_hook; continue_command_hook = mi_interp_continue_command_hook; run_command_hook = mi_interp_run_command_hook; } void mi_remove_notify_hooks () { create_breakpoint_hook = NULL; delete_breakpoint_hook = NULL; modify_breakpoint_hook = NULL; frame_changed_hook = NULL; stack_changed_hook = NULL; context_hook = NULL; /* command_line_input_hook = NULL; */ query_hook = NULL; command_line_input_hook = NULL; stepping_command_hook = NULL; continue_command_hook = NULL; run_command_hook = NULL; } int mi_interp_query_hook (const char *ctlstr, va_list ap) { return 1; } static char * mi_interp_read_one_line_hook (char *prompt, int repeat, char *anno) { static char buff[256]; if (strlen (prompt) > 200) internal_error (__FILE__, __LINE__, "Prompt \"%s\" ridiculously long.", prompt); sprintf (buff, "read-one-line,prompt=\"%s\"", prompt); mi_output_async_notification (buff); (void) fgets(buff, sizeof(buff), stdin); buff[(strlen(buff) - 1)] = 0; return buff; } static void mi0_command_loop (void) { mi_command_loop (0); } static void mi1_command_loop (void) { mi_command_loop (1); } static void mi2_command_loop (void) { mi_command_loop (2); } static void mi3_command_loop (void) { mi_command_loop (3); } static void mi_command_loop (int mi_version) { /* 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, "@", '"'); /* APPLE LOCAL: Don't make a new uiout. There's already one in the mi_interp we are starting up, and we need to use that one or we won't get hook messages from interpreter-exec. */ #if 0 /* 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); #endif /* 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; print_frame_more_info_hook = mi_print_frame_more_info; /* Set the uiout to the interpreter's uiout. */ uiout = interp_ui_out (NULL); /* 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); else start_event_loop (); } static char * mi_input (char *buf) { return gdb_readline (NULL); } static void mi_load_progress (const char *section_name, unsigned long sent_so_far, unsigned long total_section, unsigned long total_sent, unsigned long grand_total) { struct timeval time_now, delta, update_threshold; static struct timeval last_update; static char *previous_sect_name = NULL; int new_section; if (!interpreter_p || strncmp (interpreter_p, "mi", 2) != 0) return; update_threshold.tv_sec = 0; update_threshold.tv_usec = 500000; gettimeofday (&time_now, NULL); delta.tv_usec = time_now.tv_usec - last_update.tv_usec; delta.tv_sec = time_now.tv_sec - last_update.tv_sec; if (delta.tv_usec < 0) { delta.tv_sec -= 1; delta.tv_usec += 1000000; } new_section = (previous_sect_name ? strcmp (previous_sect_name, section_name) : 1); if (new_section) { struct cleanup *cleanup_tuple; xfree (previous_sect_name); previous_sect_name = xstrdup (section_name); if (current_command_token) fputs_unfiltered (current_command_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); ui_out_field_int (uiout, "section-size", total_section); ui_out_field_int (uiout, "total-size", grand_total); do_cleanups (cleanup_tuple); mi_out_put (uiout, raw_stdout); fputs_unfiltered ("\n", raw_stdout); gdb_flush (raw_stdout); } if (delta.tv_sec >= update_threshold.tv_sec && delta.tv_usec >= update_threshold.tv_usec) { struct cleanup *cleanup_tuple; last_update.tv_sec = time_now.tv_sec; last_update.tv_usec = time_now.tv_usec; if (current_command_token) fputs_unfiltered (current_command_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); ui_out_field_int (uiout, "section-sent", sent_so_far); ui_out_field_int (uiout, "section-size", total_section); ui_out_field_int (uiout, "total-sent", total_sent); ui_out_field_int (uiout, "total-size", grand_total); do_cleanups (cleanup_tuple); mi_out_put (uiout, raw_stdout); fputs_unfiltered ("\n", raw_stdout); gdb_flush (raw_stdout); } } extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */ void _initialize_mi_interp (void) { 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 */ }; /* 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)); /* "mi" selects the most recent released version. "mi2" was released as part of GDB 6.0. */ /* APPLE LOCAL: Set this back to mi0, since CodeWarrior just asks for the "mi" and doesn't specify a version, but chokes on mi2. */ interp_add (interp_new (INTERP_MI, NULL, mi_out_new (0), &procs)); }