📄 mi-main.c
字号:
/* MI Command Set. Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Cygnus Solutions (a Red Hat company). 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 "interps.h"#include "event-loop.h"#include "event-top.h"#include "gdbcore.h" /* for write_memory() */#include "value.h" /* for deprecated_write_register_bytes() */#include "regcache.h"#include "gdb.h"#include "frame.h"#include "mi-main.h"#include <ctype.h>#include <sys/time.h>enum { 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_SUPRESS_PROMPT, EXECUTE_COMMAND_DISPLAY_ERROR };/* 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;/* The token of the last asynchronous command */static char *last_async_command;static char *previous_async_command;char *mi_error_message;static char *old_regs;extern void _initialize_mi_main (void);static enum mi_cmd_result 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 int register_changed_p (int regnum);static int get_register (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 formalized. */enum mi_cmd_resultmi_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_resultmi_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_resultmi_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_resultmi_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_resultmi_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_resultmi_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_resultmi_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_resultmi_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_resultmi_cmd_exec_return (char *args, int from_tty){ /* 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 (args, 0); else /* Call return_command with from_tty argument equal to 0 so as to avoid being queried. */ return_command (NULL, 0); /* Because we have called return_command with from_tty = 0, we need to print the frame here. */ print_stack_frame (get_selected_frame (), 1, LOC_AND_ADDRESS); return MI_CMD_DONE;}enum mi_cmd_resultmi_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_resultmi_cmd_exec_interrupt (char *args, int from_tty){ if (!target_executing) { mi_error_message = xstrprintf ("mi_cmd_exec_interrupt: Inferior not executing."); return MI_CMD_ERROR; } 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;}enum mi_cmd_resultmi_cmd_thread_select (char *command, char **argv, int argc){ enum gdb_rc rc; if (argc != 1) { mi_error_message = xstrprintf ("mi_cmd_thread_select: USAGE: threadnum."); return MI_CMD_ERROR; } else rc = gdb_thread_select (uiout, argv[0]); /* RC is enum gdb_rc if it is successful (>=0) enum return_reason if not (<0). */ if ((int) rc < 0 && (enum return_reason) rc == RETURN_ERROR) return MI_CMD_CAUGHT_ERROR; else if ((int) rc >= 0 && rc == GDB_RC_FAIL) return MI_CMD_ERROR; else return MI_CMD_DONE;}enum mi_cmd_resultmi_cmd_thread_list_ids (char *command, char **argv, int argc){ enum gdb_rc rc = MI_CMD_DONE; if (argc != 0) { mi_error_message = xstrprintf ("mi_cmd_thread_list_ids: No arguments required."); return MI_CMD_ERROR; } else rc = gdb_list_thread_ids (uiout); if (rc == GDB_RC_FAIL) return MI_CMD_CAUGHT_ERROR; else return MI_CMD_DONE;}enum mi_cmd_resultmi_cmd_data_list_register_names (char *command, char **argv, int argc){ int regnum, numregs; int i; struct cleanup *cleanup; /* 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 = NUM_REGS + NUM_PSEUDO_REGS; cleanup = make_cleanup_ui_out_list_begin_end (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') ui_out_field_string (uiout, NULL, ""); else 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) { do_cleanups (cleanup); mi_error_message = xstrprintf ("bad register number"); return MI_CMD_ERROR; } if (REGISTER_NAME (regnum) == NULL || *(REGISTER_NAME (regnum)) == '\0') ui_out_field_string (uiout, NULL, ""); else ui_out_field_string (uiout, NULL, REGISTER_NAME (regnum)); } do_cleanups (cleanup); return MI_CMD_DONE;}enum mi_cmd_resultmi_cmd_data_list_changed_registers (char *command, char **argv, int argc){ int regnum, numregs, changed; int i; struct cleanup *cleanup; /* 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 = NUM_REGS + NUM_PSEUDO_REGS; cleanup = make_cleanup_ui_out_list_begin_end (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) { do_cleanups (cleanup); mi_error_message = xstrprintf ("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) { do_cleanups (cleanup); mi_error_message = xstrprintf ("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 { do_cleanups (cleanup); mi_error_message = xstrprintf ("bad register number"); return MI_CMD_ERROR; } } do_cleanups (cleanup); return MI_CMD_DONE;}static intregister_changed_p (int regnum){ char raw_buffer[MAX_REGISTER_SIZE]; if (! frame_register_read (deprecated_selected_frame, regnum, raw_buffer)) return -1; if (memcmp (&old_regs[DEPRECATED_REGISTER_BYTE (regnum)], raw_buffer, register_size (current_gdbarch, regnum)) == 0) return 0; /* Found a changed register. Return 1. */ memcpy (&old_regs[DEPRECATED_REGISTER_BYTE (regnum)], raw_buffer, register_size (current_gdbarch, 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_resultmi_cmd_data_list_register_values (char *command, char **argv, int argc){ int regnum, numregs, format, result; int i; struct cleanup *list_cleanup, *tuple_cleanup; /* 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 = NUM_REGS + NUM_PSEUDO_REGS; if (argc == 0) { mi_error_message = xstrprintf ("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) { mi_error_message = xstrprintf ("mi_cmd_data_list_register_values: No registers."); return MI_CMD_ERROR; } list_cleanup = make_cleanup_ui_out_list_begin_end (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; tuple_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); ui_out_field_int (uiout, "number", regnum); result = get_register (regnum, format); if (result == -1) { do_cleanups (list_cleanup); return MI_CMD_ERROR; } do_cleanups (tuple_cleanup); } } /* 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') { tuple_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); ui_out_field_int (uiout, "number", regnum); result = get_register (regnum, format); if (result == -1) { do_cleanups (list_cleanup); return MI_CMD_ERROR; } do_cleanups (tuple_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -