if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
{
record_list->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);
}
else
{
if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
entry->u.mem.len))
{
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
warning (_("Process record: error writing memory at "
"addr = %s len = %d."),
paddress (gdbarch, entry->u.mem.addr),
entry->u.mem.len);
}
else
memcpy (entry->u.mem.val, mem, entry->u.mem.len);
}
Please help me review it.
Thanks,
Hui
---
record.c | 951 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 825 insertions(+), 126 deletions(-)
--- a/record.c
+++ b/record.c
@@ -23,15 +23,23 @@
#include "gdbthread.h"
#include "event-top.h"
#include "exceptions.h"
+#include "completer.h"
+#include "arch-utils.h"
+#include "gdbcore.h"
+#include "exec.h"
#include "record.h"
+#include <byteswap.h>
#include <signal.h>
+#include <netinet/in.h>
#define DEFAULT_RECORD_INSN_MAX_NUM 200000
#define RECORD_IS_REPLAY \
(record_list->next || execution_direction == EXEC_REVERSE)
+#define RECORD_FILE_MAGIC htonl(0x20090726)
+
/* These are the core struct of record function.
An record_entry is a record of the value change of a register
@@ -78,9 +86,22 @@ struct record_entry
} u;
};
+struct record_core_buf_entry
+{
+ struct record_core_buf_entry *prev;
+ struct target_section *p;
+ bfd_byte *buf;
+};
+
/* This is the debug switch for process record. */
int record_debug = 0;
+/* Record buf with core target. */
+static gdb_byte *record_core_regbuf = NULL;
+static struct target_section *record_core_start;
+static struct target_section *record_core_end;
+static struct record_core_buf_entry *record_core_buf_list = NULL;
+
/* These list is for execution log. */
static struct record_entry record_first;
static struct record_entry *record_list = &record_first;
@@ -94,6 +115,7 @@ static int record_insn_num = 0;
/* The target_ops of process record. */
static struct target_ops record_ops;
+static struct target_ops record_core_ops;
/* The beneath function pointers. */
static struct target_ops *record_beneath_to_resume_ops;
@@ -169,7 +191,7 @@ record_list_release_next (void)
}
static void
-record_list_release_first (void)
+record_list_release_first_insn (void)
{
struct record_entry *tmp = NULL;
enum record_type type;
@@ -340,30 +362,30 @@ record_check_insn_num (int set_terminal)
if (q)
record_stop_at_limit = 0;
else
- error (_("Process record: inferior program stopped."));
+ error (_("Process record: stoped by user."));
}
}
}
}
+static void
+record_arch_list_cleanups (void *ignore)
+{
+ record_list_release (record_arch_list_tail);
+}
+
/* Before inferior step (when GDB record the running message, inferior
only can step), GDB will call this function to record the values to
record_list. This function will call gdbarch_process_record to
record the running message of inferior and set them to
record_arch_list, and add it to record_list. */
-static void
-record_message_cleanups (void *ignore)
-{
- record_list_release (record_arch_list_tail);
-}
-
static int
record_message (void *args)
{
int ret;
struct regcache *regcache = args;
- struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
+ struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
@@ -386,7 +408,7 @@ record_message (void *args)
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
@@ -416,13 +438,291 @@ record_gdb_operation_disable_set (void)
return old_cleanups;
}
+static inline void
+record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
+ struct record_entry *entry)
+{
+ switch (entry->type)
+ {
+ case record_reg: /* reg */
+ {
+ gdb_byte reg[MAX_REGISTER_SIZE];
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_reg %s to "
+ "inferior num = %d.\n",
+ host_address_to_string (entry),
+ entry->u.reg.num);
+
+ regcache_cooked_read (regcache, entry->u.reg.num, reg);
+ regcache_cooked_write (regcache, entry->u.reg.num, entry->u.reg.val);
+ memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE);
+ }
+ break;
+
+ case record_mem: /* mem */
+ {
+ if (!record_list->u.mem.mem_entry_not_accessible)
+ {
+ gdb_byte *mem = alloca (entry->u.mem.len);
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_mem %s to "
+ "inferior addr = %s len = %d.\n",
+ host_address_to_string (entry),
+ paddress (gdbarch, entry->u.mem.addr),
+ record_list->u.mem.len);
+
+ if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
+ {
+ record_list->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);
+ }
+ else
+ {
+ if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
+ entry->u.mem.len))
+ {
+ record_list->u.mem.mem_entry_not_accessible = 1;
+ if (record_debug)
+ warning (_("Process record: error writing memory at "
+ "addr = %s len = %d."),
+ paddress (gdbarch, entry->u.mem.addr),
+ entry->u.mem.len);
+ }
+ else
+ memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+ }
+ }
+ }
+ break;
+ }
+}
+
+static inline void
+record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte)
+{
+ if (read (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to read dump of execution records in '%s'."),
+ recfilename);
+}
+
static void
-record_open (char *name, int from_tty)
+record_fd_cleanups (void *recfdp)
{
- struct target_ops *t;
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
- if (record_debug)
- fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+/* Load the execution log from a file. */
+
+static void
+record_load (char *name)
+{
+ int recfd;
+ uint32_t magic;
+ struct cleanup *old_cleanups;
+ struct cleanup *old_cleanups2;
+ struct record_entry *rec;
+ int insn_number = 0;
+
+ if (!name || (name && !*name))
+ return;
+
+ /* Open the load file. */
+ recfd = open (name, O_RDONLY | O_BINARY);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for loading execution records: %s"),
+ name, strerror (errno));
+ old_cleanups = make_cleanup (record_fd_cleanups, &recfd);
+
+ /* Check the magic code. */
+ record_read_dump (name, recfd, &magic, 4);
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s' is not a valid dump of execution records."), name);
+
+ /* Load the entries in recfd to the record_arch_list_head and
+ record_arch_list_tail. */
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0);
+
+ while (1)
+ {
+ int ret;
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ ret = read (recfd, &tmpu8, 1);
+ if (ret < 0)
+ error (_("Failed to read dump of execution records in '%s'."), name);
+ if (ret == 0)
+ break;
+
+ switch (tmpu8)
+ {
+ case record_reg: /* reg */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+
+ /* Get num. */
+ record_read_dump (name, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.reg.num = tmpu64;
+
+ /* Get val. */
+ record_read_dump (name, recfd, rec->u.reg.val, MAX_REGISTER_SIZE);
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+Reading register %d (1 plus 8 plus %d bytes)\n"),
+ rec->u.reg.num,
+ MAX_REGISTER_SIZE);
+
+ record_arch_list_add (rec);
+ break;
+
+ case record_mem: /* mem */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_mem;
+
+ /* Get addr. */
+ record_read_dump (name, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.mem.addr = tmpu64;
+
+ /* Get len. */
+ record_read_dump (name, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.mem.len = tmpu64;
+ rec->u.mem.mem_entry_not_accessible = 0;
+ rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len);
+
+ /* Get val. */
+ record_read_dump (name, recfd, rec->u.mem.val, rec->u.mem.len);
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+ paddress (get_current_arch (),
+ rec->u.mem.addr),
+ rec->u.mem.len);
+
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Reading record_end (1 byte)\n"));
+
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+ record_arch_list_add (rec);
+ insn_number ++;
+ break;
+
+ default:
+ error (_("Format of '%s' is not right."), name);
+ break;
+ }
+ }
+
+ discard_cleanups (old_cleanups2);
+
+ /* Add record_arch_list_head to the end of record list. */
+ for (rec = record_list; rec->next; rec = rec->next);
+ rec->next = record_arch_list_head;
+ record_arch_list_head->prev = rec;
+
+ /* Update record_insn_num and record_insn_max_num. */
+ record_insn_num += insn_number;
+ if (record_insn_num > record_insn_max_num)
+ {
+ record_insn_max_num = record_insn_num;
+ warning (_("Auto increase record/replay buffer limit to %d."),
+ record_insn_max_num);
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", name);
+}
+
+static struct target_ops *tmp_to_resume_ops;
+static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
+ enum target_signal);
+static struct target_ops *tmp_to_wait_ops;
+static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
+ struct target_waitstatus *,
+ int);
+static struct target_ops *tmp_to_store_registers_ops;
+static void (*tmp_to_store_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
+static struct target_ops *tmp_to_xfer_partial_ops;
+static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
+ enum target_object object,
+ const char *annex,
+ gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset,
+ LONGEST len);
+static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
+ struct bp_target_info *);
+static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
+ struct bp_target_info *);
+
+static void
+record_core_open_1 (char *name, int from_tty)
+{
+ struct regcache *regcache = get_current_regcache ();
+ int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ if (!name || (name && !*name))
+ error (_("Argument for gdb record filename required.\n"));
+
+ /* Get record_core_regbuf. */
+ target_fetch_registers (regcache, -1);
+ record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
+ for (i = 0; i < regnum; i ++)
+ regcache_raw_collect (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+
+ /* Get record_core_start and record_core_end. */
+ if (build_section_table (core_bfd, &record_core_start, &record_core_end))
+ {
+ xfree (record_core_regbuf);
+ record_core_regbuf = NULL;
+ error (_("\"%s\": Can't find sections: %s"),
+ bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+ }
+
+ push_target (&record_core_ops);
+}
+
+static void
+record_open_1 (char *name, int from_tty)
+{
+ struct target_ops *t;
/* check exec */
if (!target_has_execution)
@@ -438,6 +738,28 @@ record_open (char *name, int from_tty)
error (_("Process record: the current architecture doesn't support "
"record function."));
+ if (!tmp_to_resume)
+ error (_("Process record can't get to_resume."));
+ if (!tmp_to_wait)
+ error (_("Process record can't get to_wait."));
+ if (!tmp_to_store_registers)
+ error (_("Process record can't get to_store_registers."));
+ if (!tmp_to_insert_breakpoint)
+ error (_("Process record can't get to_insert_breakpoint."));
+ if (!tmp_to_remove_breakpoint)
+ error (_("Process record can't get to_remove_breakpoint."));
+
+ push_target (&record_ops);
+}
+
+static void
+record_open (char *name, int from_tty)
+{
+ struct target_ops *t;
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+
/* Check if record target is already running. */
if (current_target.to_stratum == record_stratum)
{
@@ -447,70 +769,102 @@ record_open (char *name, int from_tty)
return;
}
- /*Reset the beneath function pointers. */
- record_beneath_to_resume = NULL;
- record_beneath_to_wait = NULL;
- record_beneath_to_store_registers = NULL;
- record_beneath_to_xfer_partial = NULL;
- record_beneath_to_insert_breakpoint = NULL;
- record_beneath_to_remove_breakpoint = NULL;
+ /* Reset the tmp beneath pointers. */
+ tmp_to_resume_ops = NULL;
+ tmp_to_resume = NULL;
+ tmp_to_wait_ops = NULL;
+ tmp_to_wait = NULL;
+ tmp_to_store_registers_ops = NULL;
+ tmp_to_store_registers = NULL;
+ tmp_to_xfer_partial_ops = NULL;
+ tmp_to_xfer_partial = NULL;
+ tmp_to_insert_breakpoint = NULL;
+ tmp_to_remove_breakpoint = NULL;
/* Set the beneath function pointers. */
for (t = current_target.beneath; t != NULL; t = t->beneath)
{
- if (!record_beneath_to_resume)
+ if (!tmp_to_resume)
{
- record_beneath_to_resume = t->to_resume;
- record_beneath_to_resume_ops = t;
+ tmp_to_resume = t->to_resume;
+ tmp_to_resume_ops = t;
}
- if (!record_beneath_to_wait)
+ if (!tmp_to_wait)
{
- record_beneath_to_wait = t->to_wait;
- record_beneath_to_wait_ops = t;
+ tmp_to_wait = t->to_wait;
+ tmp_to_wait_ops = t;
}
- if (!record_beneath_to_store_registers)
+ if (!tmp_to_store_registers)
{
- record_beneath_to_store_registers = t->to_store_registers;
- record_beneath_to_store_registers_ops = t;
+ tmp_to_store_registers = t->to_store_registers;
+ tmp_to_store_registers_ops = t;
}
- if (!record_beneath_to_xfer_partial)
+ if (!tmp_to_xfer_partial)
{
- record_beneath_to_xfer_partial = t->to_xfer_partial;
- record_beneath_to_xfer_partial_ops = t;
+ tmp_to_xfer_partial = t->to_xfer_partial;
+ tmp_to_xfer_partial_ops = t;
}
- if (!record_beneath_to_insert_breakpoint)
- record_beneath_to_insert_breakpoint = t->to_insert_breakpoint;
- if (!record_beneath_to_remove_breakpoint)
- record_beneath_to_remove_breakpoint = t->to_remove_breakpoint;
+ if (!tmp_to_insert_breakpoint)
+ tmp_to_insert_breakpoint = t->to_insert_breakpoint;
+ if (!tmp_to_remove_breakpoint)
+ tmp_to_remove_breakpoint = t->to_remove_breakpoint;
}
- if (!record_beneath_to_resume)
- error (_("Process record can't get to_resume."));
- if (!record_beneath_to_wait)
- error (_("Process record can't get to_wait."));
- if (!record_beneath_to_store_registers)
- error (_("Process record can't get to_store_registers."));
- if (!record_beneath_to_xfer_partial)
+ if (!tmp_to_xfer_partial)
error (_("Process record can't get to_xfer_partial."));
- if (!record_beneath_to_insert_breakpoint)
- error (_("Process record can't get to_insert_breakpoint."));
- if (!record_beneath_to_remove_breakpoint)
- error (_("Process record can't get to_remove_breakpoint."));
- push_target (&record_ops);
+ if (current_target.to_stratum == core_stratum)
+ record_core_open_1 (name, from_tty);
+ else
+ record_open_1 (name, from_tty);
/* Reset */
record_insn_num = 0;
record_list = &record_first;
record_list->next = NULL;
+
+ /* Set the tmp beneath pointers to beneath pointers. */
+ record_beneath_to_resume_ops = tmp_to_resume_ops;
+ record_beneath_to_resume = tmp_to_resume;
+ record_beneath_to_wait_ops = tmp_to_wait_ops;
+ record_beneath_to_wait = tmp_to_wait;
+ record_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
+ record_beneath_to_store_registers = tmp_to_store_registers;
+ record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops;
+ 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;
+
+ /* Load if there is argument. */
+ record_load (name);
}
static void
record_close (int quitting)
{
+ struct record_core_buf_entry *entry;
+
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
record_list_release (record_list);
+
+ /* Release record_core_regbuf. */
+ if (record_core_regbuf)
+ {
+ xfree (record_core_regbuf);
+ record_core_regbuf = NULL;
+ }
+
+ /* Release record_core_buf_list. */
+ if (record_core_buf_list)
+ {
+ for (entry = record_core_buf_list->prev; entry; entry = entry->prev)
+ {
+ xfree (record_core_buf_list);
+ record_core_buf_list = entry;
+ }
+ record_core_buf_list = NULL;
+ }
}
static int record_resume_step = 0;
@@ -584,7 +938,7 @@ record_wait (struct target_ops *ops,
"record_resume_step = %d\n",
record_resume_step);
- if (!RECORD_IS_REPLAY)
+ if (!RECORD_IS_REPLAY && ops != &record_core_ops)
{
if (record_resume_error)
{
@@ -712,76 +1066,9 @@ record_wait (struct target_ops *ops,
break;
}
- /* Set ptid, register and memory according to record_list. */
- if (record_list->type == record_reg)
- {
- /* reg */
- gdb_byte reg[MAX_REGISTER_SIZE];
- if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_reg %s to "
- "inferior num = %d.\n",
- host_address_to_string (record_list),
- record_list->u.reg.num);
- regcache_cooked_read (regcache, record_list->u.reg.num, reg);
- regcache_cooked_write (regcache, record_list->u.reg.num,
- record_list->u.reg.val);
- memcpy (record_list->u.reg.val, reg, MAX_REGISTER_SIZE);
- }
- else if (record_list->type == record_mem)
- {
- /* mem */
- /* Nothing to do if the entry is flagged not_accessible. */
- if (!record_list->u.mem.mem_entry_not_accessible)
- {
- gdb_byte *mem = alloca (record_list->u.mem.len);
- if (record_debug > 1)
- fprintf_unfiltered (gdb_stdlog,
- "Process record: record_mem %s to "
- "inferior addr = %s len = %d.\n",
- host_address_to_string (record_list),
- paddress (gdbarch,
- record_list->u.mem.addr),
- record_list->u.mem.len);
+ record_exec_entry (regcache, gdbarch, record_list);
- if (target_read_memory (record_list->u.mem.addr, mem,
- record_list->u.mem.len))
- {
- if (execution_direction != EXEC_REVERSE)
- error (_("Process record: error reading memory at "
- "addr = %s len = %d."),
- paddress (gdbarch, record_list->u.mem.addr),
- record_list->u.mem.len);
- else
- /* Read failed --
- flag entry as not_accessible. */
- record_list->u.mem.mem_entry_not_accessible = 1;
- }
- else
- {
- if (target_write_memory (record_list->u.mem.addr,
- record_list->u.mem.val,
- record_list->u.mem.len))
- {
- if (execution_direction != EXEC_REVERSE)
- error (_("Process record: error writing memory at "
- "addr = %s len = %d."),
- paddress (gdbarch, record_list->u.mem.addr),
- record_list->u.mem.len);
- else
- /* Write failed --
- flag entry as not_accessible. */
- record_list->u.mem.mem_entry_not_accessible = 1;
- }
- else
- {
- memcpy (record_list->u.mem.val, mem,
- record_list->u.mem.len);
- }
- }
- }
- }
- else
+ if (record_list->type == record_end)
{
if (record_debug > 1)
fprintf_unfiltered (gdb_stdlog,
@@ -901,6 +1188,7 @@ record_kill (struct target_ops *ops)
fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
unpush_target (&record_ops);
+
target_kill ();
}
@@ -945,7 +1233,7 @@ record_registers_change (struct regcache
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
@@ -1058,7 +1346,7 @@ record_xfer_partial (struct target_ops *
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
@@ -1138,6 +1426,191 @@ init_record_ops (void)
}
static void
+record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
+ enum target_signal siggnal)
+{
+ record_resume_step = step;
+ record_resume_siggnal = siggnal;
+}
+
+static void
+record_core_kill (struct target_ops *ops)
+{
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
+
+ unpush_target (&record_core_ops);
+}
+
+static void
+record_core_fetch_registers (struct target_ops *ops,
+ struct regcache *regcache,
+ int regno)
+{
+ if (regno < 0)
+ {
+ int num = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ for (i = 0; i < num; i ++)
+ regcache_raw_supply (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+ }
+ else
+ regcache_raw_supply (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+}
+
+static void
+record_core_prepare_to_store (struct regcache *regcache)
+{
+}
+
+static void
+record_core_store_registers (struct target_ops *ops,
+ struct regcache *regcache,
+ int regno)
+{
+ if (record_gdb_operation_disable)
+ regcache_raw_collect (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+ else
+ error (_("You can't do that without a process to debug."));
+}
+
+static LONGEST
+record_core_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ 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;
+
+ if (offset >= p->endaddr)
+ continue;
+
+ if (offset + len > p->endaddr)
+ len = p->endaddr - 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 + 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 + 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,
+ offset, len);
+}
+
+static int
+record_core_insert_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ return 0;
+}
+
+static int
+record_core_remove_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ return 0;
+}
+
+int
+record_core_has_execution (struct target_ops *ops)
+{
+ return 1;
+}
+
+static void
+init_record_core_ops (void)
+{
+ 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_open = record_open;
+ record_core_ops.to_close = record_close;
+ record_core_ops.to_resume = record_core_resume;
+ record_core_ops.to_wait = record_wait;
+ record_core_ops.to_kill = record_core_kill;
+ record_core_ops.to_fetch_registers = record_core_fetch_registers;
+ record_core_ops.to_prepare_to_store = record_core_prepare_to_store;
+ record_core_ops.to_store_registers = record_core_store_registers;
+ 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_can_execute_reverse = record_can_execute_reverse;
+ record_core_ops.to_has_execution = record_core_has_execution;
+ record_core_ops.to_stratum = record_stratum;
+ record_core_ops.to_magic = OPS_MAGIC;
+}
+
+static void
show_record_debug (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
@@ -1153,6 +1626,212 @@ cmd_record_start (char *args, int from_t
execute_command ("target record", from_tty);
}
+static void
+cmd_record_load (char *args, int from_tty)
+{
+ char buf[512];
+
+ snprintf (buf, 512, "target record %s", args);
+ execute_command (buf, from_tty);
+}
+
+static inline void
+record_write_dump (char *recfilename, int fildes, const void *buf,
+ size_t nbyte)
+{
+ if (write (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to write dump of execution records to '%s'."),
+ recfilename);
+}
+
+/* Record log save-file format
+ Version 1
+
+ Header:
+ 4 bytes: magic number htonl(0x20090726).
+ NOTE: be sure to change whenever this file format changes!
+
+ Records:
+ record_end:
+ 1 byte: record type (record_end).
+ record_reg:
+ 1 byte: record type (record_reg).
+ 8 bytes: register id (network byte order).
+ MAX_REGISTER_SIZE bytes: register value.
+ record_mem:
+ 1 byte: record type (record_mem).
+ 8 bytes: memory address (network byte order).
+ 8 bytes: memory length (network byte order).
+ n bytes: memory value (n == memory length).
+*/
+
+/* Dump the execution log to a file. */
+
+static void
+cmd_record_dump (char *args, int from_tty)
+{
+ char *recfilename, recfilename_buffer[40];
+ int recfd;
+ struct record_entry *cur_record_list;
+ uint32_t magic;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ struct cleanup *old_cleanups;
+ struct cleanup *set_cleanups;
+
+ if (strcmp (current_target.to_shortname, "record") != 0)
+ error (_("Process record is not started.\n"));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default corefile name is "gdb_record.PID". */
+ snprintf (recfilename_buffer, 40, "gdb_record.%d",
+ PIDGET (inferior_ptid));
+ recfilename = recfilename_buffer;
+ }
+
+ /* Open the dump file. */
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Saving recording to file '%s'\n"),
+ recfilename);
+ recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for dump execution records: %s"),
+ recfilename, strerror (errno));
+ old_cleanups = make_cleanup (record_fd_cleanups, &recfd);
+
+ /* Save the current record entry to "cur_record_list". */
+ cur_record_list = record_list;
+
+ /* Get the values of regcache and gdbarch. */
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+
+ /* Disable the GDB operation record. */
+ set_cleanups = record_gdb_operation_disable_set ();
+
+ /* Write the magic code. */
+ magic = RECORD_FILE_MAGIC;
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+ magic);
+ record_write_dump (recfilename, recfd, &magic, 4);
+
+ /* Reverse execute to the begin of record list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == &record_first)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ /* Dump the entries to recfd and forward execute to the end of
+ record list. */
+ while (1)
+ {
+ /* Dump entry. */
+ if (record_list != &record_first)
+ {
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ tmpu8 = record_list->type;
+ record_write_dump (recfilename, recfd, &tmpu8, 1);
+
+ switch (record_list->type)
+ {
+ case record_reg: /* reg */
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+Writing register %d (1 plus 8 plus %d bytes)\n"),
+ record_list->u.reg.num,
+ MAX_REGISTER_SIZE);
+
+ tmpu64 = record_list->u.reg.num;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd, record_list->u.reg.val,
+ MAX_REGISTER_SIZE);
+ break;
+
+ case record_mem: /* mem */
+ if (!record_list->u.mem.mem_entry_not_accessible)
+ {
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+ paddress (gdbarch,
+ record_list->u.mem.addr),
+ record_list->u.mem.len);
+
+ tmpu64 = record_list->u.mem.addr;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ tmpu64 = record_list->u.mem.len;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd,
+ record_list->u.mem.val,
+ record_list->u.mem.len);
+ }
+ break;
+
+ case record_end:
+ /* FIXME: record the contents of record_end rec. */
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Writing record_end (1 byte)\n"));
+ break;
+ }
+ }
+
+ /* Execute entry. */
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->next)
+ record_list = record_list->next;
+ else
+ break;
+ }
+
+ /* Reverse execute to cur_record_list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == cur_record_list)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ do_cleanups (set_cleanups);
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, _("Saved dump of execution "
+ "records to `%s'.\n"),
+ recfilename);
+}
+
/* Truncate the record log from the present point
of replay until the end. */
@@ -1185,7 +1864,12 @@ cmd_record_stop (char *args, int from_tt
{
if (!record_list || !from_tty || query (_("Delete recorded log and "
"stop recording?")))
- unpush_target (&record_ops);
+ {
+ if (strcmp (current_target.to_shortname, "record") == 0)
+ unpush_target (&record_ops);
+ else
+ unpush_target (&record_core_ops);
+ }
}
else
printf_unfiltered (_("Process record is not started.\n"));
@@ -1203,7 +1887,7 @@ set_record_insn_max_num (char *args, int
"the first ones?\n"));
while (record_insn_num > record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
}
}
@@ -1243,6 +1927,8 @@ info_record_command (char *args, int fro
void
_initialize_record (void)
{
+ struct cmd_list_element *c;
+
/* Init record_first. */
record_first.prev = NULL;
record_first.next = NULL;
@@ -1250,6 +1936,8 @@ _initialize_record (void)
init_record_ops ();
add_target (&record_ops);
+ init_record_core_ops ();
+ add_target (&record_core_ops);
add_setshow_zinteger_cmd ("record", no_class, &record_debug,
_("Set debugging of record/replay feature."),
@@ -1259,9 +1947,10 @@ _initialize_record (void)
NULL, show_record_debug, &setdebuglist,
&showdebuglist);
- add_prefix_cmd ("record", class_obscure, cmd_record_start,
- _("Abbreviated form of \"target record\" command."),
- &record_cmdlist, "record ", 0, &cmdlist);
+ c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
+ _("Abbreviated form of \"target record\" command."),
+ &record_cmdlist, "record ", 0, &cmdlist);
+ set_cmd_completer (c, filename_completer);
add_com_alias ("rec", "record", class_obscure, 1);
add_prefix_cmd ("record", class_support, set_record_command,
_("Set record options"), &set_record_cmdlist,
@@ -1276,6 +1965,16 @@ _initialize_record (void)
"info record ", 0, &infolist);
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
+ c = add_cmd ("load", class_obscure, cmd_record_load,
+ _("Load previously dumped execution records from \
+a file given as argument."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ c = add_cmd ("dump", class_obscure, cmd_record_dump,
+ _("Dump the execution records to a file.\n\
+Argument is optional filename. Default filename is
'gdb_record.<process_id>'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
add_cmd ("delete", class_obscure, cmd_record_delete,
_("Delete the rest of execution log and start recording it anew."),