/* 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 *,
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_arch_list, and add it to record_list. */
static int
-record_message (struct regcache *regcache, enum target_signal signal)
+record_message (struct regcache *regcache, enum gdb_signal signal)
{
int ret;
struct gdbarch *gdbarch = get_regcache_arch (regcache);
record_list->u.end.sigval = signal;
}
- if (signal == TARGET_SIGNAL_0
+ if (signal == GDB_SIGNAL_0
|| !gdbarch_process_record_signal_p (gdbarch))
ret = gdbarch_process_record (gdbarch,
regcache,
struct record_message_args {
struct regcache *regcache;
- enum target_signal signal;
+ enum gdb_signal signal;
};
static int
static int
record_message_wrapper_safe (struct regcache *regcache,
- enum target_signal signal)
+ enum gdb_signal signal)
{
struct record_message_args args;
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);
}
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_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_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_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;
+/* 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)
{
+ struct gdbarch *gdbarch = target_thread_architecture (ptid);
+
record_message (get_current_regcache (), signal);
- record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
- signal);
+
+ if (!step)
+ {
+ /* 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;
+ }
+ }
+ }
+ }
+
+ /* 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_resumed)
+ {
+ 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)
{
/* 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;
handle it. */
if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch *gdbarch
+ = get_regcache_arch (regcache);
CORE_ADDR decr_pc_after_break
= gdbarch_decr_pc_after_break (gdbarch);
if (decr_pc_after_break)
}
else
{
- /* This must be a single-step trap. Record the
- insn and issue another step. */
+ /* 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,
- TARGET_SIGNAL_0))
+ GDB_SIGNAL_0))
{
status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = TARGET_SIGNAL_0;
+ 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;
}
}
}
}
- 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.
if (record_hw_watchpoint)
{
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
-Process record: hit hw watchpoint.\n");
+ 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 (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;
}
+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)
{
/* 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.";
/* 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 ()));
}
/* 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)
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)
fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
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_FILE_MAGIC (0x%s)\n",
+ 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
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. */
rec->u.reg.len, &bfd_offset);
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Reading register %d (1 plus %lu plus %d bytes)\n",
+ 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);
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",
+ 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),
rec->u.end.insn_num = count;
record_insn_count = count + 1;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
+ 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 (),
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;
/* Write the magic code. */
magic = RECORD_FILE_MAGIC;
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
+ 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);
{
case record_reg: /* reg */
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Writing register %d (1 plus %lu plus %d bytes)\n",
+ 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);
case record_mem: /* mem */
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
+ 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),
case record_end:
if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "\
- Writing record_end (1 + %lu + %lu bytes)\n",
+ fprintf_unfiltered (gdb_stdlog,
+ " Writing record_end (1 + "
+ "%lu + %lu bytes)\n",
(unsigned long) sizeof (signal),
(unsigned long) sizeof (count));
/* Write signal value. */
}
do_cleanups (set_cleanups);
- bfd_close (obfd);
+ gdb_bfd_unref (obfd);
discard_cleanups (old_cleanups);
/* Succeeded. */
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."),
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);
+
}