mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
fc55e99ad7
This patch ensures that all ordinary help strings are wrapped at 80 columns. For the most part this consists of changing code like this (note the embedded \n and the trailing backslash without a newline): -Manage the space-separated list of debuginfod server URLs that GDB will query \ -when missing debuginfo, executables or source files.\nThe default value is \ -copied from the DEBUGINFOD_URLS environment variable."), ... to end each line with \n\, like: +Manage the space-separated list of debuginfod server URLs that GDB will\n\ +query when missing debuginfo, executables or source files.\n\ +The default value is copied from the DEBUGINFOD_URLS environment variable."), Approved-By: Eli Zaretskii <eliz@gnu.org>
910 lines
23 KiB
C
910 lines
23 KiB
C
/* Process record and replay target for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2008-2024 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 "cli/cli-cmds.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 "interps.h"
|
|
#include "top.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;
|
|
static 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) \
|
|
gdb_printf (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 recording is currently active.\n"
|
|
"Use the \"record full\" or \"record btrace\" command 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 ());
|
|
|
|
current_inferior ()->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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
gdb_printf (file, _("Debugging of process record target is %s.\n"),
|
|
value);
|
|
}
|
|
|
|
/* Alias for "target record-full". */
|
|
|
|
static void
|
|
cmd_record_start (const char *args, int from_tty)
|
|
{
|
|
/* As 'record' is a prefix command then if the user types 'record blah'
|
|
GDB will search for the 'blah' sub-command and either run that instead
|
|
of calling this function, or throw an error if 'blah' doesn't exist.
|
|
As a result, we only get here if no args are given. */
|
|
gdb_assert (args == nullptr);
|
|
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))
|
|
{
|
|
gdb_printf (_("Already at end of record list.\n"));
|
|
return;
|
|
}
|
|
|
|
if (!target_supports_delete_record ())
|
|
{
|
|
gdb_printf (_("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);
|
|
|
|
gdb_printf (_("Process record is stopped and all execution "
|
|
"logs are deleted.\n"));
|
|
|
|
interps_notify_record_changed (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)
|
|
{
|
|
gdb_printf (_("No recording is currently active.\n"));
|
|
return;
|
|
}
|
|
|
|
gdb_printf (_("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 'a':
|
|
modifiers |= DISASSEMBLY_OMIT_AUX_INSN;
|
|
break;
|
|
case 'm':
|
|
case 's':
|
|
modifiers |= DISASSEMBLY_SOURCE;
|
|
modifiers |= DISASSEMBLY_FILENAME;
|
|
break;
|
|
case 'r':
|
|
modifiers |= DISASSEMBLY_RAW_INSN;
|
|
break;
|
|
case 'b':
|
|
modifiers |= DISASSEMBLY_RAW_BYTES;
|
|
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;
|
|
case 'a':
|
|
modifiers |= RECORD_DONT_PRINT_AUX;
|
|
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);
|
|
|
|
cmd_list_element *record_cmd
|
|
= add_prefix_cmd ("record", class_obscure, cmd_record_start,
|
|
_("Start recording."),
|
|
&record_cmdlist, 0, &cmdlist);
|
|
add_com_alias ("rec", record_cmd, class_obscure, 1);
|
|
|
|
set_show_commands setshow_record_cmds
|
|
= add_setshow_prefix_cmd ("record", class_support,
|
|
_("Set record options."),
|
|
_("Show record options."),
|
|
&set_record_cmdlist, &show_record_cmdlist,
|
|
&setlist, &showlist);
|
|
|
|
|
|
add_alias_cmd ("rec", setshow_record_cmds.set, class_obscure, 1, &setlist);
|
|
add_alias_cmd ("rec", setshow_record_cmds.show, class_obscure, 1, &showlist);
|
|
|
|
cmd_list_element *info_record_cmd
|
|
= add_prefix_cmd ("record", class_support, info_record_command,
|
|
_("Info record options."), &info_record_cmdlist,
|
|
0, &infolist);
|
|
add_alias_cmd ("rec", info_record_cmd, 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, deprecated_filename_completer);
|
|
|
|
cmd_list_element *record_delete_cmd
|
|
= 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", record_delete_cmd, class_obscure, 1, &record_cmdlist);
|
|
add_alias_cmd ("del", record_delete_cmd, class_obscure, 1, &record_cmdlist);
|
|
|
|
cmd_list_element *record_stop_cmd
|
|
= add_cmd ("stop", class_obscure, cmd_record_stop,
|
|
_("Stop the record/replay target."),
|
|
&record_cmdlist);
|
|
add_alias_cmd ("s", record_stop_cmd, 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, 1, &record_cmdlist);
|
|
|
|
cmd_list_element *record_goto_begin_cmd
|
|
= add_cmd ("begin", class_obscure, cmd_record_goto_begin,
|
|
_("Go to the beginning of the execution log."),
|
|
&record_goto_cmdlist);
|
|
add_alias_cmd ("start", record_goto_begin_cmd, 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 a /a modifier, omits output of auxiliary data, which is enabled\n\
|
|
by default.\n\
|
|
With no argument, disassembles ten more instructions after the previous\n\
|
|
disassembly.\n\
|
|
\"record instruction-history -\" disassembles ten instructions before a\n\
|
|
previous disassembly.\n\
|
|
One argument specifies an instruction number as shown by 'info record', and\n\
|
|
ten instructions are disassembled after that instruction.\n\
|
|
Two arguments with comma between them specify starting and ending instruction\n\
|
|
numbers to disassemble.\n\
|
|
If the second argument is preceded by '+' or '-', it specifies the distance\n\
|
|
from the first argument.\n\
|
|
The number of instructions to disassemble can be defined with\n\
|
|
\"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\n\
|
|
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 a /a modifier, omits output of auxiliary data, which is enabled \
|
|
by default.\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\n\
|
|
print.\n\
|
|
One argument specifies a function number as shown by 'info record', and\n\
|
|
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\n\
|
|
from the first argument.\n\
|
|
The number of functions to print can be defined with\n\
|
|
\"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;
|
|
}
|