/* Process record and replay target for GDB, the GNU debugger.
- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009, 2010, 2011 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 <signal.h>
/* This is the debug switch for process record. */
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
{
struct record_core_buf_entry *prev;
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;
}
}
"record list.\n",
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 (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 target_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 == TARGET_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 target_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 target_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
{
entry->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
- warning ("Process record: error reading memory at "
- "addr = %s len = %d.",
+ warning (_("Process record: error reading memory at "
+ "addr = %s len = %d."),
paddress (gdbarch, entry->u.mem.addr),
entry->u.mem.len);
}
{
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);
}
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))
error (_("Process record: the current architecture doesn't support "
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);
}
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);
}
/* "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. */
enum target_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);
+
+ 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
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. */
- 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,
+ TARGET_SIGNAL_0))
+ {
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_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,
+ ptid, step,
TARGET_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 */
}
while (continue_flag);
- signal (SIGINT, handle_sigint);
-
replay_out:
if (record_get_sig)
status->value.sig = TARGET_SIGNAL_INT;
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++)
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;
}
enum target_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;
}
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);
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. */
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);
+
}