/* Process record and replay target for GDB, the GNU debugger.
- Copyright (C) 2013-2021 Free Software Foundation, Inc.
+ Copyright (C) 2013-2023 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbsupport/gdb_unlinker.h"
#include "gdbsupport/byte-vector.h"
#include "async-event.h"
+#include "valprint.h"
+#include "interps.h"
#include <signal.h>
/* Record buf with core target. */
static detached_regcache *record_full_core_regbuf = NULL;
-static target_section_table record_full_core_sections;
+static std::vector<target_section> record_full_core_sections;
static struct record_full_core_buf_entry *record_full_core_buf_list = NULL;
/* The following variables are used for managing the linked list that
strata stratum () const override { return record_stratum; }
void close () override;
- void async (int) override;
+ void async (bool) override;
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
bool stopped_by_watchpoint () override;
bool stopped_data_address (CORE_ADDR *) override;
const target_info &info () const override
{ return record_full_target_info; }
- void commit_resume () override;
void resume (ptid_t, int, enum gdb_signal) override;
void disconnect (const char *, int) override;
void detach (inferior *, int) override;
record_full_arch_list_add (struct record_full_entry *rec)
{
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_arch_list_add %s.\n",
- host_address_to_string (rec));
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_arch_list_add %s.\n",
+ host_address_to_string (rec));
if (record_full_arch_list_tail)
{
struct record_full_entry *rec;
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: add register num = %d to "
- "record list.\n",
- regnum);
+ gdb_printf (gdb_stdlog,
+ "Process record: add register num = %d to "
+ "record list.\n",
+ regnum);
rec = record_full_reg_alloc (regcache, regnum);
struct record_full_entry *rec;
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: add mem addr = %s len = %d to "
- "record list.\n",
- paddress (target_gdbarch (), addr), len);
+ gdb_printf (gdb_stdlog,
+ "Process record: add mem addr = %s len = %d to "
+ "record list.\n",
+ paddress (current_inferior ()->arch (), addr), len);
if (!addr) /* FIXME: Why? Some arch must permit it... */
return 0;
rec = record_full_mem_alloc (addr, len);
- if (record_read_memory (target_gdbarch (), addr,
+ if (record_read_memory (current_inferior ()->arch (), addr,
record_full_get_loc (rec), len))
{
record_full_mem_release (rec);
struct record_full_entry *rec;
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: add end to arch list.\n");
+ gdb_printf (gdb_stdlog,
+ "Process record: add end to arch list.\n");
rec = record_full_end_alloc ();
rec->u.end.sigval = GDB_SIGNAL_0;
{
record_full_message (regcache, signal);
}
- catch (const gdb_exception &ex)
+ catch (const gdb_exception_error &ex)
{
exception_print (gdb_stderr, ex);
return false;
gdb::byte_vector reg (entry->u.reg.len);
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_reg %s to "
- "inferior num = %d.\n",
- host_address_to_string (entry),
- entry->u.reg.num);
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_reg %s to "
+ "inferior num = %d.\n",
+ host_address_to_string (entry),
+ entry->u.reg.num);
regcache->cooked_read (entry->u.reg.num, reg.data ());
regcache->cooked_write (entry->u.reg.num, record_full_get_loc (entry));
gdb::byte_vector mem (entry->u.mem.len);
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_mem %s to "
- "inferior addr = %s len = %d.\n",
- host_address_to_string (entry),
- paddress (gdbarch, entry->u.mem.addr),
- entry->u.mem.len);
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_mem %s to "
+ "inferior addr = %s len = %d.\n",
+ host_address_to_string (entry),
+ paddress (gdbarch, entry->u.mem.addr),
+ entry->u.mem.len);
if (record_read_memory (gdbarch,
entry->u.mem.addr, mem.data (),
not doing the change at all if the watchpoint
traps. */
if (hardware_watchpoint_inserted_in_range
- (regcache->aspace (),
+ (current_inferior ()->aspace.get (),
entry->u.mem.addr, entry->u.mem.len))
record_full_stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
}
static void
record_full_core_open_1 (const char *name, int from_tty)
{
- struct regcache *regcache = get_current_regcache ();
+ regcache *regcache = get_thread_regcache (inferior_thread ());
int regnum = gdbarch_num_regs (regcache->arch ());
int i;
record_full_core_sections = build_section_table (core_bfd);
- push_target (&record_full_core_ops);
+ current_inferior ()->push_target (&record_full_core_ops);
record_full_restore ();
}
record_full_open_1 (const char *name, int from_tty)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_full_open_1\n");
+ gdb_printf (gdb_stdlog, "Process record: record_full_open_1\n");
/* check exec */
if (!target_has_execution ())
error (_("Process record target can't debug inferior in non-stop mode "
"(non-stop)."));
- if (!gdbarch_process_record_p (target_gdbarch ()))
+ if (!gdbarch_process_record_p (current_inferior ()->arch ()))
error (_("Process record: the current architecture doesn't support "
"record function."));
- push_target (&record_full_ops);
+ current_inferior ()->push_target (&record_full_ops);
}
static void record_full_init_record_breakpoints (void);
record_full_open (const char *name, int from_tty)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_full_open\n");
+ gdb_printf (gdb_stdlog, "Process record: record_full_open\n");
record_preopen ();
record_full_init_record_breakpoints ();
- gdb::observers::record_changed.notify (current_inferior (), 1, "full", NULL);
+ interps_notify_record_changed (current_inferior (), 1, "full", NULL);
}
/* "close" target method. Close the process record target. */
struct record_full_core_buf_entry *entry;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_full_close\n");
+ gdb_printf (gdb_stdlog, "Process record: record_full_close\n");
record_full_list_release (record_full_list);
/* "async" target method. */
void
-record_full_base_target::async (int enable)
+record_full_base_target::async (bool enable)
{
if (enable)
mark_async_event_handler (record_full_async_inferior_event_token);
{
struct gdbarch *gdbarch = target_thread_architecture (ptid);
- record_full_message (get_current_regcache (), signal);
+ record_full_message (get_thread_regcache (inferior_thread ()), signal);
if (!step)
{
/* Make sure the target beneath reports all signals. */
target_pass_signals ({});
+ /* Disable range-stepping, forcing the process target to report stops for
+ all executed instructions, so we can record them all. */
+ process_stratum_target *proc_target
+ = current_inferior ()->process_target ();
+ for (thread_info *thread : all_non_exited_threads (proc_target, ptid))
+ thread->control.may_range_step = 0;
+
this->beneath ()->resume (ptid, step, signal);
}
-
- /* We are about to start executing the inferior (or simulate it),
- let's register it with the event loop. */
- if (target_can_async_p ())
- target_async (1);
-}
-
-/* "commit_resume" method for process record target. */
-
-void
-record_full_target::commit_resume ()
-{
- if (!RECORD_FULL_IS_REPLAY)
- beneath ()->commit_resume ();
}
static int record_full_get_sig = 0;
record_full_sig_handler (int signo)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
+ gdb_printf (gdb_stdlog, "Process record: get a signal\n");
/* It will break the running inferior in replay mode. */
record_full_resume_step = 1;
= record_full_gdb_operation_disable_set ();
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_wait "
- "record_full_resume_step = %d, "
- "record_full_resumed = %d, direction=%s\n",
- record_full_resume_step, record_full_resumed,
- record_full_execution_dir == EXEC_FORWARD
- ? "forward" : "reverse");
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_wait "
+ "record_full_resume_step = %d, "
+ "record_full_resumed = %d, direction=%s\n",
+ record_full_resume_step, record_full_resumed,
+ record_full_execution_dir == EXEC_FORWARD
+ ? "forward" : "reverse");
if (!record_full_resumed)
{
gdb_assert ((options & TARGET_WNOHANG) != 0);
/* No interesting event. */
- status->kind = TARGET_WAITKIND_IGNORE;
+ status->set_ignore ();
return minus_one_ptid;
}
while (1)
{
ret = ops->beneath ()->wait (ptid, status, options);
- if (status->kind == TARGET_WAITKIND_IGNORE)
+ if (status->kind () == TARGET_WAITKIND_IGNORE)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_wait "
- "target beneath not done yet\n");
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_wait "
+ "target beneath not done yet\n");
return ret;
}
return ret;
/* Is this a SIGTRAP? */
- if (status->kind == TARGET_WAITKIND_STOPPED
- && status->value.sig == GDB_SIGNAL_TRAP)
+ if (status->kind () == TARGET_WAITKIND_STOPPED
+ && status->sig () == GDB_SIGNAL_TRAP)
{
struct regcache *regcache;
enum target_stop_reason *stop_reason_p
registers_changed ();
switch_to_thread (current_inferior ()->process_target (),
ret);
- regcache = get_current_regcache ();
+ regcache = get_thread_regcache (inferior_thread ());
tmp_pc = regcache_read_pc (regcache);
- const struct address_space *aspace = regcache->aspace ();
+ const address_space *aspace
+ = current_inferior ()->aspace.get ();
if (target_stopped_by_watchpoint ())
{
if (!record_full_message_wrapper_safe (regcache,
GDB_SIGNAL_0))
- {
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = GDB_SIGNAL_0;
+ {
+ status->set_stopped (GDB_SIGNAL_0);
break;
- }
+ }
+
+ process_stratum_target *proc_target
+ = current_inferior ()->process_target ();
if (gdbarch_software_single_step_p (gdbarch))
{
- process_stratum_target *proc_target
- = current_inferior ()->process_target ();
-
/* Try to insert the software single step breakpoint.
If insert success, set step to 0. */
set_executing (proc_target, inferior_ptid, false);
- reinit_frame_cache ();
+ SCOPE_EXIT
+ {
+ set_executing (proc_target, inferior_ptid, true);
+ };
+ reinit_frame_cache ();
step = !insert_single_step_breakpoints (gdbarch);
-
- set_executing (proc_target, inferior_ptid, true);
}
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_full_wait "
- "issuing one more step in the "
- "target beneath\n");
+ gdb_printf (gdb_stdlog,
+ "Process record: record_full_wait "
+ "issuing one more step in the "
+ "target beneath\n");
ops->beneath ()->resume (ptid, step, GDB_SIGNAL_0);
- ops->beneath ()->commit_resume ();
+ proc_target->commit_resumed_state = true;
+ proc_target->commit_resumed ();
+ proc_target->commit_resumed_state = false;
continue;
}
}
{
switch_to_thread (current_inferior ()->process_target (),
record_full_resume_ptid);
- struct regcache *regcache = get_current_regcache ();
+ regcache *regcache = get_thread_regcache (inferior_thread ());
struct gdbarch *gdbarch = regcache->arch ();
- const struct address_space *aspace = regcache->aspace ();
+ const address_space *aspace = current_inferior ()->aspace.get ();
int continue_flag = 1;
int first_record_full_end = 1;
CORE_ADDR tmp_pc;
record_full_stop_reason = TARGET_STOPPED_BY_NO_REASON;
- status->kind = TARGET_WAITKIND_STOPPED;
+ status->set_stopped (GDB_SIGNAL_0);
/* Check breakpoint when forward execute. */
if (execution_direction == EXEC_FORWARD)
&record_full_stop_reason))
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: break at %s.\n",
- paddress (gdbarch, tmp_pc));
+ gdb_printf (gdb_stdlog,
+ "Process record: break at %s.\n",
+ paddress (gdbarch, tmp_pc));
goto replay_out;
}
}
&& record_full_list == &record_full_first)
{
/* Hit beginning of record log in reverse. */
- status->kind = TARGET_WAITKIND_NO_HISTORY;
+ status->set_no_history ();
break;
}
if (execution_direction != EXEC_REVERSE
&& !record_full_list->next)
{
/* Hit end of record log going forward. */
- status->kind = TARGET_WAITKIND_NO_HISTORY;
+ status->set_no_history ();
break;
}
if (record_full_list->type == record_full_end)
{
if (record_debug > 1)
- fprintf_unfiltered
+ gdb_printf
(gdb_stdlog,
"Process record: record_full_end %s to "
"inferior.\n",
if (record_full_resume_step)
{
if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: step.\n");
+ gdb_printf (gdb_stdlog,
+ "Process record: step.\n");
continue_flag = 0;
}
(aspace, tmp_pc, &record_full_stop_reason))
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: break "
- "at %s.\n",
- paddress (gdbarch, tmp_pc));
+ gdb_printf (gdb_stdlog,
+ "Process record: break "
+ "at %s.\n",
+ paddress (gdbarch, tmp_pc));
continue_flag = 0;
}
== TARGET_STOPPED_BY_WATCHPOINT)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: hit hw "
- "watchpoint.\n");
+ gdb_printf (gdb_stdlog,
+ "Process record: hit hw "
+ "watchpoint.\n");
continue_flag = 0;
}
/* Check target signal */
while (continue_flag);
replay_out:
- if (record_full_get_sig)
- status->value.sig = GDB_SIGNAL_INT;
- else if (record_full_list->u.end.sigval != GDB_SIGNAL_0)
- /* FIXME: better way to check */
- status->value.sig = record_full_list->u.end.sigval;
- else
- status->value.sig = GDB_SIGNAL_TRAP;
+ if (status->kind () == TARGET_WAITKIND_STOPPED)
+ {
+ if (record_full_get_sig)
+ status->set_stopped (GDB_SIGNAL_INT);
+ else if (record_full_list->u.end.sigval != GDB_SIGNAL_0)
+ /* FIXME: better way to check */
+ status->set_stopped (record_full_list->u.end.sigval);
+ else
+ status->set_stopped (GDB_SIGNAL_TRAP);
+ }
}
catch (const gdb_exception &ex)
{
{
ptid_t return_ptid;
+ clear_async_event_handler (record_full_async_inferior_event_token);
+
return_ptid = record_full_wait_1 (this, ptid, status, options);
- if (status->kind != TARGET_WAITKIND_IGNORE)
+ if (status->kind () != TARGET_WAITKIND_IGNORE)
{
/* We're reporting a stop. Make sure any spurious
target_wait(WNOHANG) doesn't advance the target until the
if (!query (_("Because GDB is in replay mode, writing to memory "
"will make the execution log unusable from this "
"point onward. Write memory at address %s?"),
- paddress (target_gdbarch (), offset)))
+ paddress (current_inferior ()->arch (), offset)))
error (_("Process record canceled the operation."));
/* Destroy the record from here forward. */
{
record_full_list_release (record_full_arch_list_tail);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: failed to record "
- "execution log.");
+ gdb_printf (gdb_stdlog,
+ "Process record: failed to record "
+ "execution log.");
return TARGET_XFER_E_IO;
}
if (record_full_arch_list_add_end ())
{
record_full_list_release (record_full_arch_list_tail);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: failed to record "
- "execution log.");
+ gdb_printf (gdb_stdlog,
+ "Process record: failed to record "
+ "execution log.");
return TARGET_XFER_E_IO;
}
record_full_list->next = record_full_arch_list_head;
active. */
static std::vector<record_full_breakpoint> record_full_breakpoints;
-static void
-record_full_sync_record_breakpoints (struct bp_location *loc, void *data)
-{
- if (loc->loc_type != bp_loc_software_breakpoint)
- return;
-
- if (loc->inserted)
- {
- record_full_breakpoints.emplace_back
- (loc->target_info.placed_address_space,
- loc->target_info.placed_address,
- 1);
- }
-}
-
/* Sync existing breakpoints to record_full_breakpoints. */
static void
{
record_full_breakpoints.clear ();
- iterate_over_bp_locations (record_full_sync_record_breakpoints);
+ for (bp_location *loc : all_bp_locations ())
+ {
+ if (loc->loc_type != bp_loc_software_breakpoint)
+ continue;
+
+ if (loc->inserted)
+ record_full_breakpoints.emplace_back
+ (loc->target_info.placed_address_space,
+ loc->target_info.placed_address, 1);
+ }
}
/* Behavior is conditional on RECORD_FULL_IS_REPLAY. We will not actually
if (record_debug)
{
if (ret)
- fprintf_unfiltered (gdb_stdlog,
- "record_full_get_bookmark returns %s\n", ret);
+ gdb_printf (gdb_stdlog,
+ "record_full_get_bookmark returns %s\n", ret);
else
- fprintf_unfiltered (gdb_stdlog,
- "record_full_get_bookmark returns NULL\n");
+ gdb_printf (gdb_stdlog,
+ "record_full_get_bookmark returns NULL\n");
}
return (gdb_byte *) ret;
}
const char *bookmark = (const char *) raw_bookmark;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "record_full_goto_bookmark receives %s\n", bookmark);
+ gdb_printf (gdb_stdlog,
+ "record_full_goto_bookmark receives %s\n", bookmark);
std::string name_holder;
if (bookmark[0] == '\'' || bookmark[0] == '\"')
struct record_full_entry *p;
if (RECORD_FULL_IS_REPLAY)
- printf_filtered (_("Replay mode:\n"));
+ gdb_printf (_("Replay mode:\n"));
else
- printf_filtered (_("Record mode:\n"));
+ gdb_printf (_("Record mode:\n"));
/* Find entry for first actual instruction in the log. */
for (p = record_full_first.next;
if (p != NULL && p->type == record_full_end)
{
/* Display instruction number for first instruction in the log. */
- printf_filtered (_("Lowest recorded instruction number is %s.\n"),
- pulongest (p->u.end.insn_num));
+ gdb_printf (_("Lowest recorded instruction number is %s.\n"),
+ pulongest (p->u.end.insn_num));
/* If in replay mode, display where we are in the log. */
if (RECORD_FULL_IS_REPLAY)
- printf_filtered (_("Current instruction number is %s.\n"),
- pulongest (record_full_list->u.end.insn_num));
+ gdb_printf (_("Current instruction number is %s.\n"),
+ pulongest (record_full_list->u.end.insn_num));
/* Display instruction number for last instruction in the log. */
- printf_filtered (_("Highest recorded instruction number is %s.\n"),
- pulongest (record_full_insn_count));
+ gdb_printf (_("Highest recorded instruction number is %s.\n"),
+ pulongest (record_full_insn_count));
/* Display log count. */
- printf_filtered (_("Log contains %u instructions.\n"),
- record_full_insn_num);
+ gdb_printf (_("Log contains %u instructions.\n"),
+ record_full_insn_num);
}
else
- printf_filtered (_("No instructions have been logged.\n"));
+ gdb_printf (_("No instructions have been logged.\n"));
/* Display max log size. */
- printf_filtered (_("Max logged instructions is %u.\n"),
- record_full_insn_max_num);
+ gdb_printf (_("Max logged instructions is %u.\n"),
+ record_full_insn_max_num);
}
bool
error (_("Already at target insn."));
else if (p->u.end.insn_num > record_full_list->u.end.insn_num)
{
- printf_filtered (_("Go forward to insn number %s\n"),
- pulongest (p->u.end.insn_num));
+ gdb_printf (_("Go forward to insn number %s\n"),
+ pulongest (p->u.end.insn_num));
record_full_goto_insn (p, EXEC_FORWARD);
}
else
{
- printf_filtered (_("Go backward to insn number %s\n"),
- pulongest (p->u.end.insn_num));
+ gdb_printf (_("Go backward to insn number %s\n"),
+ pulongest (p->u.end.insn_num));
record_full_goto_insn (p, EXEC_REVERSE);
}
registers_changed ();
reinit_frame_cache ();
- inferior_thread ()->suspend.stop_pc
- = regcache_read_pc (get_current_regcache ());
+
+ thread_info *thr = inferior_thread ();
+ thr->set_stop_pc (regcache_read_pc (get_thread_regcache (thr)));
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
}
record_full_resume_step = step;
record_full_resumed = 1;
record_full_execution_dir = ::execution_direction;
-
- /* We are about to start executing the inferior (or simulate it),
- let's register it with the event loop. */
- if (target_can_async_p ())
- target_async (1);
}
/* "kill" method for prec over corefile. */
record_full_core_target::kill ()
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_full_core_kill\n");
+ gdb_printf (gdb_stdlog, "Process record: record_full_core_kill\n");
- unpush_target (this);
+ current_inferior ()->unpush_target (this);
}
/* "fetch_registers" method for prec over corefile. */
asection *osec;
uint32_t osec_size;
int bfd_offset = 0;
- struct regcache *regcache;
/* We restore the execution log from the open core bfd,
if there is one. */
gdb_assert (record_full_first.next == NULL);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Restoring recording from core file.\n");
+ gdb_printf (gdb_stdlog, "Restoring recording from core file.\n");
/* Now need to find our special note section. */
osec = bfd_get_section_by_name (core_bfd, "null0");
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
- osec ? "succeeded" : "failed");
+ gdb_printf (gdb_stdlog, "Find precord section %s.\n",
+ osec ? "succeeded" : "failed");
if (osec == NULL)
return;
osec_size = bfd_section_size (osec);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (osec));
+ gdb_printf (gdb_stdlog, "%s", bfd_section_name (osec));
/* Check the magic code. */
bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
error (_("Version mis-match or file format error in core file %s."),
bfd_get_filename (core_bfd));
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Reading 4-byte magic cookie "
- "RECORD_FULL_FILE_MAGIC (0x%s)\n",
- phex_nz (netorder32 (magic), 4));
+ gdb_printf (gdb_stdlog,
+ " Reading 4-byte magic cookie "
+ "RECORD_FULL_FILE_MAGIC (0x%s)\n",
+ phex_nz (netorder32 (magic), 4));
/* Restore the entries in recfd into record_full_arch_list_head and
record_full_arch_list_tail. */
try
{
- regcache = get_current_regcache ();
+ regcache *regcache = get_thread_regcache (inferior_thread ());
while (1)
{
rec->u.reg.len, &bfd_offset);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Reading register %d (1 "
- "plus %lu plus %d bytes)\n",
- rec->u.reg.num,
- (unsigned long) sizeof (regnum),
- rec->u.reg.len);
+ gdb_printf (gdb_stdlog,
+ " Reading register %d (1 "
+ "plus %lu plus %d bytes)\n",
+ rec->u.reg.num,
+ (unsigned long) sizeof (regnum),
+ rec->u.reg.len);
break;
case record_full_mem: /* mem */
rec->u.mem.len, &bfd_offset);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Reading memory %s (1 plus "
- "%lu plus %lu plus %d bytes)\n",
- paddress (get_current_arch (),
- rec->u.mem.addr),
- (unsigned long) sizeof (addr),
- (unsigned long) sizeof (len),
- rec->u.mem.len);
+ gdb_printf (gdb_stdlog,
+ " Reading memory %s (1 plus "
+ "%lu plus %lu plus %d bytes)\n",
+ paddress (get_current_arch (),
+ rec->u.mem.addr),
+ (unsigned long) sizeof (addr),
+ (unsigned long) sizeof (len),
+ rec->u.mem.len);
break;
case record_full_end: /* end */
rec->u.end.insn_num = count;
record_full_insn_count = count + 1;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Reading record_full_end (1 + "
- "%lu + %lu bytes), offset == %s\n",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count),
- paddress (get_current_arch (),
- bfd_offset));
+ gdb_printf (gdb_stdlog,
+ " Reading record_full_end (1 + "
+ "%lu + %lu bytes), offset == %s\n",
+ (unsigned long) sizeof (signal),
+ (unsigned long) sizeof (count),
+ paddress (get_current_arch (),
+ bfd_offset));
break;
default:
}
/* Succeeded. */
- printf_filtered (_("Restored records from core file %s.\n"),
- bfd_get_filename (core_bfd));
+ gdb_printf (_("Restored records from core file %s.\n"),
+ bfd_get_filename (core_bfd));
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
}
{
struct record_full_entry *cur_record_full_list;
uint32_t magic;
- struct regcache *regcache;
struct gdbarch *gdbarch;
int save_size = 0;
asection *osec = NULL;
/* Open the save file. */
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
- recfilename);
+ gdb_printf (gdb_stdlog, "Saving execution log to core file '%s'\n",
+ recfilename);
/* Open the output file. */
gdb_bfd_ref_ptr obfd (create_gcore_bfd (recfilename));
cur_record_full_list = record_full_list;
/* Get the values of regcache and gdbarch. */
- regcache = get_current_regcache ();
+ regcache *regcache = get_thread_regcache (inferior_thread ());
gdbarch = regcache->arch ();
/* Disable the GDB operation record. */
/* Write the magic code. */
magic = RECORD_FULL_FILE_MAGIC;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Writing 4-byte magic cookie "
- "RECORD_FULL_FILE_MAGIC (0x%s)\n",
- phex_nz (magic, 4));
+ gdb_printf (gdb_stdlog,
+ " Writing 4-byte magic cookie "
+ "RECORD_FULL_FILE_MAGIC (0x%s)\n",
+ phex_nz (magic, 4));
bfdcore_write (obfd.get (), osec, &magic, sizeof (magic), &bfd_offset);
/* Save the entries to recfd and forward execute to the end of
{
case record_full_reg: /* reg */
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Writing register %d (1 "
- "plus %lu plus %d bytes)\n",
- record_full_list->u.reg.num,
- (unsigned long) sizeof (regnum),
- record_full_list->u.reg.len);
+ gdb_printf (gdb_stdlog,
+ " Writing register %d (1 "
+ "plus %lu plus %d bytes)\n",
+ record_full_list->u.reg.num,
+ (unsigned long) sizeof (regnum),
+ record_full_list->u.reg.len);
/* Write regnum. */
regnum = netorder32 (record_full_list->u.reg.num);
case record_full_mem: /* mem */
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Writing memory %s (1 plus "
- "%lu plus %lu plus %d bytes)\n",
- paddress (gdbarch,
- record_full_list->u.mem.addr),
- (unsigned long) sizeof (addr),
- (unsigned long) sizeof (len),
- record_full_list->u.mem.len);
+ gdb_printf (gdb_stdlog,
+ " Writing memory %s (1 plus "
+ "%lu plus %lu plus %d bytes)\n",
+ paddress (gdbarch,
+ record_full_list->u.mem.addr),
+ (unsigned long) sizeof (addr),
+ (unsigned long) sizeof (len),
+ record_full_list->u.mem.len);
/* Write memlen. */
len = netorder32 (record_full_list->u.mem.len);
case record_full_end:
if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- " Writing record_full_end (1 + "
- "%lu + %lu bytes)\n",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count));
+ gdb_printf (gdb_stdlog,
+ " Writing record_full_end (1 + "
+ "%lu + %lu bytes)\n",
+ (unsigned long) sizeof (signal),
+ (unsigned long) sizeof (count));
/* Write signal value. */
signal = netorder32 (record_full_list->u.end.sigval);
bfdcore_write (obfd.get (), osec, &signal,
unlink_file.keep ();
/* Succeeded. */
- printf_filtered (_("Saved core file %s with execution log.\n"),
- recfilename);
+ gdb_printf (_("Saved core file %s with execution log.\n"),
+ recfilename);
}
/* record_full_goto_insn -- rewind the record log (forward or backward,
{
scoped_restore restore_operation_disable
= record_full_gdb_operation_disable_set ();
- struct regcache *regcache = get_current_regcache ();
+ regcache *regcache = get_thread_regcache (inferior_thread ());
struct gdbarch *gdbarch = regcache->arch ();
/* Assume everything is valid: we will hit the entry,
}
}
+/* Implement the 'maintenance print record-instruction' command. */
+
+static void
+maintenance_print_record_instruction (const char *args, int from_tty)
+{
+ struct record_full_entry *to_print = record_full_list;
+
+ if (args != nullptr)
+ {
+ int offset = value_as_long (parse_and_eval (args));
+ if (offset > 0)
+ {
+ /* Move forward OFFSET instructions. We know we found the
+ end of an instruction when to_print->type is record_full_end. */
+ while (to_print->next != nullptr && offset > 0)
+ {
+ to_print = to_print->next;
+ if (to_print->type == record_full_end)
+ offset--;
+ }
+ if (offset != 0)
+ error (_("Not enough recorded history"));
+ }
+ else
+ {
+ while (to_print->prev != nullptr && offset < 0)
+ {
+ to_print = to_print->prev;
+ if (to_print->type == record_full_end)
+ offset++;
+ }
+ if (offset != 0)
+ error (_("Not enough recorded history"));
+ }
+ }
+ gdb_assert (to_print != nullptr);
+
+ gdbarch *arch = current_inferior ()->arch ();
+
+ /* Go back to the start of the instruction. */
+ while (to_print->prev != nullptr && to_print->prev->type != record_full_end)
+ to_print = to_print->prev;
+
+ /* if we're in the first record, there are no actual instructions
+ recorded. Warn the user and leave. */
+ if (to_print == &record_full_first)
+ error (_("Not enough recorded history"));
+
+ while (to_print->type != record_full_end)
+ {
+ switch (to_print->type)
+ {
+ case record_full_reg:
+ {
+ type *regtype = gdbarch_register_type (arch, to_print->u.reg.num);
+ value *val
+ = value_from_contents (regtype,
+ record_full_get_loc (to_print));
+ gdb_printf ("Register %s changed: ",
+ gdbarch_register_name (arch, to_print->u.reg.num));
+ struct value_print_options opts;
+ get_user_print_options (&opts);
+ opts.raw = true;
+ value_print (val, gdb_stdout, &opts);
+ gdb_printf ("\n");
+ break;
+ }
+ case record_full_mem:
+ {
+ gdb_byte *b = record_full_get_loc (to_print);
+ gdb_printf ("%d bytes of memory at address %s changed from:",
+ to_print->u.mem.len,
+ print_core_address (arch, to_print->u.mem.addr));
+ for (int i = 0; i < to_print->u.mem.len; i++)
+ gdb_printf (" %02x", b[i]);
+ gdb_printf ("\n");
+ break;
+ }
+ }
+ to_print = to_print->next;
+ }
+}
+
void _initialize_record_full ();
void
_initialize_record_full ()
add_prefix_cmd ("full", class_obscure, cmd_record_full_start,
_("Start full execution recording."), &record_full_cmdlist,
- "record full ", 0, &record_cmdlist);
+ 0, &record_cmdlist);
- c = add_cmd ("restore", class_obscure, cmd_record_full_restore,
+ cmd_list_element *record_full_restore_cmd
+ = add_cmd ("restore", class_obscure, cmd_record_full_restore,
_("Restore the execution log from a file.\n\
Argument is filename. File must be created with 'record save'."),
&record_full_cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer (record_full_restore_cmd, filename_completer);
/* Deprecate the old version without "full" prefix. */
- c = add_alias_cmd ("restore", "full restore", class_obscure, 1,
+ c = add_alias_cmd ("restore", record_full_restore_cmd, class_obscure, 1,
&record_cmdlist);
set_cmd_completer (c, filename_completer);
deprecate_cmd (c, "record full restore");
- add_basic_prefix_cmd ("full", class_support,
- _("Set record options."), &set_record_full_cmdlist,
- "set record full ", 0, &set_record_cmdlist);
-
- add_show_prefix_cmd ("full", class_support,
- _("Show record options."), &show_record_full_cmdlist,
- "show record full ", 0, &show_record_cmdlist);
+ add_setshow_prefix_cmd ("full", class_support,
+ _("Set record options."),
+ _("Show record options."),
+ &set_record_full_cmdlist,
+ &show_record_full_cmdlist,
+ &set_record_cmdlist,
+ &show_record_cmdlist);
/* Record instructions number limit command. */
- add_setshow_boolean_cmd ("stop-at-limit", no_class,
- &record_full_stop_at_limit, _("\
+ set_show_commands set_record_full_stop_at_limit_cmds
+ = add_setshow_boolean_cmd ("stop-at-limit", no_class,
+ &record_full_stop_at_limit, _("\
Set whether record/replay stops when record/replay buffer becomes full."), _("\
Show whether record/replay stops when record/replay buffer becomes full."),
_("Default is ON.\n\
When ON, if the record/replay buffer becomes full, ask user what to do.\n\
When OFF, if the record/replay buffer becomes full,\n\
delete the oldest recorded instruction to make room for each new one."),
- NULL, NULL,
- &set_record_full_cmdlist, &show_record_full_cmdlist);
+ NULL, NULL,
+ &set_record_full_cmdlist,
+ &show_record_full_cmdlist);
- c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
+ c = add_alias_cmd ("stop-at-limit",
+ set_record_full_stop_at_limit_cmds.set, no_class, 1,
&set_record_cmdlist);
deprecate_cmd (c, "set record full stop-at-limit");
- c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
+ c = add_alias_cmd ("stop-at-limit",
+ set_record_full_stop_at_limit_cmds.show, no_class, 1,
&show_record_cmdlist);
deprecate_cmd (c, "show record full stop-at-limit");
- add_setshow_uinteger_cmd ("insn-number-max", no_class,
- &record_full_insn_max_num,
- _("Set record/replay buffer limit."),
- _("Show record/replay buffer limit."), _("\
+ set_show_commands record_full_insn_number_max_cmds
+ = add_setshow_uinteger_cmd ("insn-number-max", no_class,
+ &record_full_insn_max_num,
+ _("Set record/replay buffer limit."),
+ _("Show record/replay buffer limit."), _("\
Set the maximum number of instructions to be stored in the\n\
record/replay buffer. A value of either \"unlimited\" or zero means no\n\
limit. Default is 200000."),
- set_record_full_insn_max_num,
- NULL, &set_record_full_cmdlist,
- &show_record_full_cmdlist);
+ set_record_full_insn_max_num,
+ NULL, &set_record_full_cmdlist,
+ &show_record_full_cmdlist);
- c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
- &set_record_cmdlist);
+ c = add_alias_cmd ("insn-number-max", record_full_insn_number_max_cmds.set,
+ no_class, 1, &set_record_cmdlist);
deprecate_cmd (c, "set record full insn-number-max");
- c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
- &show_record_cmdlist);
+ c = add_alias_cmd ("insn-number-max", record_full_insn_number_max_cmds.show,
+ no_class, 1, &show_record_cmdlist);
deprecate_cmd (c, "show record full insn-number-max");
- add_setshow_boolean_cmd ("memory-query", no_class,
- &record_full_memory_query, _("\
+ set_show_commands record_full_memory_query_cmds
+ = add_setshow_boolean_cmd ("memory-query", no_class,
+ &record_full_memory_query, _("\
Set whether query if PREC cannot record memory change of next instruction."),
- _("\
+ _("\
Show whether query if PREC cannot record memory change of next instruction."),
- _("\
+ _("\
Default is OFF.\n\
When ON, query if PREC cannot record memory change of next instruction."),
- NULL, NULL,
- &set_record_full_cmdlist,
- &show_record_full_cmdlist);
+ NULL, NULL,
+ &set_record_full_cmdlist,
+ &show_record_full_cmdlist);
- c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
- &set_record_cmdlist);
+ c = add_alias_cmd ("memory-query", record_full_memory_query_cmds.set,
+ no_class, 1, &set_record_cmdlist);
deprecate_cmd (c, "set record full memory-query");
- c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
- &show_record_cmdlist);
+ c = add_alias_cmd ("memory-query", record_full_memory_query_cmds.show,
+ no_class, 1,&show_record_cmdlist);
deprecate_cmd (c, "show record full memory-query");
+
+ add_cmd ("record-instruction", class_maintenance,
+ maintenance_print_record_instruction,
+ _("\
+Print a recorded instruction.\n\
+If no argument is provided, print the last instruction recorded.\n\
+If a negative argument is given, prints how the nth previous \
+instruction will be undone.\n\
+If a positive argument is given, prints \
+how the nth following instruction will be redone."), &maintenanceprintlist);
}