diff options
author | Andrew Cagney <cagney@redhat.com> | 2000-02-23 00:25:43 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 2000-02-23 00:25:43 +0000 |
commit | fb40c20903110ed8af9701ce7c2635abd3770d52 (patch) | |
tree | 9f99a85a7d64fa61cfa9a167e006e2f747716d42 /gdb/mi/mi-main.c | |
parent | e6c6b6fe2d24c8be4909c7e3c0dd1a937819ffe9 (diff) | |
download | gdb-fb40c20903110ed8af9701ce7c2635abd3770d52.zip gdb-fb40c20903110ed8af9701ce7c2635abd3770d52.tar.gz gdb-fb40c20903110ed8af9701ce7c2635abd3770d52.tar.bz2 |
Add mi/ and testsuite/gdb.mi/ subdirectories.
Add --enable-gdbmi option to configury.
Add mi rules to Makefile.in
Add mi conditional output to event-top.c infrun.c main.c top.c.
Add -i=mi option.
Diffstat (limited to 'gdb/mi/mi-main.c')
-rw-r--r-- | gdb/mi/mi-main.c | 1446 |
1 files changed, 1446 insertions, 0 deletions
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c new file mode 100644 index 0000000..ec7f05e --- /dev/null +++ b/gdb/mi/mi-main.c @@ -0,0 +1,1446 @@ +/* MI Command Set. + Copyright (C) 2000, Free Software Foundation, Inc. + Contributed by Cygnus Solutions. + + 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. */ + +/* Work in progress */ + +#include "defs.h" +#include "target.h" +#include "inferior.h" +#include "gdb_string.h" +#include "top.h" +#include "gdbthread.h" +#include "mi-cmds.h" +#include "mi-parse.h" +#include "mi-getopt.h" +#include "mi-console.h" +#include "ui-out.h" +#include "mi-out.h" +#include "event-loop.h" +#include "event-top.h" +#include "gdbcore.h" /* for write_memory() */ +#include <ctype.h> +#include <sys/time.h> + +/* Convenience macro for allocting typesafe memory. */ + +#undef XMALLOC +#define XMALLOC(TYPE) (TYPE*) xmalloc (sizeof (TYPE)) + +enum + { + FROM_TTY = 0 + }; + + +int mi_debug_p; +struct ui_file *raw_stdout; + +/* The token of the last asynchronous command */ +static char *last_async_command; +static char *previous_async_command; +static char *mi_error_message; +static char *old_regs; + +extern void _initialize_mi_main (void); +static char *mi_input (char *); +static void mi_execute_command (char *cmd, int from_tty); +static enum mi_cmd_result mi_cmd_execute (struct mi_parse *parse); + +static void mi_execute_cli_command (const char *cli, char *args); +static enum mi_cmd_result mi_execute_async_cli_command (char *mi, char *args, int from_tty); +static void mi_execute_command_wrapper (char *cmd); + +void mi_exec_async_cli_cmd_continuation (struct continuation_arg *arg); +static void free_and_reset (char **arg); + +static int register_changed_p (int regnum); +static int get_register (int regnum, int format); +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); + +#ifdef UI_OUT +/* FIXME: these should go in some .h file, but infcmd.c doesn't have a + corresponding .h file. These wrappers will be obsolete anyway, once + we pull the plug on the sanitization. */ +extern void interrupt_target_command_wrapper (char *, int); +extern void return_command_wrapper (char *, int); +#endif + +/* Command implementations. FIXME: Is this libgdb? No. This is the MI + layer that calls libgdb. Any operation used in the below should be + formalized. */ + +enum mi_cmd_result +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); + fputs_unfiltered ("^exit\n", raw_stdout); + mi_out_put (uiout, 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) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("nexti", args, from_tty); +} + +enum mi_cmd_result +mi_cmd_exec_step (char *args, int from_tty) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("step", args, from_tty); +} + +enum mi_cmd_result +mi_cmd_exec_step_instruction (char *args, int from_tty) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("stepi", args, from_tty); +} + +enum mi_cmd_result +mi_cmd_exec_finish (char *args, int from_tty) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("finish", args, from_tty); +} + +enum mi_cmd_result +mi_cmd_exec_until (char *args, int from_tty) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("until", args, from_tty); +} + +enum mi_cmd_result +mi_cmd_exec_return (char *args, int from_tty) +{ +#ifdef UI_OUT + /* This command doesn't really execute the target, it just pops the + specified number of frames. */ + if (*args) + /* Call return_command with from_tty argument equal to 0 so as to + avoid being queried. */ + return_command_wrapper (args, 0); + else + /* Call return_command with from_tty argument equal to 0 so as to + avoid being queried. */ + return_command_wrapper (NULL, 0); + + /* Because we have called return_command with from_tty = 0, we need + to print the frame here. */ + show_and_print_stack_frame (selected_frame, + selected_frame_level, + LOC_AND_ADDRESS); +#endif + + return MI_CMD_DONE; +} + +enum mi_cmd_result +mi_cmd_exec_continue (char *args, int from_tty) +{ + /* FIXME: Should call a libgdb function, not a cli wrapper */ + return mi_execute_async_cli_command ("continue", args, from_tty); +} + +/* Interrupt the execution of the target. Note how we must play around + with the token varialbes, in order to display the current token in + 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) +{ +#ifdef UI_OUT + if (!target_executing) + { + asprintf (&mi_error_message, "mi_cmd_exec_interrupt: Inferior not executing."); + return MI_CMD_ERROR; + } + interrupt_target_command_wrapper (args, from_tty); + if (last_async_command) + fputs_unfiltered (last_async_command, raw_stdout); + fputs_unfiltered ("^done", raw_stdout); + free (last_async_command); + if (previous_async_command) + last_async_command = xstrdup (previous_async_command); + free (previous_async_command); + previous_async_command = NULL; + mi_out_put (uiout, raw_stdout); + mi_out_rewind (uiout); + fputs_unfiltered ("\n", raw_stdout); +#endif + return MI_CMD_QUIET; +} + +enum mi_cmd_result +mi_cmd_thread_select (char *command, char **argv, int argc) +{ + enum gdb_rc rc; + + if (argc != 1) + { + asprintf (&mi_error_message, + "mi_cmd_thread_select: USAGE: threadnum."); + return MI_CMD_ERROR; + } + else + rc = gdb_thread_select (argv[0]); + + if (rc == GDB_RC_FAIL) + return MI_CMD_CAUGHT_ERROR; + else + return MI_CMD_DONE; +} + +enum mi_cmd_result +mi_cmd_thread_list_ids (char *command, char **argv, int argc) +{ + enum gdb_rc rc = MI_CMD_DONE; + + if (argc != 0) + { + asprintf (&mi_error_message, + "mi_cmd_thread_list_ids: No arguments required."); + return MI_CMD_ERROR; + } + else +#ifdef UI_OUT + rc = gdb_list_thread_ids (); +#endif + + if (rc == GDB_RC_FAIL) + return MI_CMD_CAUGHT_ERROR; + else + return MI_CMD_DONE; +} + +enum mi_cmd_result +mi_cmd_data_list_register_names (char *command, char **argv, int argc) +{ + int regnum, numregs; + int i; + + /* Note that the test for a valid register must include checking the + REGISTER_NAME because NUM_REGS may be allocated for the union of + the register sets within a family of related processors. In this + case, some entries of REGISTER_NAME will change depending upon + the particular processor being debugged. */ + + numregs = ARCH_NUM_REGS; + + ui_out_list_begin (uiout, "register-names"); + + if (argc == 0) /* No args, just do all the regs */ + { + for (regnum = 0; + regnum < numregs; + regnum++) + { + if (REGISTER_NAME (regnum) == NULL + || *(REGISTER_NAME (regnum)) == '\0') + continue; + + ui_out_field_string (uiout, NULL, REGISTER_NAME (regnum)); + } + } + + /* Else, list of register #s, just do listed regs */ + for (i = 0; i < argc; i++) + { + regnum = atoi (argv[i]); + + if (regnum >= 0 + && regnum < numregs + && REGISTER_NAME (regnum) != NULL + && *REGISTER_NAME (regnum) != '\000') + ui_out_field_string (uiout, NULL, REGISTER_NAME (regnum)); + else + { + asprintf (&mi_error_message, "bad register number"); + return MI_CMD_ERROR; + } + } + ui_out_list_end (uiout); + return MI_CMD_DONE; +} + +enum mi_cmd_result +mi_cmd_data_list_changed_registers (char *command, char **argv, int argc) +{ + int regnum, numregs, changed; + int i; + + /* Note that the test for a valid register must include checking the + REGISTER_NAME because NUM_REGS may be allocated for the union of + the register sets within a family of related processors. In this + case, some entries of REGISTER_NAME will change depending upon + the particular processor being debugged. */ + + numregs = ARCH_NUM_REGS; + + ui_out_list_begin (uiout, "changed-registers"); + + if (argc == 0) /* No args, just do all the regs */ + { + for (regnum = 0; + regnum < numregs; + regnum++) + { + if (REGISTER_NAME (regnum) == NULL + || *(REGISTER_NAME (regnum)) == '\0') + continue; + changed = register_changed_p (regnum); + if (changed < 0) + { + asprintf (&mi_error_message, + "mi_cmd_data_list_changed_registers: Unable to read register contents."); + return MI_CMD_ERROR; + } + else if (changed) + ui_out_field_int (uiout, NULL, regnum); + } + } + + /* Else, list of register #s, just do listed regs */ + for (i = 0; i < argc; i++) + { + regnum = atoi (argv[i]); + + if (regnum >= 0 + && regnum < numregs + && REGISTER_NAME (regnum) != NULL + && *REGISTER_NAME (regnum) != '\000') + { + changed = register_changed_p (regnum); + if (changed < 0) + { + asprintf (&mi_error_message, + "mi_cmd_data_list_register_change: Unable to read register contents."); + return MI_CMD_ERROR; + } + else if (changed) + ui_out_field_int (uiout, NULL, regnum); + } + else + { + asprintf (&mi_error_message, "bad register number"); + return MI_CMD_ERROR; + } + } + ui_out_list_end (uiout); + return MI_CMD_DONE; +} + +static int +register_changed_p (int regnum) +{ + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + + if (read_relative_register_raw_bytes (regnum, raw_buffer)) + return -1; + + if (memcmp (&old_regs[REGISTER_BYTE (regnum)], raw_buffer, + REGISTER_RAW_SIZE (regnum)) == 0) + return 0; + + /* Found a changed register. Return 1. */ + + memcpy (&old_regs[REGISTER_BYTE (regnum)], raw_buffer, + REGISTER_RAW_SIZE (regnum)); + + return 1; +} + +/* Return a list of register number and value pairs. The valid + arguments expected are: a letter indicating the format in which to + display the registers contents. This can be one of: x (hexadecimal), d + (decimal), N (natural), t (binary), o (octal), r (raw). After the + 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 +mi_cmd_data_list_register_values (char *command, char **argv, int argc) +{ + int regnum, numregs, format, result; + int i; + + /* Note that the test for a valid register must include checking the + REGISTER_NAME because NUM_REGS may be allocated for the union of + the register sets within a family of related processors. In this + case, some entries of REGISTER_NAME will change depending upon + the particular processor being debugged. */ + + numregs = ARCH_NUM_REGS; + + if (argc == 0) + { + asprintf (&mi_error_message, + "mi_cmd_data_list_register_values: Usage: -data-list-register-values <format> [<regnum1>...<regnumN>]"); + return MI_CMD_ERROR; + } + + format = (int) argv[0][0]; + + if (!target_has_registers) + { + asprintf (&mi_error_message, "mi_cmd_data_list_register_values: No registers."); + return MI_CMD_ERROR; + } + + ui_out_list_begin (uiout, "register-values"); + + if (argc == 1) /* No args, beside the format: do all the regs */ + { + for (regnum = 0; + regnum < numregs; + regnum++) + { + if (REGISTER_NAME (regnum) == NULL + || *(REGISTER_NAME (regnum)) == '\0') + continue; + ui_out_list_begin (uiout, NULL); + ui_out_field_int (uiout, "number", regnum); + result = get_register (regnum, format); + if (result == -1) + return MI_CMD_ERROR; + ui_out_list_end (uiout); + } + } + + /* Else, list of register #s, just do listed regs */ + for (i = 1; i < argc; i++) + { + regnum = atoi (argv[i]); + + if (regnum >= 0 + && regnum < numregs + && REGISTER_NAME (regnum) != NULL + && *REGISTER_NAME (regnum) != '\000') + { + ui_out_list_begin (uiout, NULL); + ui_out_field_int (uiout, "number", regnum); + result = get_register (regnum, format); + if (result == -1) + return MI_CMD_ERROR; + ui_out_list_end (uiout); + } + else + { + asprintf (&mi_error_message, "bad register number"); + return MI_CMD_ERROR; + } + } + ui_out_list_end (uiout); + return MI_CMD_DONE; +} + +/* Output one register's contents in the desired format. */ +static int +get_register (int regnum, int format) +{ + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + int optim; + static struct ui_stream *stb = NULL; + + stb = ui_out_stream_new (uiout); + + if (format == 'N') + format = 0; + + /* read_relative_register_raw_bytes returns a virtual frame pointer + (FRAME_FP (selected_frame)) if regnum == FP_REGNUM instead + of the real contents of the register. To get around this, + use get_saved_register instead. */ + get_saved_register (raw_buffer, &optim, (CORE_ADDR *) NULL, selected_frame, + regnum, (enum lval_type *) NULL); + if (optim) + { + asprintf (&mi_error_message, "Optimized out"); + return -1; + } + + /* Convert raw data to virtual format if necessary. */ + + if (REGISTER_CONVERTIBLE (regnum)) + { + REGISTER_CONVERT_TO_VIRTUAL (regnum, REGISTER_VIRTUAL_TYPE (regnum), + raw_buffer, virtual_buffer); + } + else + memcpy (virtual_buffer, raw_buffer, REGISTER_VIRTUAL_SIZE (regnum)); + + if (format == 'r') + { + int j; + char *ptr, buf[1024]; + + strcpy (buf, "0x"); + ptr = buf + 2; + for (j = 0; j < REGISTER_RAW_SIZE (regnum); j++) + { + register int idx = TARGET_BYTE_ORDER == BIG_ENDIAN ? j + : REGISTER_RAW_SIZE (regnum) - 1 - j; + sprintf (ptr, "%02x", (unsigned char) raw_buffer[idx]); + ptr += 2; + } + ui_out_field_string (uiout, "value", buf); + /*fputs_filtered (buf, gdb_stdout); */ + } + else + { + val_print (REGISTER_VIRTUAL_TYPE (regnum), virtual_buffer, 0, 0, + stb->stream, format, 1, 0, Val_pretty_default); + ui_out_field_stream (uiout, "value", stb); + ui_out_stream_delete (stb); + } + return 1; +} + +#if 0 +/*This is commented out because we decided it was not useful. I leave + it, just in case. ezannoni:1999-12-08 */ + +/* Assign a value to a variable. The expression argument must be in + the form A=2 or "A = 2" (I.e. if there are spaces it needs to be + quoted. */ +enum mi_cmd_result +mi_cmd_data_assign (char *command, char **argv, int argc) +{ + struct expression *expr; + struct cleanup *old_chain; + + if (argc != 1) + { + asprintf (&mi_error_message, + "mi_cmd_data_assign: Usage: -data-assign expression"); + return MI_CMD_ERROR; + } + + /* NOTE what follows is a clone of set_command(). FIXME: ezannoni + 01-12-1999: Need to decide what to do with this for libgdb purposes. */ + + expr = parse_expression (argv[0]); + old_chain = make_cleanup ((make_cleanup_func) free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); + return MI_CMD_DONE; +} +#endif + +/* 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 +mi_cmd_data_evaluate_expression (char *command, char **argv, int argc) +{ + struct expression *expr; + struct cleanup *old_chain = NULL; + value_ptr val; + struct ui_stream *stb = NULL; + + stb = ui_out_stream_new (uiout); + + if (argc != 1) + { + asprintf (&mi_error_message, + "mi_cmd_data_evaluate_expression: Usage: -data-evaluate-expression expression"); + return MI_CMD_ERROR; + } + + expr = parse_expression (argv[0]); + + old_chain = make_cleanup ((make_cleanup_func) free_current_contents, &expr); + + val = evaluate_expression (expr); + + /* Print the result of the expression evaluation. */ + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), + VALUE_EMBEDDED_OFFSET (val), VALUE_ADDRESS (val), + stb->stream, 0, 0, 0, 0); + + 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; + + asprintf (&run, "load %s", args); + if (run == 0) + internal_error ("mi_cmd_target_download: no memory"); + old_cleanups = make_cleanup (free, 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; + + asprintf (&run, "target %s", args); + if (run == 0) + internal_error ("mi_cmd_target_select: no memory"); + old_cleanups = make_cleanup (free, 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: + + ADDR: start address of data to be dumped. + WORD-FORMAT: a char indicating format for the ``word''. See + the ``x'' command. + WORD-SIZE: size of each ``word''; 1,2,4, or 8 bytes + NR_ROW: Number of rows. + NR_COL: The number of colums (words per row). + ASCHAR: (OPTIONAL) Append an ascii character dump to each row. Use + ASCHAR for unprintable characters. + + Reads SIZE*NR_ROW*NR_COL bytes starting at ADDR from memory and + displayes them. Returns: + + {addr="...",rowN={wordN="..." ,... [,ascii="..."]}, ...} + + Returns: + The number of bytes read is SIZE*ROW*COL. */ + +enum mi_cmd_result +mi_cmd_data_read_memory (char *command, char **argv, int argc) +{ + struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); + CORE_ADDR addr; + long total_bytes; + long nr_cols; + long nr_rows; + char word_format; + struct type *word_type; + long word_size; + char word_asize; + char aschar; + char *mbuf; + int nr_bytes; + long offset = 0; + int optind = 0; + char *optarg; + enum opt + { + OFFSET_OPT + }; + static struct mi_opt opts[] = + { + {"o", OFFSET_OPT, 1}, + 0 + }; + + while (1) + { + int opt = mi_getopt ("mi_cmd_data_read_memory", argc, argv, opts, + &optind, &optarg); + if (opt < 0) + break; + switch ((enum opt) opt) + { + case OFFSET_OPT: + offset = atol (optarg); + break; + } + } + argv += optind; + argc -= optind; + + if (argc < 5 || argc > 6) + { + asprintf (&mi_error_message, + "mi_cmd_data_read_memory: Usage: ADDR WORD-FORMAT WORD-SIZE NR-ROWS NR-COLS [ASCHAR]."); + return MI_CMD_ERROR; + } + + /* Extract all the arguments. */ + + /* Start address of the memory dump. */ + addr = parse_and_eval_address (argv[0]) + offset; + /* The format character to use when displaying a memory word. See + the ``x'' command. */ + word_format = argv[1][0]; + /* The size of the memory word. */ + word_size = atol (argv[2]); + switch (word_size) + { + case 1: + word_type = builtin_type_int8; + word_asize = 'b'; + break; + case 2: + word_type = builtin_type_int16; + word_asize = 'h'; + break; + case 4: + word_type = builtin_type_int32; + word_asize = 'w'; + break; + case 8: + word_type = builtin_type_int64; + word_asize = 'g'; + break; + default: + word_type = builtin_type_int8; + word_asize = 'b'; + } + /* The number of rows */ + nr_rows = atol (argv[3]); + if (nr_rows <= 0) + { + asprintf (&mi_error_message, + "mi_cmd_data_read_memory: invalid number of rows."); + return MI_CMD_ERROR; + } + /* number of bytes per row. */ + nr_cols = atol (argv[4]); + if (nr_cols <= 0) + { + asprintf (&mi_error_message, + "mi_cmd_data_read_memory: invalid number of columns."); + } + /* The un-printable character when printing ascii. */ + if (argc == 6) + aschar = *argv[5]; + else + aschar = 0; + + /* create a buffer and read it in. */ + total_bytes = word_size * nr_rows * nr_cols; + mbuf = calloc (total_bytes, 1); + make_cleanup (free, mbuf); + if (mbuf == NULL) + { + asprintf (&mi_error_message, + "mi_cmd_data_read_memory: out of memory."); + return MI_CMD_ERROR; + } + nr_bytes = 0; + while (nr_bytes < total_bytes) + { + int error; + long num = target_read_memory_partial (addr + nr_bytes, mbuf + nr_bytes, + total_bytes - nr_bytes, + &error); + if (num <= 0) + break; + nr_bytes += num; + } + + /* output the header information. */ + ui_out_field_core_addr (uiout, "addr", 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); + + /* Build the result as a two dimentional table. */ + { + struct ui_stream *stream = ui_out_stream_new (uiout); + int row; + int row_byte; + ui_out_list_begin (uiout, "memory"); + for (row = 0, row_byte = 0; + row < nr_rows; + row++, row_byte += nr_cols * word_size) + { + int col; + int col_byte; + ui_out_list_begin (uiout, NULL); + ui_out_field_core_addr (uiout, "addr", addr + row_byte); + /* ui_out_field_core_addr_symbolic (uiout, "saddr", addr + row_byte); */ + ui_out_list_begin (uiout, "data"); + for (col = 0, col_byte = row_byte; + col < nr_cols; + col++, col_byte += word_size) + { + if (col_byte + word_size > nr_bytes) + { + ui_out_field_string (uiout, NULL, "N/A"); + } + else + { + ui_file_rewind (stream->stream); + print_scalar_formatted (mbuf + col_byte, word_type, word_format, + word_asize, stream->stream); + ui_out_field_stream (uiout, NULL, stream); + } + } + ui_out_list_end (uiout); + if (aschar) + { + int byte; + ui_file_rewind (stream->stream); + for (byte = row_byte; byte < row_byte + word_size * nr_cols; byte++) + { + if (byte >= nr_bytes) + { + fputc_unfiltered ('X', stream->stream); + } + else if (mbuf[byte] < 32 || mbuf[byte] > 126) + { + fputc_unfiltered (aschar, stream->stream); + } + else + fputc_unfiltered (mbuf[byte], stream->stream); + } + ui_out_field_stream (uiout, "ascii", stream); + } + ui_out_list_end (uiout); + } + ui_out_stream_delete (stream); + ui_out_list_end (uiout); + } + do_cleanups (cleanups); + return MI_CMD_DONE; +} + +/* DATA-MEMORY-WRITE: + + COLUMN_OFFSET: optional argument. Must be preceeded by '-o'. The + offset from the beginning of the memory grid row where the cell to + be written is. + ADDR: start address of the row in the memory grid where the memory + cell is, if OFFSET_COLUMN is specified. Otherwise, the address of + the location to write to. + FORMAT: a char indicating format for the ``word''. See + the ``x'' command. + WORD_SIZE: size of each ``word''; 1,2,4, or 8 bytes + VALUE: value to be written into the memory address. + + Writes VALUE into ADDR + (COLUMN_OFFSET * WORD_SIZE). + + Prints nothing. */ +enum mi_cmd_result +mi_cmd_data_write_memory (char *command, char **argv, int argc) +{ + CORE_ADDR addr; + char word_format; + long word_size; + /* FIXME: ezannoni 2000-02-17 LONGEST could possibly not be big + enough when using a compiler other than GCC. */ + LONGEST value; + unsigned char *buffer; + long offset = 0; + int optind = 0; + char *optarg; + enum opt + { + OFFSET_OPT + }; + static struct mi_opt opts[] = + { + {"o", OFFSET_OPT, 1}, + 0 + }; + + while (1) + { + int opt = mi_getopt ("mi_cmd_data_write_memory", argc, argv, opts, + &optind, &optarg); + if (opt < 0) + break; + switch ((enum opt) opt) + { + case OFFSET_OPT: + offset = atol (optarg); + break; + } + } + argv += optind; + argc -= optind; + + if (argc != 4) + { + asprintf (&mi_error_message, + "mi_cmd_data_write_memory: Usage: [-o COLUMN_OFFSET] ADDR FORMAT WORD-SIZE VALUE."); + return MI_CMD_ERROR; + } + + /* Extract all the arguments. */ + /* Start address of the memory dump. */ + addr = parse_and_eval_address (argv[0]); + /* The format character to use when displaying a memory word. See + the ``x'' command. */ + word_format = argv[1][0]; + /* The size of the memory word. */ + word_size = atol (argv[2]); + + /* Calculate the real address of the write destination. */ + addr += (offset * word_size); + + /* Get the value as a number */ + value = parse_and_eval_address (argv[3]); + /* Get the value into an array */ + buffer = (unsigned char *) xmalloc (word_size); + store_signed_integer (buffer, word_size, value); + /* Write it down to memory */ + write_memory (addr, buffer, word_size); + + return MI_CMD_DONE; +} + +/* Execute a command within a safe environment. Return >0 for + ok. Return <0 for supress prompt. Return 0 to have the error + extracted from error_last_message(). */ + +static int +captured_mi_execute_command (void *data) +{ + struct mi_parse *context = data; + enum mi_cmd_result rc; + + 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. */ + rc = mi_cmd_execute (context); + if (!target_can_async_p () || !target_executing) + { + /* print the result if there were no errors */ + if (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); + fputs_unfiltered ("\n", raw_stdout); + } + else if (rc == MI_CMD_ERROR) + { + if (mi_error_message) + { + fputs_unfiltered (context->token, raw_stdout); + fputs_unfiltered ("^error,msg=\"", raw_stdout); + fputstr_unfiltered (mi_error_message, '"', raw_stdout); + free (mi_error_message); + fputs_unfiltered ("\"\n", raw_stdout); + } + mi_out_rewind (uiout); + } + else if (rc == MI_CMD_CAUGHT_ERROR) + { + mi_out_rewind (uiout); + return 0; + } + else + mi_out_rewind (uiout); + } + else if (sync_execution) + /* Don't print the prompt. We are executing the target in + synchronous mode. */ + return -1; + break; + + case CLI_COMMAND: + /* A CLI command was read from the input stream */ + /* This will be removed as soon as we have a complete set of + mi commands */ + /* echo the command on the console. */ + fprintf_unfiltered (gdb_stdlog, "%s\n", context->command); + /* FIXME: If the command string has something that looks like + a format spec (e.g. %s) we will get a core dump */ + mi_execute_cli_command ("%s", context->command); + /* print the result */ + /* FIXME: Check for errors here. */ + 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); + break; + + } + return 1; +} + + +void +mi_execute_command (char *cmd, int from_tty) +{ + struct mi_parse *command; + + /* This is to handle EOF (^D). We just quit gdb. */ + /* FIXME: we should call some API function here. */ + if (cmd == 0) + quit_force (NULL, from_tty); + + command = mi_parse (cmd); + + if (command != NULL) + { + /* FIXME: cagney/1999-11-04: Can this use of catch_errors either + be pushed even further down or even eliminated? */ + int rc = catch_errors (captured_mi_execute_command, command, "", + RETURN_MASK_ALL); + if (rc < 0) + { + /* The command is executing synchronously. Bail out early + suppressing the finished prompt. */ + mi_parse_free (command); + return; + } + if (rc == 0) + { + char *msg = error_last_message (); + struct cleanup *cleanup = make_cleanup (free, msg); + /* The command execution failed and error() was called + somewhere */ + fputs_unfiltered (command->token, raw_stdout); + fputs_unfiltered ("^error,msg=\"", raw_stdout); + fputstr_unfiltered (msg, '"', raw_stdout); + fputs_unfiltered ("\"\n", raw_stdout); + } + mi_parse_free (command); + } + + gdb_flush (raw_stdout); + fputs_unfiltered ("(gdb) \n", raw_stdout); + /* print any buffered hook code */ + /* ..... */ +} + +static enum mi_cmd_result +mi_cmd_execute (struct mi_parse *parse) +{ + if (parse->cmd->argv_func != NULL + || parse->cmd->args_func != NULL) + { + /* 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")) + { + fputs_unfiltered (parse->token, raw_stdout); + fputs_unfiltered ("^error,msg=\"", raw_stdout); + fputs_unfiltered ("Cannot execute command ", raw_stdout); + fputstr_unfiltered (parse->command, '"', raw_stdout); + fputs_unfiltered (" while target running", raw_stdout); + fputs_unfiltered ("\"\n", raw_stdout); + return MI_CMD_ERROR; + } + } + last_async_command = xstrdup (parse->token); + make_exec_cleanup ((make_cleanup_func) free_and_reset, &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); + } + else if (parse->cmd->cli != 0) + { + /* FIXME: DELETE THIS. */ + /* The operation is still implemented by a cli command */ + /* Must be a synchronous one */ + mi_execute_cli_command (parse->cmd->cli, parse->args); + return MI_CMD_DONE; + } + else + { + /* FIXME: DELETE THIS. */ + fputs_unfiltered (parse->token, raw_stdout); + fputs_unfiltered ("^error,msg=\"", raw_stdout); + fputs_unfiltered ("Undefined mi command: ", raw_stdout); + fputstr_unfiltered (parse->command, '"', raw_stdout); + fputs_unfiltered (" (missing implementation)", raw_stdout); + fputs_unfiltered ("\"\n", raw_stdout); + return MI_CMD_ERROR; + } +} + +void +free_and_reset (char **arg) +{ + free (*arg); + *arg = NULL; +} + +static void +mi_execute_command_wrapper (char *cmd) +{ + mi_execute_command (cmd, stdin == instream); +} + +/* FIXME: This is just a hack so we can get some extra commands going. + We don't want to channel things through the CLI, but call libgdb directly */ +/* Use only for synchronous commands */ + +void +mi_execute_cli_command (const char *cli, char *args) +{ + if (cli != 0) + { + struct cleanup *old_cleanups; + char *run; + asprintf (&run, cli, args); + if (mi_debug_p) + /* FIXME: gdb_???? */ + fprintf_unfiltered (gdb_stdout, "cli=%s run=%s\n", + cli, run); + if (run == 0) + abort (); + old_cleanups = make_cleanup (free, run); + execute_command ( /*ui */ run, 0 /*from_tty */ ); + do_cleanups (old_cleanups); + return; + } +} + +enum mi_cmd_result +mi_execute_async_cli_command (char *mi, char *args, int from_tty) +{ + 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, "&"); + asprintf (&run, "%s %s", mi, async_args); + if (run == 0) + internal_error ("mi_execute_async_cli_command: no memory"); + make_exec_cleanup (free, run); + add_continuation (mi_exec_async_cli_cmd_continuation, NULL); + } + else + { + asprintf (&run, "%s %s", mi, args); + if (run == 0) + internal_error ("mi_execute_async_cli_command: no memory"); + old_cleanups = make_cleanup (free, run); + } + + 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); + } + 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); + 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); + do_exec_cleanups (ALL_CLEANUPS); +} + +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 || strcmp (interpreter_p, "mi") != 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) + { + free (previous_sect_name); + previous_sect_name = xstrdup (section_name); + + if (last_async_command) + fputs_unfiltered (last_async_command, raw_stdout); + fputs_unfiltered ("+download", raw_stdout); + ui_out_list_begin (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); + ui_out_list_end (uiout); + 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) + { + 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); + fputs_unfiltered ("+download", raw_stdout); + ui_out_list_begin (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); + ui_out_list_end (uiout); + mi_out_put (uiout, raw_stdout); + fputs_unfiltered ("\n", raw_stdout); + gdb_flush (raw_stdout); + } +} + +static void +mi_command_loop () +{ + /* 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 (); + + /* 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; + + /* 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); + + if (!event_loop_p) + simplified_command_loop (mi_input, mi_execute_command); + else + start_event_loop (); +} + +static void +setup_architecture_data () +{ + /* don't trust REGISTER_BYTES to be zero. */ + old_regs = xmalloc (REGISTER_BYTES + 1); + memset (old_regs, 0, REGISTER_BYTES + 1); +} + +static void +mi_init_ui (arg0) + char *arg0; +{ + /* Eventually this will contain code that takes control of the + console. */ +} + +void +_initialize_mi_main () +{ + /* If we're _the_ interpreter, take control. */ + if (interpreter_p + && strcmp (interpreter_p, "mi") == 0) + { + init_ui_hook = mi_init_ui; + command_loop_hook = mi_command_loop; + setup_architecture_data (); + register_gdbarch_swap (&old_regs, sizeof (old_regs), NULL); + register_gdbarch_swap (NULL, 0, setup_architecture_data); + 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: Should we notify main that we are here as a possible + interpreter? */ +} + +/* Local variables: */ +/* change-log-default-name: "ChangeLog-mi" */ +/* End: */ |