0743fc83c0
Currently there are many prefix commands that do nothing but call either help_list or cmd_show_list. I happened to notice that one such call, for "set print type", used the wrong command list parameter, causing incorrect output. Rather than fix this bug in isolation, I decided to eliminate this possibility by adding two new ways to add prefix commands, which simply route the call to help_list or cmd_show_list, as appropriate. This makes it impossible for a mismatch to occur. In some cases, a bit of output was removed; however, I don't think this output in general was very useful. It seemed redundant with what's already printed by help_list. A representative example is this hunk, removed from ada-lang.c: - printf_unfiltered (_(\ -"\"set ada\" must be followed by the name of a setting.\n")); This simplified the CLI style set/show commands quite a bit, and allowed the deletion of a macro. This also cleans up some unusual code in windows-tdep.c. Tested on x86-64 Fedora 30. Note that I have no way to build the go32-nat.c change. gdb/ChangeLog 2020-04-17 Tom Tromey <tromey@adacore.com> * auto-load.c (show_auto_load_cmd): Remove. (auto_load_show_cmdlist_get): Use add_show_prefix_cmd. * arc-tdep.c (_initialize_arc_tdep): Use add_show_prefix_cmd. (maintenance_print_arc_command): Remove. * tui/tui-win.c (tui_command): Remove. (tui_get_cmd_list): Use add_basic_prefix_cmd. * tui/tui-layout.c (tui_layout_command): Remove. (_initialize_tui_layout): Use add_basic_prefix_cmd. * python/python.c (user_set_python, user_show_python): Remove. (_initialize_python): Use add_basic_prefix_cmd, add_show_prefix_cmd. * guile/guile.c (set_guile_command, show_guile_command): Remove. (install_gdb_commands): Use add_basic_prefix_cmd, add_show_prefix_cmd. (info_guile_command): Remove. * dwarf2/read.c (set_dwarf_cmd, show_dwarf_cmd): Remove. (_initialize_dwarf2_read): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cli/cli-style.h (class cli_style_option) <add_setshow_commands>: Remove do_set and do_show parameters. * cli/cli-style.c (set_style, show_style): Remove. (_initialize_cli_style): Use add_basic_prefix_cmd, add_show_prefix_cmd. (cli_style_option::add_setshow_commands): Remove do_set and do_show parameters. (cli_style_option::add_setshow_commands): Use add_basic_prefix_cmd, add_show_prefix_cmd. (STYLE_ADD_SETSHOW_COMMANDS): Remove macro. (set_style_name): Remove. * cli/cli-dump.c (dump_command, append_command): Remove. (srec_dump_command, ihex_dump_command, verilog_dump_command) (tekhex_dump_command, binary_dump_command) (binary_append_command): Remove. (_initialize_cli_dump): Use add_basic_prefix_cmd. * windows-tdep.c (w32_prefix_command_valid): Remove global. (init_w32_command_list): Remove; move into ... (_initialize_windows_tdep): ... here. Use add_basic_prefix_cmd. * valprint.c (set_print, show_print, set_print_raw) (show_print_raw): Remove. (_initialize_valprint): Use add_basic_prefix_cmd, add_show_prefix_cmd. * typeprint.c (set_print_type, show_print_type): Remove. (_initialize_typeprint): Use add_basic_prefix_cmd, add_show_prefix_cmd. * record.c (set_record_command, show_record_command): Remove. (_initialize_record): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cli/cli-cmds.c (_initialize_cli_cmds): Use add_basic_prefix_cmd, add_show_prefix_cmd. (info_command, show_command, set_debug, show_debug): Remove. * top.h (set_history, show_history): Don't declare. * top.c (set_history, show_history): Remove. * target-descriptions.c (set_tdesc_cmd, show_tdesc_cmd) (unset_tdesc_cmd): Remove. (_initialize_target_descriptions): Use add_basic_prefix_cmd, add_show_prefix_cmd. * symtab.c (info_module_command): Remove. (_initialize_symtab): Use add_basic_prefix_cmd. * symfile.c (overlay_command): Remove. (_initialize_symfile): Use add_basic_prefix_cmd. * sparc64-tdep.c (info_adi_command): Remove. (_initialize_sparc64_adi_tdep): Use add_basic_prefix_cmd. * sh-tdep.c (show_sh_command, set_sh_command): Remove. (_initialize_sh_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * serial.c (serial_set_cmd, serial_show_cmd): Remove. (_initialize_serial): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ser-tcp.c (set_tcp_cmd, show_tcp_cmd): Remove. (_initialize_ser_tcp): Use add_basic_prefix_cmd, add_show_prefix_cmd. * rs6000-tdep.c (set_powerpc_command, show_powerpc_command) (_initialize_rs6000_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * riscv-tdep.c (show_riscv_command, set_riscv_command) (show_debug_riscv_command, set_debug_riscv_command): Remove. (_initialize_riscv_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * remote.c (remote_command, set_remote_cmd): Remove. (_initialize_remote): Use add_basic_prefix_cmd. * record-full.c (set_record_full_command) (show_record_full_command): Remove. (_initialize_record_full): Use add_basic_prefix_cmd, add_show_prefix_cmd. * record-btrace.c (cmd_set_record_btrace) (cmd_show_record_btrace, cmd_set_record_btrace_bts) (cmd_show_record_btrace_bts, cmd_set_record_btrace_pt) (cmd_show_record_btrace_pt): Remove. (_initialize_record_btrace): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ravenscar-thread.c (set_ravenscar_command) (show_ravenscar_command): Remove. (_initialize_ravenscar): Use add_basic_prefix_cmd, add_show_prefix_cmd. * mips-tdep.c (show_mips_command, set_mips_command) (_initialize_mips_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * maint.c (maintenance_command, maintenance_info_command) (maintenance_check_command, maintenance_print_command) (maintenance_set_cmd, maintenance_show_cmd): Remove. (_initialize_maint_cmds): Use add_basic_prefix_cmd, add_show_prefix_cmd. (show_per_command_cmd): Remove. * maint-test-settings.c (maintenance_set_test_settings_cmd): Remove. (maintenance_show_test_settings_cmd): Remove. (_initialize_maint_test_settings): Use add_basic_prefix_cmd, add_show_prefix_cmd. * maint-test-options.c (maintenance_test_options_command): Remove. (_initialize_maint_test_options): Use add_basic_prefix_cmd. * macrocmd.c (macro_command): Remove (_initialize_macrocmd): Use add_basic_prefix_cmd. * language.c (set_check, show_check): Remove. (_initialize_language): Use add_basic_prefix_cmd, add_show_prefix_cmd. * infcmd.c (unset_command): Remove. (_initialize_infcmd): Use add_basic_prefix_cmd. * i386-tdep.c (set_mpx_cmd, show_mpx_cmd): Remove. (_initialize_i386_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * go32-nat.c (go32_info_dos_command): Remove. (_initialize_go32_nat): Use add_basic_prefix_cmd. * cli/cli-decode.c (do_prefix_cmd, add_basic_prefix_cmd) (do_show_prefix_cmd, add_show_prefix_cmd): New functions. * frame.c (set_backtrace_cmd, show_backtrace_cmd): Remove. (_initialize_frame): Use add_basic_prefix_cmd, add_show_prefix_cmd. * dcache.c (set_dcache_command, show_dcache_command): Remove. (_initialize_dcache): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cp-support.c (maint_cplus_command): Remove. (_initialize_cp_support): Use add_basic_prefix_cmd. * btrace.c (maint_btrace_cmd, maint_btrace_set_cmd) (maint_btrace_show_cmd, maint_btrace_pt_set_cmd) (maint_btrace_pt_show_cmd, _initialize_btrace): Use add_basic_prefix_cmd, add_show_prefix_cmd. * breakpoint.c (save_command): Remove. (_initialize_breakpoint): Use add_basic_prefix_cmd. * arm-tdep.c (set_arm_command, show_arm_command): Remove. (_initialize_arm_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ada-lang.c (maint_set_ada_cmd, maint_show_ada_cmd) (set_ada_command, show_ada_command): Remove. (_initialize_ada_language): Use add_basic_prefix_cmd, add_show_prefix_cmd. * command.h (add_basic_prefix_cmd, add_show_prefix_cmd): Declare. gdb/testsuite/ChangeLog 2020-04-17 Tom Tromey <tromey@adacore.com> * gdb.cp/maint.exp (test_help): Simplify multiple_help_body. Update tests. * gdb.btrace/cpu.exp: Update tests. * gdb.base/maint.exp: Update tests. * gdb.base/default.exp: Update tests. * gdb.base/completion.exp: Update tests.
883 lines
22 KiB
C
883 lines
22 KiB
C
/* Process record and replay target for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2008-2020 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 3 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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "gdbcmd.h"
|
|
#include "completer.h"
|
|
#include "record.h"
|
|
#include "observable.h"
|
|
#include "inferior.h"
|
|
#include "gdbsupport/common-utils.h"
|
|
#include "cli/cli-utils.h"
|
|
#include "disasm.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
/* This is the debug switch for process record. */
|
|
unsigned int record_debug = 0;
|
|
|
|
/* The number of instructions to print in "record instruction-history". */
|
|
static unsigned int record_insn_history_size = 10;
|
|
|
|
/* The variable registered as control variable in the "record
|
|
instruction-history" command. Necessary for extra input
|
|
validation. */
|
|
static unsigned int record_insn_history_size_setshow_var;
|
|
|
|
/* The number of functions to print in "record function-call-history". */
|
|
static unsigned int record_call_history_size = 10;
|
|
|
|
/* The variable registered as control variable in the "record
|
|
call-history" command. Necessary for extra input validation. */
|
|
static unsigned int record_call_history_size_setshow_var;
|
|
|
|
struct cmd_list_element *record_cmdlist = NULL;
|
|
struct cmd_list_element *record_goto_cmdlist = NULL;
|
|
struct cmd_list_element *set_record_cmdlist = NULL;
|
|
struct cmd_list_element *show_record_cmdlist = NULL;
|
|
struct cmd_list_element *info_record_cmdlist = NULL;
|
|
|
|
#define DEBUG(msg, args...) \
|
|
if (record_debug) \
|
|
fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)
|
|
|
|
/* See record.h. */
|
|
|
|
struct target_ops *
|
|
find_record_target (void)
|
|
{
|
|
return find_target_at (record_stratum);
|
|
}
|
|
|
|
/* Check that recording is active. Throw an error, if it isn't. */
|
|
|
|
static struct target_ops *
|
|
require_record_target (void)
|
|
{
|
|
struct target_ops *t;
|
|
|
|
t = find_record_target ();
|
|
if (t == NULL)
|
|
error (_("No record target is currently active.\n"
|
|
"Use one of the \"target record-<TAB><TAB>\" commands first."));
|
|
|
|
return t;
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_preopen (void)
|
|
{
|
|
/* Check if a record target is already running. */
|
|
if (find_record_target () != NULL)
|
|
error (_("The process is already being recorded. Use \"record stop\" to "
|
|
"stop recording first."));
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_start (const char *method, const char *format, int from_tty)
|
|
{
|
|
if (method == NULL)
|
|
{
|
|
if (format == NULL)
|
|
execute_command_to_string ("record", from_tty, false);
|
|
else
|
|
error (_("Invalid format."));
|
|
}
|
|
else if (strcmp (method, "full") == 0)
|
|
{
|
|
if (format == NULL)
|
|
execute_command_to_string ("record full", from_tty, false);
|
|
else
|
|
error (_("Invalid format."));
|
|
}
|
|
else if (strcmp (method, "btrace") == 0)
|
|
{
|
|
if (format == NULL)
|
|
execute_command_to_string ("record btrace", from_tty, false);
|
|
else if (strcmp (format, "bts") == 0)
|
|
execute_command_to_string ("record btrace bts", from_tty, false);
|
|
else if (strcmp (format, "pt") == 0)
|
|
execute_command_to_string ("record btrace pt", from_tty, false);
|
|
else
|
|
error (_("Invalid format."));
|
|
}
|
|
else
|
|
error (_("Invalid method."));
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_stop (int from_tty)
|
|
{
|
|
execute_command_to_string ("record stop", from_tty, false);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
int
|
|
record_read_memory (struct gdbarch *gdbarch,
|
|
CORE_ADDR memaddr, gdb_byte *myaddr,
|
|
ssize_t len)
|
|
{
|
|
int ret = target_read_memory (memaddr, myaddr, len);
|
|
|
|
if (ret != 0)
|
|
DEBUG ("error reading memory at addr %s len = %ld.\n",
|
|
paddress (gdbarch, memaddr), (long) len);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Stop recording. */
|
|
|
|
static void
|
|
record_stop (struct target_ops *t)
|
|
{
|
|
DEBUG ("stop %s", t->shortname ());
|
|
|
|
t->stop_recording ();
|
|
}
|
|
|
|
/* Unpush the record target. */
|
|
|
|
static void
|
|
record_unpush (struct target_ops *t)
|
|
{
|
|
DEBUG ("unpush %s", t->shortname ());
|
|
|
|
unpush_target (t);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_disconnect (struct target_ops *t, const char *args, int from_tty)
|
|
{
|
|
gdb_assert (t->stratum () == record_stratum);
|
|
|
|
DEBUG ("disconnect %s", t->shortname ());
|
|
|
|
record_stop (t);
|
|
record_unpush (t);
|
|
|
|
target_disconnect (args, from_tty);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_detach (struct target_ops *t, inferior *inf, int from_tty)
|
|
{
|
|
gdb_assert (t->stratum () == record_stratum);
|
|
|
|
DEBUG ("detach %s", t->shortname ());
|
|
|
|
record_stop (t);
|
|
record_unpush (t);
|
|
|
|
target_detach (inf, from_tty);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_mourn_inferior (struct target_ops *t)
|
|
{
|
|
gdb_assert (t->stratum () == record_stratum);
|
|
|
|
DEBUG ("mourn inferior %s", t->shortname ());
|
|
|
|
/* It is safer to not stop recording. Resources will be freed when
|
|
threads are discarded. */
|
|
record_unpush (t);
|
|
|
|
target_mourn_inferior (inferior_ptid);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_kill (struct target_ops *t)
|
|
{
|
|
gdb_assert (t->stratum () == record_stratum);
|
|
|
|
DEBUG ("kill %s", t->shortname ());
|
|
|
|
/* It is safer to not stop recording. Resources will be freed when
|
|
threads are discarded. */
|
|
record_unpush (t);
|
|
|
|
target_kill ();
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
int
|
|
record_check_stopped_by_breakpoint (const address_space *aspace,
|
|
CORE_ADDR pc,
|
|
enum target_stop_reason *reason)
|
|
{
|
|
if (breakpoint_inserted_here_p (aspace, pc))
|
|
{
|
|
if (hardware_breakpoint_inserted_here_p (aspace, pc))
|
|
*reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
|
|
else
|
|
*reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
|
|
return 1;
|
|
}
|
|
|
|
*reason = TARGET_STOPPED_BY_NO_REASON;
|
|
return 0;
|
|
}
|
|
|
|
/* Implement "show record debug" command. */
|
|
|
|
static void
|
|
show_record_debug (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *c, const char *value)
|
|
{
|
|
fprintf_filtered (file, _("Debugging of process record target is %s.\n"),
|
|
value);
|
|
}
|
|
|
|
/* Alias for "target record". */
|
|
|
|
static void
|
|
cmd_record_start (const char *args, int from_tty)
|
|
{
|
|
execute_command ("target record-full", from_tty);
|
|
}
|
|
|
|
/* Truncate the record log from the present point
|
|
of replay until the end. */
|
|
|
|
static void
|
|
cmd_record_delete (const char *args, int from_tty)
|
|
{
|
|
require_record_target ();
|
|
|
|
if (!target_record_is_replaying (inferior_ptid))
|
|
{
|
|
printf_unfiltered (_("Already at end of record list.\n"));
|
|
return;
|
|
}
|
|
|
|
if (!target_supports_delete_record ())
|
|
{
|
|
printf_unfiltered (_("The current record target does not support "
|
|
"this operation.\n"));
|
|
return;
|
|
}
|
|
|
|
if (!from_tty || query (_("Delete the log from this point forward "
|
|
"and begin to record the running message "
|
|
"at current PC?")))
|
|
target_delete_record ();
|
|
}
|
|
|
|
/* Implement the "stoprecord" or "record stop" command. */
|
|
|
|
static void
|
|
cmd_record_stop (const char *args, int from_tty)
|
|
{
|
|
struct target_ops *t;
|
|
|
|
t = require_record_target ();
|
|
|
|
record_stop (t);
|
|
record_unpush (t);
|
|
|
|
printf_unfiltered (_("Process record is stopped and all execution "
|
|
"logs are deleted.\n"));
|
|
|
|
gdb::observers::record_changed.notify (current_inferior (), 0, NULL, NULL);
|
|
}
|
|
|
|
|
|
/* The "info record" command. */
|
|
|
|
static void
|
|
info_record_command (const char *args, int from_tty)
|
|
{
|
|
struct target_ops *t;
|
|
|
|
t = find_record_target ();
|
|
if (t == NULL)
|
|
{
|
|
printf_filtered (_("No record target is currently active.\n"));
|
|
return;
|
|
}
|
|
|
|
printf_filtered (_("Active record target: %s\n"), t->shortname ());
|
|
t->info_record ();
|
|
}
|
|
|
|
/* The "record save" command. */
|
|
|
|
static void
|
|
cmd_record_save (const char *args, int from_tty)
|
|
{
|
|
const char *recfilename;
|
|
char recfilename_buffer[40];
|
|
|
|
require_record_target ();
|
|
|
|
if (args != NULL && *args != 0)
|
|
recfilename = args;
|
|
else
|
|
{
|
|
/* Default recfile name is "gdb_record.PID". */
|
|
xsnprintf (recfilename_buffer, sizeof (recfilename_buffer),
|
|
"gdb_record.%d", inferior_ptid.pid ());
|
|
recfilename = recfilename_buffer;
|
|
}
|
|
|
|
target_save_record (recfilename);
|
|
}
|
|
|
|
/* See record.h. */
|
|
|
|
void
|
|
record_goto (const char *arg)
|
|
{
|
|
ULONGEST insn;
|
|
|
|
if (arg == NULL || *arg == '\0')
|
|
error (_("Command requires an argument (insn number to go to)."));
|
|
|
|
insn = parse_and_eval_long (arg);
|
|
|
|
require_record_target ();
|
|
target_goto_record (insn);
|
|
}
|
|
|
|
/* "record goto" command. Argument is an instruction number,
|
|
as given by "info record".
|
|
|
|
Rewinds the recording (forward or backward) to the given instruction. */
|
|
|
|
static void
|
|
cmd_record_goto (const char *arg, int from_tty)
|
|
{
|
|
record_goto (arg);
|
|
}
|
|
|
|
/* The "record goto begin" command. */
|
|
|
|
static void
|
|
cmd_record_goto_begin (const char *arg, int from_tty)
|
|
{
|
|
if (arg != NULL && *arg != '\0')
|
|
error (_("Junk after argument: %s."), arg);
|
|
|
|
require_record_target ();
|
|
target_goto_record_begin ();
|
|
}
|
|
|
|
/* The "record goto end" command. */
|
|
|
|
static void
|
|
cmd_record_goto_end (const char *arg, int from_tty)
|
|
{
|
|
if (arg != NULL && *arg != '\0')
|
|
error (_("Junk after argument: %s."), arg);
|
|
|
|
require_record_target ();
|
|
target_goto_record_end ();
|
|
}
|
|
|
|
/* Read an instruction number from an argument string. */
|
|
|
|
static ULONGEST
|
|
get_insn_number (const char **arg)
|
|
{
|
|
ULONGEST number;
|
|
const char *begin, *end, *pos;
|
|
|
|
begin = *arg;
|
|
pos = skip_spaces (begin);
|
|
|
|
if (!isdigit (*pos))
|
|
error (_("Expected positive number, got: %s."), pos);
|
|
|
|
number = strtoulst (pos, &end, 10);
|
|
|
|
*arg += (end - begin);
|
|
|
|
return number;
|
|
}
|
|
|
|
/* Read a context size from an argument string. */
|
|
|
|
static int
|
|
get_context_size (const char **arg)
|
|
{
|
|
const char *pos;
|
|
char *end;
|
|
|
|
pos = skip_spaces (*arg);
|
|
|
|
if (!isdigit (*pos))
|
|
error (_("Expected positive number, got: %s."), pos);
|
|
|
|
long result = strtol (pos, &end, 10);
|
|
*arg = end;
|
|
return result;
|
|
}
|
|
|
|
/* Complain about junk at the end of an argument string. */
|
|
|
|
static void
|
|
no_chunk (const char *arg)
|
|
{
|
|
if (*arg != 0)
|
|
error (_("Junk after argument: %s."), arg);
|
|
}
|
|
|
|
/* Read instruction-history modifiers from an argument string. */
|
|
|
|
static gdb_disassembly_flags
|
|
get_insn_history_modifiers (const char **arg)
|
|
{
|
|
gdb_disassembly_flags modifiers;
|
|
const char *args;
|
|
|
|
modifiers = 0;
|
|
args = *arg;
|
|
|
|
if (args == NULL)
|
|
return modifiers;
|
|
|
|
while (*args == '/')
|
|
{
|
|
++args;
|
|
|
|
if (*args == '\0')
|
|
error (_("Missing modifier."));
|
|
|
|
for (; *args; ++args)
|
|
{
|
|
if (isspace (*args))
|
|
break;
|
|
|
|
if (*args == '/')
|
|
continue;
|
|
|
|
switch (*args)
|
|
{
|
|
case 'm':
|
|
case 's':
|
|
modifiers |= DISASSEMBLY_SOURCE;
|
|
modifiers |= DISASSEMBLY_FILENAME;
|
|
break;
|
|
case 'r':
|
|
modifiers |= DISASSEMBLY_RAW_INSN;
|
|
break;
|
|
case 'f':
|
|
modifiers |= DISASSEMBLY_OMIT_FNAME;
|
|
break;
|
|
case 'p':
|
|
modifiers |= DISASSEMBLY_OMIT_PC;
|
|
break;
|
|
default:
|
|
error (_("Invalid modifier: %c."), *args);
|
|
}
|
|
}
|
|
|
|
args = skip_spaces (args);
|
|
}
|
|
|
|
/* Update the argument string. */
|
|
*arg = args;
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
/* The "set record instruction-history-size / set record
|
|
function-call-history-size" commands are unsigned, with UINT_MAX
|
|
meaning unlimited. The target interfaces works with signed int
|
|
though, to indicate direction, so map "unlimited" to INT_MAX, which
|
|
is about the same as unlimited in practice. If the user does have
|
|
a log that huge, she can fetch it in chunks across several requests,
|
|
but she'll likely have other problems first... */
|
|
|
|
static int
|
|
command_size_to_target_size (unsigned int size)
|
|
{
|
|
gdb_assert (size <= INT_MAX || size == UINT_MAX);
|
|
|
|
if (size == UINT_MAX)
|
|
return INT_MAX;
|
|
else
|
|
return size;
|
|
}
|
|
|
|
/* The "record instruction-history" command. */
|
|
|
|
static void
|
|
cmd_record_insn_history (const char *arg, int from_tty)
|
|
{
|
|
require_record_target ();
|
|
|
|
gdb_disassembly_flags flags = get_insn_history_modifiers (&arg);
|
|
|
|
int size = command_size_to_target_size (record_insn_history_size);
|
|
|
|
if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
|
|
target_insn_history (size, flags);
|
|
else if (strcmp (arg, "-") == 0)
|
|
target_insn_history (-size, flags);
|
|
else
|
|
{
|
|
ULONGEST begin, end;
|
|
|
|
begin = get_insn_number (&arg);
|
|
|
|
if (*arg == ',')
|
|
{
|
|
arg = skip_spaces (++arg);
|
|
|
|
if (*arg == '+')
|
|
{
|
|
arg += 1;
|
|
size = get_context_size (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_insn_history_from (begin, size, flags);
|
|
}
|
|
else if (*arg == '-')
|
|
{
|
|
arg += 1;
|
|
size = get_context_size (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_insn_history_from (begin, -size, flags);
|
|
}
|
|
else
|
|
{
|
|
end = get_insn_number (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_insn_history_range (begin, end, flags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
no_chunk (arg);
|
|
|
|
target_insn_history_from (begin, size, flags);
|
|
}
|
|
|
|
dont_repeat ();
|
|
}
|
|
}
|
|
|
|
/* Read function-call-history modifiers from an argument string. */
|
|
|
|
static record_print_flags
|
|
get_call_history_modifiers (const char **arg)
|
|
{
|
|
record_print_flags modifiers = 0;
|
|
const char *args = *arg;
|
|
|
|
if (args == NULL)
|
|
return modifiers;
|
|
|
|
while (*args == '/')
|
|
{
|
|
++args;
|
|
|
|
if (*args == '\0')
|
|
error (_("Missing modifier."));
|
|
|
|
for (; *args; ++args)
|
|
{
|
|
if (isspace (*args))
|
|
break;
|
|
|
|
if (*args == '/')
|
|
continue;
|
|
|
|
switch (*args)
|
|
{
|
|
case 'l':
|
|
modifiers |= RECORD_PRINT_SRC_LINE;
|
|
break;
|
|
case 'i':
|
|
modifiers |= RECORD_PRINT_INSN_RANGE;
|
|
break;
|
|
case 'c':
|
|
modifiers |= RECORD_PRINT_INDENT_CALLS;
|
|
break;
|
|
default:
|
|
error (_("Invalid modifier: %c."), *args);
|
|
}
|
|
}
|
|
|
|
args = skip_spaces (args);
|
|
}
|
|
|
|
/* Update the argument string. */
|
|
*arg = args;
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
/* The "record function-call-history" command. */
|
|
|
|
static void
|
|
cmd_record_call_history (const char *arg, int from_tty)
|
|
{
|
|
require_record_target ();
|
|
|
|
record_print_flags flags = get_call_history_modifiers (&arg);
|
|
|
|
int size = command_size_to_target_size (record_call_history_size);
|
|
|
|
if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
|
|
target_call_history (size, flags);
|
|
else if (strcmp (arg, "-") == 0)
|
|
target_call_history (-size, flags);
|
|
else
|
|
{
|
|
ULONGEST begin, end;
|
|
|
|
begin = get_insn_number (&arg);
|
|
|
|
if (*arg == ',')
|
|
{
|
|
arg = skip_spaces (++arg);
|
|
|
|
if (*arg == '+')
|
|
{
|
|
arg += 1;
|
|
size = get_context_size (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_call_history_from (begin, size, flags);
|
|
}
|
|
else if (*arg == '-')
|
|
{
|
|
arg += 1;
|
|
size = get_context_size (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_call_history_from (begin, -size, flags);
|
|
}
|
|
else
|
|
{
|
|
end = get_insn_number (&arg);
|
|
|
|
no_chunk (arg);
|
|
|
|
target_call_history_range (begin, end, flags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
no_chunk (arg);
|
|
|
|
target_call_history_from (begin, size, flags);
|
|
}
|
|
|
|
dont_repeat ();
|
|
}
|
|
}
|
|
|
|
/* Helper for "set record instruction-history-size" and "set record
|
|
function-call-history-size" input validation. COMMAND_VAR is the
|
|
variable registered in the command as control variable. *SETTING
|
|
is the real setting the command allows changing. */
|
|
|
|
static void
|
|
validate_history_size (unsigned int *command_var, unsigned int *setting)
|
|
{
|
|
if (*command_var != UINT_MAX && *command_var > INT_MAX)
|
|
{
|
|
unsigned int new_value = *command_var;
|
|
|
|
/* Restore previous value. */
|
|
*command_var = *setting;
|
|
error (_("integer %u out of range"), new_value);
|
|
}
|
|
|
|
/* Commit new value. */
|
|
*setting = *command_var;
|
|
}
|
|
|
|
/* Called by do_setshow_command. We only want values in the
|
|
[0..INT_MAX] range, while the command's machinery accepts
|
|
[0..UINT_MAX]. See command_size_to_target_size. */
|
|
|
|
static void
|
|
set_record_insn_history_size (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
validate_history_size (&record_insn_history_size_setshow_var,
|
|
&record_insn_history_size);
|
|
}
|
|
|
|
/* Called by do_setshow_command. We only want values in the
|
|
[0..INT_MAX] range, while the command's machinery accepts
|
|
[0..UINT_MAX]. See command_size_to_target_size. */
|
|
|
|
static void
|
|
set_record_call_history_size (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
validate_history_size (&record_call_history_size_setshow_var,
|
|
&record_call_history_size);
|
|
}
|
|
|
|
void _initialize_record ();
|
|
void
|
|
_initialize_record ()
|
|
{
|
|
struct cmd_list_element *c;
|
|
|
|
add_setshow_zuinteger_cmd ("record", no_class, &record_debug,
|
|
_("Set debugging of record/replay feature."),
|
|
_("Show debugging of record/replay feature."),
|
|
_("When enabled, debugging output for "
|
|
"record/replay feature is displayed."),
|
|
NULL, show_record_debug, &setdebuglist,
|
|
&showdebuglist);
|
|
|
|
add_setshow_uinteger_cmd ("instruction-history-size", no_class,
|
|
&record_insn_history_size_setshow_var, _("\
|
|
Set number of instructions to print in \"record instruction-history\"."), _("\
|
|
Show number of instructions to print in \"record instruction-history\"."), _("\
|
|
A size of \"unlimited\" means unlimited instructions. The default is 10."),
|
|
set_record_insn_history_size, NULL,
|
|
&set_record_cmdlist, &show_record_cmdlist);
|
|
|
|
add_setshow_uinteger_cmd ("function-call-history-size", no_class,
|
|
&record_call_history_size_setshow_var, _("\
|
|
Set number of function to print in \"record function-call-history\"."), _("\
|
|
Show number of functions to print in \"record function-call-history\"."), _("\
|
|
A size of \"unlimited\" means unlimited lines. The default is 10."),
|
|
set_record_call_history_size, NULL,
|
|
&set_record_cmdlist, &show_record_cmdlist);
|
|
|
|
c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
|
|
_("Start recording."),
|
|
&record_cmdlist, "record ", 0, &cmdlist);
|
|
set_cmd_completer (c, filename_completer);
|
|
|
|
add_com_alias ("rec", "record", class_obscure, 1);
|
|
add_basic_prefix_cmd ("record", class_support,
|
|
_("Set record options."), &set_record_cmdlist,
|
|
"set record ", 0, &setlist);
|
|
add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
|
|
add_show_prefix_cmd ("record", class_support,
|
|
_("Show record options."), &show_record_cmdlist,
|
|
"show record ", 0, &showlist);
|
|
add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
|
|
add_prefix_cmd ("record", class_support, info_record_command,
|
|
_("Info record options."), &info_record_cmdlist,
|
|
"info record ", 0, &infolist);
|
|
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
|
|
|
|
c = add_cmd ("save", class_obscure, cmd_record_save,
|
|
_("Save the execution log to a file.\n\
|
|
Usage: record save [FILENAME]\n\
|
|
Default filename is 'gdb_record.PROCESS_ID'."),
|
|
&record_cmdlist);
|
|
set_cmd_completer (c, filename_completer);
|
|
|
|
add_cmd ("delete", class_obscure, cmd_record_delete,
|
|
_("Delete the rest of execution log and start recording it anew."),
|
|
&record_cmdlist);
|
|
add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist);
|
|
add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist);
|
|
|
|
add_cmd ("stop", class_obscure, cmd_record_stop,
|
|
_("Stop the record/replay target."),
|
|
&record_cmdlist);
|
|
add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
|
|
|
|
add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\
|
|
Restore the program to its state at instruction number N.\n\
|
|
Argument is instruction number, as shown by 'info record'."),
|
|
&record_goto_cmdlist, "record goto ", 1, &record_cmdlist);
|
|
|
|
add_cmd ("begin", class_obscure, cmd_record_goto_begin,
|
|
_("Go to the beginning of the execution log."),
|
|
&record_goto_cmdlist);
|
|
add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist);
|
|
|
|
add_cmd ("end", class_obscure, cmd_record_goto_end,
|
|
_("Go to the end of the execution log."),
|
|
&record_goto_cmdlist);
|
|
|
|
add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\
|
|
Print disassembled instructions stored in the execution log.\n\
|
|
With a /m or /s modifier, source lines are included (if available).\n\
|
|
With a /r modifier, raw instructions in hex are included.\n\
|
|
With a /f modifier, function names are omitted.\n\
|
|
With a /p modifier, current position markers are omitted.\n\
|
|
With no argument, disassembles ten more instructions after the previous \
|
|
disassembly.\n\
|
|
\"record instruction-history -\" disassembles ten instructions before a \
|
|
previous disassembly.\n\
|
|
One argument specifies an instruction number as shown by 'info record', and \
|
|
ten instructions are disassembled after that instruction.\n\
|
|
Two arguments with comma between them specify starting and ending instruction \
|
|
numbers to disassemble.\n\
|
|
If the second argument is preceded by '+' or '-', it specifies the distance \
|
|
from the first argument.\n\
|
|
The number of instructions to disassemble can be defined with \"set record \
|
|
instruction-history-size\"."),
|
|
&record_cmdlist);
|
|
|
|
add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\
|
|
Prints the execution history at function granularity.\n\
|
|
It prints one line for each sequence of instructions that belong to the same \
|
|
function.\n\
|
|
Without modifiers, it prints the function name.\n\
|
|
With a /l modifier, the source file and line number range is included.\n\
|
|
With a /i modifier, the instruction number range is included.\n\
|
|
With a /c modifier, the output is indented based on the call stack depth.\n\
|
|
With no argument, prints ten more lines after the previous ten-line print.\n\
|
|
\"record function-call-history -\" prints ten lines before a previous ten-line \
|
|
print.\n\
|
|
One argument specifies a function number as shown by 'info record', and \
|
|
ten lines are printed after that function.\n\
|
|
Two arguments with comma between them specify a range of functions to print.\n\
|
|
If the second argument is preceded by '+' or '-', it specifies the distance \
|
|
from the first argument.\n\
|
|
The number of functions to print can be defined with \"set record \
|
|
function-call-history-size\"."),
|
|
&record_cmdlist);
|
|
|
|
/* Sync command control variables. */
|
|
record_insn_history_size_setshow_var = record_insn_history_size;
|
|
record_call_history_size_setshow_var = record_call_history_size;
|
|
}
|