/* Process record and replay target for GDB, the GNU debugger.
- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2008-2012 Free Software Foundation, Inc.
This file is part of GDB.
#include "record.h"
#include "elf-bfd.h"
#include "gcore.h"
+#include "event-loop.h"
+#include "inf-loop.h"
+#include "gdb_bfd.h"
+#include "observer.h"
#include <signal.h>
struct record_end_entry
{
- enum target_signal sigval;
+ enum gdb_signal sigval;
ULONGEST insn_num;
};
};
/* This is the debug switch for process record. */
-int record_debug = 0;
+unsigned int record_debug = 0;
+
+/* If true, query if PREC cannot record memory
+ change of next instruction. */
+int record_memory_query = 0;
struct record_core_buf_entry
{
/* The beneath function pointers. */
static struct target_ops *record_beneath_to_resume_ops;
static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
- enum target_signal);
+ enum gdb_signal);
static struct target_ops *record_beneath_to_wait_ops;
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
struct target_waitstatus *,
struct bp_target_info *);
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
struct bp_target_info *);
+static int (*record_beneath_to_stopped_by_watchpoint) (void);
+static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
+ CORE_ADDR *);
+static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
/* Alloc and free functions for record_reg, record_mem, and record_end
entries. */
return rec->u.reg.u.buf;
case record_end:
default:
- gdb_assert (0);
+ gdb_assert_not_reached ("unexpected record_entry type");
return NULL;
}
}
return 0;
}
+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 && record_debug)
+ printf_unfiltered (_("Process record: error reading memory "
+ "at addr %s len = %ld.\n"),
+ paddress (gdbarch, memaddr), (long) len);
+ return ret;
+}
+
/* Record the value of a region of memory whose address is ADDR and
length is LEN to record_arch_list. */
fprintf_unfiltered (gdb_stdlog,
"Process record: add mem addr = %s len = %d to "
"record list.\n",
- paddress (target_gdbarch, addr), len);
+ paddress (target_gdbarch (), addr), len);
- if (!addr) /* FIXME: Why? Some arch must permit it... */
+ if (!addr) /* FIXME: Why? Some arch must permit it... */
return 0;
rec = record_mem_alloc (addr, len);
- if (target_read_memory (addr, record_get_loc (rec), len))
+ if (record_read_memory (target_gdbarch (), addr, record_get_loc (rec), len))
{
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: error reading memory at "
- "addr = %s len = %d.\n",
- paddress (target_gdbarch, addr), len);
record_mem_release (rec);
return -1;
}
"Process record: add end to arch list.\n");
rec = record_end_alloc ();
- rec->u.end.sigval = TARGET_SIGNAL_0;
+ rec->u.end.sigval = GDB_SIGNAL_0;
rec->u.end.insn_num = ++record_insn_count;
record_arch_list_add (rec);
if (record_stop_at_limit)
{
int q;
+
if (set_terminal)
target_terminal_ours ();
q = yquery (_("Do you want to auto delete previous execution "
record the running message of inferior and set them to
record_arch_list, and add it to record_list. */
-struct record_message_args {
- struct regcache *regcache;
- enum target_signal signal;
-};
-
static int
-record_message (void *args)
+record_message (struct regcache *regcache, enum gdb_signal signal)
{
int ret;
- struct record_message_args *myargs = args;
- struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
record_arch_list_head = NULL;
if (record_list != &record_first) /* FIXME better way to check */
{
gdb_assert (record_list->type == record_end);
- record_list->u.end.sigval = myargs->signal;
+ record_list->u.end.sigval = signal;
}
- if (myargs->signal == TARGET_SIGNAL_0
+ if (signal == GDB_SIGNAL_0
|| !gdbarch_process_record_signal_p (gdbarch))
ret = gdbarch_process_record (gdbarch,
- myargs->regcache,
- regcache_read_pc (myargs->regcache));
+ regcache,
+ regcache_read_pc (regcache));
else
ret = gdbarch_process_record_signal (gdbarch,
- myargs->regcache,
- myargs->signal);
+ regcache,
+ signal);
if (ret > 0)
error (_("Process record: inferior program stopped."));
return 1;
}
+struct record_message_args {
+ struct regcache *regcache;
+ enum gdb_signal signal;
+};
+
+static int
+record_message_wrapper (void *args)
+{
+ struct record_message_args *record_args = args;
+
+ return record_message (record_args->regcache, record_args->signal);
+}
+
static int
-do_record_message (struct regcache *regcache,
- enum target_signal signal)
+record_message_wrapper_safe (struct regcache *regcache,
+ enum gdb_signal signal)
{
struct record_message_args args;
args.regcache = regcache;
args.signal = signal;
- return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL);
+
+ return catch_errors (record_message_wrapper, &args, NULL, RETURN_MASK_ALL);
}
/* Set to 1 if record_store_registers and record_xfer_partial
return old_cleanups;
}
+/* Flag set to TRUE for target_stopped_by_watchpoint. */
+static int record_hw_watchpoint = 0;
+
/* Execute one instruction from the record log. Each instruction in
the log will be represented by an arbitrary sequence of register
entries and memory entries, followed by an 'end' entry. */
paddress (gdbarch, entry->u.mem.addr),
entry->u.mem.len);
- if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
- {
- entry->u.mem.mem_entry_not_accessible = 1;
- if (record_debug)
- warning ("Process record: error reading memory at "
- "addr = %s len = %d.",
- paddress (gdbarch, entry->u.mem.addr),
- entry->u.mem.len);
- }
+ if (record_read_memory (gdbarch,
+ entry->u.mem.addr, mem, entry->u.mem.len))
+ entry->u.mem.mem_entry_not_accessible = 1;
else
{
if (target_write_memory (entry->u.mem.addr,
{
entry->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
- warning ("Process record: error writing memory at "
- "addr = %s len = %d.",
+ warning (_("Process record: error writing memory at "
+ "addr = %s len = %d."),
paddress (gdbarch, entry->u.mem.addr),
entry->u.mem.len);
}
else
- memcpy (record_get_loc (entry), mem, entry->u.mem.len);
+ {
+ memcpy (record_get_loc (entry), mem, entry->u.mem.len);
+
+ /* We've changed memory --- check if a hardware
+ watchpoint should trap. Note that this
+ presently assumes the target beneath supports
+ continuable watchpoints. On non-continuable
+ watchpoints target, we'll want to check this
+ _before_ actually doing the memory change, and
+ not doing the change at all if the watchpoint
+ traps. */
+ if (hardware_watchpoint_inserted_in_range
+ (get_regcache_aspace (regcache),
+ entry->u.mem.addr, entry->u.mem.len))
+ record_hw_watchpoint = 1;
+ }
}
}
}
static struct target_ops *tmp_to_resume_ops;
static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
- enum target_signal);
+ enum gdb_signal);
static struct target_ops *tmp_to_wait_ops;
static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
struct target_waitstatus *,
struct bp_target_info *);
static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
struct bp_target_info *);
+static int (*tmp_to_stopped_by_watchpoint) (void);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static void (*tmp_to_async) (void (*) (enum inferior_event_type, void *), void *);
static void record_restore (void);
+/* Asynchronous signal handle registered as event loop source for when
+ we have pending events ready to be passed to the core. */
+
+static struct async_event_handler *record_async_inferior_event_token;
+
+static void
+record_async_inferior_event_handler (gdb_client_data data)
+{
+ inferior_event_handler (INF_REG_EVENT, NULL);
+}
+
/* Open the process record target. */
static void
static void
record_open_1 (char *name, int from_tty)
{
- struct target_ops *t;
-
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
if (non_stop)
error (_("Process record target can't debug inferior in non-stop mode "
"(non-stop)."));
- if (target_async_permitted)
- error (_("Process record target can't debug inferior in asynchronous "
- "mode (target-async)."));
- if (!gdbarch_process_record_p (target_gdbarch))
+ if (!gdbarch_process_record_p (target_gdbarch ()))
error (_("Process record: the current architecture doesn't support "
"record function."));
if (!tmp_to_wait)
error (_("Could not find 'to_wait' method on the target stack."));
if (!tmp_to_store_registers)
- error (_("Could not find 'to_store_registers' method on the target stack."));
+ error (_("Could not find 'to_store_registers' "
+ "method on the target stack."));
if (!tmp_to_insert_breakpoint)
- error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
+ error (_("Could not find 'to_insert_breakpoint' "
+ "method on the target stack."));
if (!tmp_to_remove_breakpoint)
- error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
+ error (_("Could not find 'to_remove_breakpoint' "
+ "method on the target stack."));
+ if (!tmp_to_stopped_by_watchpoint)
+ error (_("Could not find 'to_stopped_by_watchpoint' "
+ "method on the target stack."));
+ if (!tmp_to_stopped_data_address)
+ error (_("Could not find 'to_stopped_data_address' "
+ "method on the target stack."));
push_target (&record_ops);
}
+static void record_init_record_breakpoints (void);
+
/* "to_open" target method. Open the process record target. */
static void
tmp_to_xfer_partial = NULL;
tmp_to_insert_breakpoint = NULL;
tmp_to_remove_breakpoint = NULL;
+ tmp_to_stopped_by_watchpoint = NULL;
+ tmp_to_stopped_data_address = NULL;
+ tmp_to_async = NULL;
/* Set the beneath function pointers. */
for (t = current_target.beneath; t != NULL; t = t->beneath)
tmp_to_insert_breakpoint = t->to_insert_breakpoint;
if (!tmp_to_remove_breakpoint)
tmp_to_remove_breakpoint = t->to_remove_breakpoint;
+ if (!tmp_to_stopped_by_watchpoint)
+ tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
+ if (!tmp_to_stopped_data_address)
+ tmp_to_stopped_data_address = t->to_stopped_data_address;
+ if (!tmp_to_async)
+ tmp_to_async = t->to_async;
}
if (!tmp_to_xfer_partial)
error (_("Could not find 'to_xfer_partial' method on the target stack."));
record_beneath_to_xfer_partial = tmp_to_xfer_partial;
record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
+ record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
+ record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
+ record_beneath_to_async = tmp_to_async;
- if (current_target.to_stratum == core_stratum)
+ if (core_bfd)
record_core_open_1 (name, from_tty);
else
record_open_1 (name, from_tty);
+
+ /* Register extra event sources in the event loop. */
+ record_async_inferior_event_token
+ = create_async_event_handler (record_async_inferior_event_handler,
+ NULL);
+
+ record_init_record_breakpoints ();
+
+ observer_notify_record_changed (current_inferior (), 1);
}
/* "to_close" target method. Close the process record target. */
}
record_core_buf_list = NULL;
}
+
+ if (record_async_inferior_event_token)
+ delete_async_event_handler (&record_async_inferior_event_token);
}
static int record_resume_step = 0;
-static int record_resume_error;
+
+/* True if we've been resumed, and so each record_wait call should
+ advance execution. If this is false, record_wait will return a
+ TARGET_WAITKIND_IGNORE. */
+static int record_resumed = 0;
+
+/* The execution direction of the last resume we got. This is
+ necessary for async mode. Vis (order is not strictly accurate):
+
+ 1. user has the global execution direction set to forward
+ 2. user does a reverse-step command
+ 3. record_resume is called with global execution direction
+ temporarily switched to reverse
+ 4. GDB's execution direction is reverted back to forward
+ 5. target record notifies event loop there's an event to handle
+ 6. infrun asks the target which direction was it going, and switches
+ the global execution direction accordingly (to reverse)
+ 7. infrun polls an event out of the record target, and handles it
+ 8. GDB goes back to the event loop, and goto #4.
+*/
+static enum exec_direction_kind record_execution_dir = EXEC_FORWARD;
/* "to_resume" target method. Resume the process record target. */
static void
record_resume (struct target_ops *ops, ptid_t ptid, int step,
- enum target_signal signal)
+ enum gdb_signal signal)
{
record_resume_step = step;
+ record_resumed = 1;
+ record_execution_dir = execution_direction;
if (!RECORD_IS_REPLAY)
{
- if (do_record_message (get_current_regcache (), signal))
- {
- record_resume_error = 0;
- }
- else
+ struct gdbarch *gdbarch = target_thread_architecture (ptid);
+
+ record_message (get_current_regcache (), signal);
+
+ if (!step)
{
- record_resume_error = 1;
- return;
+ /* This is not hard single step. */
+ if (!gdbarch_software_single_step_p (gdbarch))
+ {
+ /* This is a normal continue. */
+ step = 1;
+ }
+ else
+ {
+ /* This arch support soft sigle step. */
+ if (single_step_breakpoints_inserted ())
+ {
+ /* This is a soft single step. */
+ record_resume_step = 1;
+ }
+ else
+ {
+ /* This is a continue.
+ Try to insert a soft single step breakpoint. */
+ if (!gdbarch_software_single_step (gdbarch,
+ get_current_frame ()))
+ {
+ /* This system don't want use soft single step.
+ Use hard sigle step. */
+ step = 1;
+ }
+ }
+ }
}
- record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
- signal);
+
+ /* Make sure the target beneath reports all signals. */
+ target_pass_signals (0, NULL);
+
+ record_beneath_to_resume (record_beneath_to_resume_ops,
+ 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 (inferior_event_handler, 0);
+ /* Notify the event loop there's an event to wait for. We do
+ most of the work in record_wait. */
+ mark_async_event_handler (record_async_inferior_event_token);
}
}
where to stop. */
static ptid_t
-record_wait (struct target_ops *ops,
- ptid_t ptid, struct target_waitstatus *status,
- int options)
+record_wait_1 (struct target_ops *ops,
+ ptid_t ptid, struct target_waitstatus *status,
+ int options)
{
struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
"Process record: record_wait "
- "record_resume_step = %d\n",
- record_resume_step);
+ "record_resume_step = %d, record_resumed = %d, direction=%s\n",
+ record_resume_step, record_resumed,
+ record_execution_dir == EXEC_FORWARD ? "forward" : "reverse");
- if (!RECORD_IS_REPLAY && ops != &record_core_ops)
+ if (!record_resumed)
{
- if (record_resume_error)
- {
- /* If record_resume get error, return directly. */
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = TARGET_SIGNAL_ABRT;
- return inferior_ptid;
- }
+ gdb_assert ((options & TARGET_WNOHANG) != 0);
+ /* No interesting event. */
+ status->kind = TARGET_WAITKIND_IGNORE;
+ return minus_one_ptid;
+ }
+
+ record_get_sig = 0;
+ signal (SIGINT, record_sig_handler);
+
+ if (!RECORD_IS_REPLAY && ops != &record_core_ops)
+ {
if (record_resume_step)
{
/* This is a single step. */
/* This is not a single step. */
ptid_t ret;
CORE_ADDR tmp_pc;
+ struct gdbarch *gdbarch = target_thread_architecture (inferior_ptid);
while (1)
{
ret = record_beneath_to_wait (record_beneath_to_wait_ops,
ptid, status, options);
+ if (status->kind == TARGET_WAITKIND_IGNORE)
+ {
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_wait "
+ "target beneath not done yet\n");
+ return ret;
+ }
+
+ if (single_step_breakpoints_inserted ())
+ remove_single_step_breakpoints ();
+
+ if (record_resume_step)
+ return ret;
/* Is this a SIGTRAP? */
if (status->kind == TARGET_WAITKIND_STOPPED
- && status->value.sig == TARGET_SIGNAL_TRAP)
+ && status->value.sig == GDB_SIGNAL_TRAP)
{
struct regcache *regcache;
+ struct address_space *aspace;
+
+ /* Yes -- this is likely our single-step finishing,
+ but check if there's any reason the core would be
+ interested in the event. */
- /* Yes -- check if there is a breakpoint. */
registers_changed ();
regcache = get_current_regcache ();
tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
- tmp_pc))
+ aspace = get_regcache_aspace (regcache);
+
+ if (target_stopped_by_watchpoint ())
{
- /* There is a breakpoint. GDB will want to stop. */
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- CORE_ADDR decr_pc_after_break
- = gdbarch_decr_pc_after_break (gdbarch);
- if (decr_pc_after_break)
- regcache_write_pc (regcache,
- tmp_pc + decr_pc_after_break);
+ /* Always interested in watchpoints. */
+ }
+ else if (breakpoint_inserted_here_p (aspace, tmp_pc))
+ {
+ /* There is a breakpoint here. Let the core
+ handle it. */
+ if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
+ {
+ struct gdbarch *gdbarch
+ = get_regcache_arch (regcache);
+ CORE_ADDR decr_pc_after_break
+ = gdbarch_decr_pc_after_break (gdbarch);
+ if (decr_pc_after_break)
+ regcache_write_pc (regcache,
+ tmp_pc + decr_pc_after_break);
+ }
}
else
{
- /* There is not a breakpoint, and gdb is not
- stepping, therefore gdb will not stop.
- Therefore we will not return to gdb.
- Record the insn and resume. */
- if (!do_record_message (regcache, TARGET_SIGNAL_0))
- break;
+ /* This is a single-step trap. Record the
+ insn and issue another step.
+ FIXME: this part can be a random SIGTRAP too.
+ But GDB cannot handle it. */
+ int step = 1;
+
+ if (!record_message_wrapper_safe (regcache,
+ GDB_SIGNAL_0))
+ {
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = GDB_SIGNAL_0;
+ break;
+ }
+
+ if (gdbarch_software_single_step_p (gdbarch))
+ {
+ /* Try to insert the software single step breakpoint.
+ If insert success, set step to 0. */
+ set_executing (inferior_ptid, 0);
+ reinit_frame_cache ();
+ if (gdbarch_software_single_step (gdbarch,
+ get_current_frame ()))
+ step = 0;
+ set_executing (inferior_ptid, 1);
+ }
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_wait "
+ "issuing one more step in the target beneath\n");
record_beneath_to_resume (record_beneath_to_resume_ops,
- ptid, 1,
- TARGET_SIGNAL_0);
+ ptid, step,
+ GDB_SIGNAL_0);
continue;
}
}
{
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct address_space *aspace = get_regcache_aspace (regcache);
int continue_flag = 1;
int first_record_end = 1;
struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
CORE_ADDR tmp_pc;
+ record_hw_watchpoint = 0;
status->kind = TARGET_WAITKIND_STOPPED;
/* Check breakpoint when forward execute. */
if (execution_direction == EXEC_FORWARD)
{
tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
- tmp_pc))
+ if (breakpoint_inserted_here_p (aspace, tmp_pc))
{
+ int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
+
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
"Process record: break at %s.\n",
paddress (gdbarch, tmp_pc));
- if (gdbarch_decr_pc_after_break (gdbarch)
- && !record_resume_step)
+
+ if (decr_pc_after_break
+ && !record_resume_step
+ && software_breakpoint_inserted_here_p (aspace, tmp_pc))
regcache_write_pc (regcache,
- tmp_pc +
- gdbarch_decr_pc_after_break (gdbarch));
+ tmp_pc + decr_pc_after_break);
goto replay_out;
}
}
- record_get_sig = 0;
- signal (SIGINT, record_sig_handler);
/* If GDB is in terminal_inferior mode, it will not get the signal.
And in GDB replay mode, GDB doesn't need to be in terminal_inferior
mode, because inferior will not executed.
/* check breakpoint */
tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
- tmp_pc))
+ if (breakpoint_inserted_here_p (aspace, tmp_pc))
{
+ int decr_pc_after_break
+ = gdbarch_decr_pc_after_break (gdbarch);
+
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
"Process record: break "
"at %s.\n",
paddress (gdbarch, tmp_pc));
- if (gdbarch_decr_pc_after_break (gdbarch)
+ if (decr_pc_after_break
&& execution_direction == EXEC_FORWARD
- && !record_resume_step)
+ && !record_resume_step
+ && software_breakpoint_inserted_here_p (aspace,
+ tmp_pc))
regcache_write_pc (regcache,
- tmp_pc +
- gdbarch_decr_pc_after_break (gdbarch));
+ tmp_pc + decr_pc_after_break);
+ continue_flag = 0;
+ }
+
+ if (record_hw_watchpoint)
+ {
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: hit hw "
+ "watchpoint.\n");
continue_flag = 0;
}
/* Check target signal */
- if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+ if (record_list->u.end.sigval != GDB_SIGNAL_0)
/* FIXME: better way to check */
continue_flag = 0;
}
}
while (continue_flag);
- signal (SIGINT, handle_sigint);
-
replay_out:
if (record_get_sig)
- status->value.sig = TARGET_SIGNAL_INT;
- else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+ status->value.sig = GDB_SIGNAL_INT;
+ else if (record_list->u.end.sigval != GDB_SIGNAL_0)
/* FIXME: better way to check */
status->value.sig = record_list->u.end.sigval;
else
- status->value.sig = TARGET_SIGNAL_TRAP;
+ status->value.sig = GDB_SIGNAL_TRAP;
discard_cleanups (old_cleanups);
}
+ signal (SIGINT, handle_sigint);
+
do_cleanups (set_cleanups);
return inferior_ptid;
}
+static ptid_t
+record_wait (struct target_ops *ops,
+ ptid_t ptid, struct target_waitstatus *status,
+ int options)
+{
+ ptid_t return_ptid;
+
+ return_ptid = record_wait_1 (ops, ptid, status, options);
+ 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
+ core wants us resumed again. */
+ record_resumed = 0;
+ }
+ return return_ptid;
+}
+
+static int
+record_stopped_by_watchpoint (void)
+{
+ if (RECORD_IS_REPLAY)
+ return record_hw_watchpoint;
+ else
+ return record_beneath_to_stopped_by_watchpoint ();
+}
+
+static int
+record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
+{
+ if (RECORD_IS_REPLAY)
+ return 0;
+ else
+ return record_beneath_to_stopped_data_address (ops, addr_p);
+}
+
/* "to_disconnect" method for process record target. */
static void
if (regnum < 0)
{
int i;
+
for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
{
if (record_arch_list_add_reg (regcache, i))
if (regno < 0)
{
int i;
+
for (i = 0;
i < gdbarch_num_regs (get_regcache_arch (regcache));
i++)
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 (target_gdbarch (), offset)))
error (_("Process record canceled the operation."));
/* Destroy the record from here forward. */
offset, len);
}
-/* Behavior is conditional on RECORD_IS_REPLAY.
- We will not actually insert or remove breakpoints when replaying,
- nor when recording. */
+/* This structure represents a breakpoint inserted while the record
+ target is active. We use this to know when to install/remove
+ breakpoints in/from the target beneath. For example, a breakpoint
+ may be inserted while recording, but removed when not replaying nor
+ recording. In that case, the breakpoint had not been inserted on
+ the target beneath, so we should not try to remove it there. */
+
+struct record_breakpoint
+{
+ /* The address and address space the breakpoint was set at. */
+ struct address_space *address_space;
+ CORE_ADDR addr;
+
+ /* True when the breakpoint has been also installed in the target
+ beneath. This will be false for breakpoints set during replay or
+ when recording. */
+ int in_target_beneath;
+};
+
+typedef struct record_breakpoint *record_breakpoint_p;
+DEF_VEC_P(record_breakpoint_p);
+
+/* The list of breakpoints inserted while the record target is
+ active. */
+VEC(record_breakpoint_p) *record_breakpoints = NULL;
+
+static void
+record_sync_record_breakpoints (struct bp_location *loc, void *data)
+{
+ if (loc->loc_type != bp_loc_software_breakpoint)
+ return;
+
+ if (loc->inserted)
+ {
+ struct record_breakpoint *bp = XNEW (struct record_breakpoint);
+
+ bp->addr = loc->target_info.placed_address;
+ bp->address_space = loc->target_info.placed_address_space;
+
+ bp->in_target_beneath = 1;
+
+ VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
+ }
+}
+
+/* Sync existing breakpoints to record_breakpoints. */
+
+static void
+record_init_record_breakpoints (void)
+{
+ VEC_free (record_breakpoint_p, record_breakpoints);
+
+ iterate_over_bp_locations (record_sync_record_breakpoints);
+}
+
+/* Behavior is conditional on RECORD_IS_REPLAY. We will not actually
+ insert or remove breakpoints in the real target when replaying, nor
+ when recording. */
static int
record_insert_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
+ struct record_breakpoint *bp;
+ int in_target_beneath = 0;
+
if (!RECORD_IS_REPLAY)
{
- struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
- int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
+ /* When recording, we currently always single-step, so we don't
+ really need to install regular breakpoints in the inferior.
+ However, we do have to insert software single-step
+ breakpoints, in case the target can't hardware step. To keep
+ things single, we always insert. */
+ struct cleanup *old_cleanups;
+ int ret;
+ old_cleanups = record_gdb_operation_disable_set ();
+ ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
do_cleanups (old_cleanups);
- return ret;
+ if (ret != 0)
+ return ret;
+
+ in_target_beneath = 1;
}
+ bp = XNEW (struct record_breakpoint);
+ bp->addr = bp_tgt->placed_address;
+ bp->address_space = bp_tgt->placed_address_space;
+ bp->in_target_beneath = in_target_beneath;
+ VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
return 0;
}
record_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- if (!RECORD_IS_REPLAY)
+ struct record_breakpoint *bp;
+ int ix;
+
+ for (ix = 0;
+ VEC_iterate (record_breakpoint_p, record_breakpoints, ix, bp);
+ ++ix)
{
- struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
- int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
+ if (bp->addr == bp_tgt->placed_address
+ && bp->address_space == bp_tgt->placed_address_space)
+ {
+ if (bp->in_target_beneath)
+ {
+ struct cleanup *old_cleanups;
+ int ret;
- do_cleanups (old_cleanups);
+ old_cleanups = record_gdb_operation_disable_set ();
+ ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
+ do_cleanups (old_cleanups);
+
+ if (ret != 0)
+ return ret;
+ }
- return ret;
+ VEC_unordered_remove (record_breakpoint_p, record_breakpoints, ix);
+ return 0;
+ }
}
- return 0;
+ gdb_assert_not_reached ("removing unknown breakpoint");
}
/* "to_can_execute_reverse" method for process record target. */
return 1;
}
+/* "to_get_bookmark" method for process record and prec over core. */
+
+static gdb_byte *
+record_get_bookmark (char *args, int from_tty)
+{
+ gdb_byte *ret = NULL;
+
+ /* Return stringified form of instruction count. */
+ if (record_list && record_list->type == record_end)
+ ret = xstrdup (pulongest (record_list->u.end.insn_num));
+
+ if (record_debug)
+ {
+ if (ret)
+ fprintf_unfiltered (gdb_stdlog,
+ "record_get_bookmark returns %s\n", ret);
+ else
+ fprintf_unfiltered (gdb_stdlog,
+ "record_get_bookmark returns NULL\n");
+ }
+ return ret;
+}
+
+/* The implementation of the command "record goto". */
+static void cmd_record_goto (char *, int);
+
+/* "to_goto_bookmark" method for process record and prec over core. */
+
+static void
+record_goto_bookmark (gdb_byte *bookmark, int from_tty)
+{
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "record_goto_bookmark receives %s\n", bookmark);
+
+ if (bookmark[0] == '\'' || bookmark[0] == '\"')
+ {
+ if (bookmark[strlen (bookmark) - 1] != bookmark[0])
+ error (_("Unbalanced quotes: %s"), bookmark);
+
+ /* Strip trailing quote. */
+ bookmark[strlen (bookmark) - 1] = '\0';
+ /* Strip leading quote. */
+ bookmark++;
+ /* Pass along to cmd_record_goto. */
+ }
+
+ cmd_record_goto ((char *) bookmark, from_tty);
+ return;
+}
+
+static void
+record_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ /* If we're on top of a line target (e.g., linux-nat, remote), then
+ set it to async mode as well. Will be NULL if we're sitting on
+ top of the core target, for "record restore". */
+ if (record_beneath_to_async != NULL)
+ record_beneath_to_async (callback, context);
+}
+
+static int
+record_can_async_p (void)
+{
+ /* We only enable async when the user specifically asks for it. */
+ return target_async_permitted;
+}
+
+static int
+record_is_async_p (void)
+{
+ /* We only enable async when the user specifically asks for it. */
+ return target_async_permitted;
+}
+
+static enum exec_direction_kind
+record_execution_direction (void)
+{
+ return record_execution_dir;
+}
+
static void
init_record_ops (void)
{
record_ops.to_xfer_partial = record_xfer_partial;
record_ops.to_insert_breakpoint = record_insert_breakpoint;
record_ops.to_remove_breakpoint = record_remove_breakpoint;
+ record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
+ record_ops.to_stopped_data_address = record_stopped_data_address;
record_ops.to_can_execute_reverse = record_can_execute_reverse;
record_ops.to_stratum = record_stratum;
+ /* Add bookmark target methods. */
+ record_ops.to_get_bookmark = record_get_bookmark;
+ record_ops.to_goto_bookmark = record_goto_bookmark;
+ record_ops.to_async = record_async;
+ record_ops.to_can_async_p = record_can_async_p;
+ record_ops.to_is_async_p = record_is_async_p;
+ record_ops.to_execution_direction = record_execution_direction;
record_ops.to_magic = OPS_MAGIC;
}
static void
record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
- enum target_signal signal)
+ enum gdb_signal signal)
{
record_resume_step = step;
+ record_resumed = 1;
+ record_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 (inferior_event_handler, 0);
+
+ /* Notify the event loop there's an event to wait for. */
+ mark_async_event_handler (record_async_inferior_event_token);
+ }
}
/* "to_kill" method for prec over corefile. */
const gdb_byte *writebuf, ULONGEST offset,
LONGEST len)
{
- if (object == TARGET_OBJECT_MEMORY)
- {
- if (record_gdb_operation_disable || !writebuf)
- {
- struct target_section *p;
- for (p = record_core_start; p < record_core_end; p++)
- {
- if (offset >= p->addr)
- {
- struct record_core_buf_entry *entry;
- ULONGEST sec_offset;
-
- if (offset >= p->endaddr)
- continue;
-
- if (offset + len > p->endaddr)
- len = p->endaddr - offset;
-
- sec_offset = offset - p->addr;
-
- /* Read readbuf or write writebuf p, offset, len. */
- /* Check flags. */
- if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
- || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
- {
- if (readbuf)
- memset (readbuf, 0, len);
- return len;
- }
- /* Get record_core_buf_entry. */
- for (entry = record_core_buf_list; entry;
- entry = entry->prev)
- if (entry->p == p)
- break;
- if (writebuf)
- {
- if (!entry)
- {
- /* Add a new entry. */
- entry
- = (struct record_core_buf_entry *)
- xmalloc
- (sizeof (struct record_core_buf_entry));
- entry->p = p;
- if (!bfd_malloc_and_get_section (p->bfd,
- p->the_bfd_section,
- &entry->buf))
- {
- xfree (entry);
- return 0;
- }
- entry->prev = record_core_buf_list;
- record_core_buf_list = entry;
- }
-
- memcpy (entry->buf + sec_offset, writebuf,
- (size_t) len);
- }
- else
- {
- if (!entry)
- return record_beneath_to_xfer_partial
- (record_beneath_to_xfer_partial_ops,
- object, annex, readbuf, writebuf,
- offset, len);
-
- memcpy (readbuf, entry->buf + sec_offset,
- (size_t) len);
- }
-
- return len;
- }
- }
-
- return -1;
- }
- else
- error (_("You can't do that without a process to debug."));
- }
+ if (object == TARGET_OBJECT_MEMORY)
+ {
+ if (record_gdb_operation_disable || !writebuf)
+ {
+ struct target_section *p;
+
+ for (p = record_core_start; p < record_core_end; p++)
+ {
+ if (offset >= p->addr)
+ {
+ struct record_core_buf_entry *entry;
+ ULONGEST sec_offset;
+
+ if (offset >= p->endaddr)
+ continue;
+
+ if (offset + len > p->endaddr)
+ len = p->endaddr - offset;
+
+ sec_offset = offset - p->addr;
+
+ /* Read readbuf or write writebuf p, offset, len. */
+ /* Check flags. */
+ if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
+ || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ if (readbuf)
+ memset (readbuf, 0, len);
+ return len;
+ }
+ /* Get record_core_buf_entry. */
+ for (entry = record_core_buf_list; entry;
+ entry = entry->prev)
+ if (entry->p == p)
+ break;
+ if (writebuf)
+ {
+ if (!entry)
+ {
+ /* Add a new entry. */
+ entry = (struct record_core_buf_entry *)
+ xmalloc (sizeof (struct record_core_buf_entry));
+ entry->p = p;
+ if (!bfd_malloc_and_get_section (p->bfd,
+ p->the_bfd_section,
+ &entry->buf))
+ {
+ xfree (entry);
+ return 0;
+ }
+ entry->prev = record_core_buf_list;
+ record_core_buf_list = entry;
+ }
+
+ memcpy (entry->buf + sec_offset, writebuf,
+ (size_t) len);
+ }
+ else
+ {
+ if (!entry)
+ return record_beneath_to_xfer_partial
+ (record_beneath_to_xfer_partial_ops,
+ object, annex, readbuf, writebuf,
+ offset, len);
+
+ memcpy (readbuf, entry->buf + sec_offset,
+ (size_t) len);
+ }
+
+ return len;
+ }
+ }
+
+ return -1;
+ }
+ else
+ error (_("You can't do that without a process to debug."));
+ }
return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
object, annex, readbuf, writebuf,
/* "to_has_execution" method for prec over corefile. */
-int
-record_core_has_execution (struct target_ops *ops)
+static int
+record_core_has_execution (struct target_ops *ops, ptid_t the_ptid)
{
return 1;
}
static void
init_record_core_ops (void)
{
- record_core_ops.to_shortname = "record_core";
+ record_core_ops.to_shortname = "record-core";
record_core_ops.to_longname = "Process record and replay target";
record_core_ops.to_doc =
"Log program while executing and replay execution from log.";
record_core_ops.to_xfer_partial = record_core_xfer_partial;
record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
+ record_core_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
+ record_core_ops.to_stopped_data_address = record_stopped_data_address;
record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
record_core_ops.to_has_execution = record_core_has_execution;
record_core_ops.to_stratum = record_stratum;
+ /* Add bookmark target methods. */
+ record_core_ops.to_get_bookmark = record_get_bookmark;
+ record_core_ops.to_goto_bookmark = record_goto_bookmark;
+ record_core_ops.to_async = record_async;
+ record_core_ops.to_can_async_p = record_can_async_p;
+ record_core_ops.to_is_async_p = record_is_async_p;
+ record_core_ops.to_execution_direction = record_execution_direction;
record_core_ops.to_magic = OPS_MAGIC;
}
unpush_target (&record_ops);
printf_unfiltered (_("Process record is stopped and all execution "
"logs are deleted.\n"));
+
+ observer_notify_record_changed (current_inferior (), 0);
}
else
printf_unfiltered (_("Process record is not started.\n"));
static void
set_record_command (char *args, int from_tty)
{
- printf_unfiltered (_("\
-\"set record\" must be followed by an apporpriate subcommand.\n"));
+ printf_unfiltered (_("\"set record\" must be followed "
+ "by an apporpriate subcommand.\n"));
help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
}
if (ret)
*offset += len;
else
- error (_("Failed to read %d bytes from core file %s ('%s').\n"),
+ error (_("Failed to read %d bytes from core file %s ('%s')."),
len, bfd_get_filename (obfd),
bfd_errmsg (bfd_get_error ()));
}
gdb_assert (record_first.next == NULL);
if (record_debug)
- printf_filtered ("Restoring recording from core file.\n");
+ fprintf_unfiltered (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");
- osec_size = bfd_section_size (core_bfd, osec);
if (record_debug)
- printf_filtered ("Find precord section %s.\n",
- osec ? "succeeded" : "failed");
- if (!osec)
+ fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
+ osec ? "succeeded" : "failed");
+ if (osec == NULL)
return;
+ osec_size = bfd_section_size (core_bfd, osec);
if (record_debug)
- printf_filtered ("%s", bfd_section_name (core_bfd, osec));
+ fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, 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)
- printf_filtered ("\
- Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
- phex_nz (netorder32 (magic), 4));
+ fprintf_unfiltered (gdb_stdlog,
+ " Reading 4-byte magic cookie "
+ "RECORD_FILE_MAGIC (0x%s)\n",
+ phex_nz (netorder32 (magic), 4));
/* Restore the entries in recfd into record_arch_list_head and
record_arch_list_tail. */
while (1)
{
- int ret;
- uint8_t tmpu8;
+ uint8_t rectype;
uint32_t regnum, len, signal, count;
uint64_t addr;
/* We are finished when offset reaches osec_size. */
if (bfd_offset >= osec_size)
break;
- bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
+ bfdcore_read (core_bfd, osec, &rectype, sizeof (rectype), &bfd_offset);
- switch (tmpu8)
+ switch (rectype)
{
case record_reg: /* reg */
/* Get register number to regnum. */
bfdcore_read (core_bfd, osec, record_get_loc (rec),
rec->u.reg.len, &bfd_offset);
- if (record_debug)
- printf_filtered ("\
- Reading register %d (1 plus %lu plus %d bytes)\n",
- rec->u.reg.num,
- (unsigned long) sizeof (regnum),
- rec->u.reg.len);
+ 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);
break;
case record_mem: /* mem */
bfdcore_read (core_bfd, osec, record_get_loc (rec),
rec->u.mem.len, &bfd_offset);
- if (record_debug)
- printf_filtered ("\
- 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);
+ 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);
break;
case record_end: /* end */
count = netorder32 (count);
rec->u.end.insn_num = count;
record_insn_count = count + 1;
- if (record_debug)
- printf_filtered ("\
- Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count),
- paddress (get_current_arch (),
- bfd_offset));
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ " Reading record_end (1 + "
+ "%lu + %lu bytes), offset == %s\n",
+ (unsigned long) sizeof (signal),
+ (unsigned long) sizeof (count),
+ paddress (get_current_arch (),
+ bfd_offset));
break;
default:
if (ret)
*offset += len;
else
- error (_("Failed to write %d bytes to core file %s ('%s').\n"),
+ error (_("Failed to write %d bytes to core file %s ('%s')."),
len, bfd_get_filename (obfd),
bfd_errmsg (bfd_get_error ()));
}
{
bfd *obfd = data;
char *pathname = xstrdup (bfd_get_filename (obfd));
- bfd_close (obfd);
+
+ gdb_bfd_unref (obfd);
unlink (pathname);
xfree (pathname);
}
cmd_record_save (char *args, int from_tty)
{
char *recfilename, recfilename_buffer[40];
- int recfd;
struct record_entry *cur_record_list;
uint32_t magic;
struct regcache *regcache;
/* Open the save file. */
if (record_debug)
- printf_filtered ("Saving execution log to core file '%s'\n", recfilename);
+ fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
+ recfilename);
/* Open the output file. */
obfd = create_gcore_bfd (recfilename);
/* Write the magic code. */
magic = RECORD_FILE_MAGIC;
if (record_debug)
- printf_filtered ("\
- Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
- phex_nz (magic, 4));
+ fprintf_unfiltered (gdb_stdlog,
+ " Writing 4-byte magic cookie "
+ "RECORD_FILE_MAGIC (0x%s)\n",
+ phex_nz (magic, 4));
bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
/* Save the entries to recfd and forward execute to the end of
switch (record_list->type)
{
case record_reg: /* reg */
- if (record_debug)
- printf_filtered ("\
- Writing register %d (1 plus %lu plus %d bytes)\n",
- record_list->u.reg.num,
- (unsigned long) sizeof (regnum),
- record_list->u.reg.len);
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ " Writing register %d (1 "
+ "plus %lu plus %d bytes)\n",
+ record_list->u.reg.num,
+ (unsigned long) sizeof (regnum),
+ record_list->u.reg.len);
/* Write regnum. */
regnum = netorder32 (record_list->u.reg.num);
case record_mem: /* mem */
if (record_debug)
- printf_filtered ("\
- Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
- paddress (gdbarch,
- record_list->u.mem.addr),
- (unsigned long) sizeof (addr),
- (unsigned long) sizeof (len),
- record_list->u.mem.len);
+ fprintf_unfiltered (gdb_stdlog,
+ " Writing memory %s (1 plus "
+ "%lu plus %lu plus %d bytes)\n",
+ paddress (gdbarch,
+ record_list->u.mem.addr),
+ (unsigned long) sizeof (addr),
+ (unsigned long) sizeof (len),
+ record_list->u.mem.len);
/* Write memlen. */
len = netorder32 (record_list->u.mem.len);
break;
case record_end:
- if (record_debug)
- printf_filtered ("\
- Writing record_end (1 + %lu + %lu bytes)\n",
- (unsigned long) sizeof (signal),
- (unsigned long) sizeof (count));
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ " Writing record_end (1 + "
+ "%lu + %lu bytes)\n",
+ (unsigned long) sizeof (signal),
+ (unsigned long) sizeof (count));
/* Write signal value. */
signal = netorder32 (record_list->u.end.sigval);
bfdcore_write (obfd, osec, &signal,
}
do_cleanups (set_cleanups);
- bfd_close (obfd);
+ gdb_bfd_unref (obfd);
discard_cleanups (old_cleanups);
/* Succeeded. */
recfilename);
}
+/* record_goto_insn -- rewind the record log (forward or backward,
+ depending on DIR) to the given entry, changing the program state
+ correspondingly. */
+
+static void
+record_goto_insn (struct record_entry *entry,
+ enum exec_direction_kind dir)
+{
+ struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+ /* Assume everything is valid: we will hit the entry,
+ and we will not hit the end of the recording. */
+
+ if (dir == EXEC_FORWARD)
+ record_list = record_list->next;
+
+ do
+ {
+ record_exec_insn (regcache, gdbarch, record_list);
+ if (dir == EXEC_REVERSE)
+ record_list = record_list->prev;
+ else
+ record_list = record_list->next;
+ } while (record_list != entry);
+ do_cleanups (set_cleanups);
+}
+
+/* "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 (char *arg, int from_tty)
+{
+ struct record_entry *p = NULL;
+ ULONGEST target_insn = 0;
+
+ if (arg == NULL || *arg == '\0')
+ error (_("Command requires an argument (insn number to go to)."));
+
+ if (strncmp (arg, "start", strlen ("start")) == 0
+ || strncmp (arg, "begin", strlen ("begin")) == 0)
+ {
+ /* Special case. Find first insn. */
+ for (p = &record_first; p != NULL; p = p->next)
+ if (p->type == record_end)
+ break;
+ if (p)
+ target_insn = p->u.end.insn_num;
+ }
+ else if (strncmp (arg, "end", strlen ("end")) == 0)
+ {
+ /* Special case. Find last insn. */
+ for (p = record_list; p->next != NULL; p = p->next)
+ ;
+ for (; p!= NULL; p = p->prev)
+ if (p->type == record_end)
+ break;
+ if (p)
+ target_insn = p->u.end.insn_num;
+ }
+ else
+ {
+ /* General case. Find designated insn. */
+ target_insn = parse_and_eval_long (arg);
+
+ for (p = &record_first; p != NULL; p = p->next)
+ if (p->type == record_end && p->u.end.insn_num == target_insn)
+ break;
+ }
+
+ if (p == NULL)
+ error (_("Target insn '%s' not found."), arg);
+ else if (p == record_list)
+ error (_("Already at insn '%s'."), arg);
+ else if (p->u.end.insn_num > record_list->u.end.insn_num)
+ {
+ printf_filtered (_("Go forward to insn number %s\n"),
+ pulongest (target_insn));
+ record_goto_insn (p, EXEC_FORWARD);
+ }
+ else
+ {
+ printf_filtered (_("Go backward to insn number %s\n"),
+ pulongest (target_insn));
+ record_goto_insn (p, EXEC_REVERSE);
+ }
+ registers_changed ();
+ reinit_frame_cache ();
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_record;
+
void
_initialize_record (void)
{
init_record_core_ops ();
add_target (&record_core_ops);
- add_setshow_zinteger_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_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);
c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
_("Abbreviated form of \"target record\" command."),
add_setshow_boolean_cmd ("stop-at-limit", no_class,
&record_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\
+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."),
record/replay buffer. Zero means unlimited. Default is 200000."),
set_record_insn_max_num,
NULL, &set_record_cmdlist, &show_record_cmdlist);
+
+ add_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_cmdlist);
+
+ add_setshow_boolean_cmd ("memory-query", no_class,
+ &record_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_cmdlist, &show_record_cmdlist);
+
}