summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Snyder <msnyder@specifix.com>2009-08-07 02:15:54 +0000
committerMichael Snyder <msnyder@specifix.com>2009-08-07 02:15:54 +0000
commit424cdd679ca21af1b3afd0dccd717f6023fe4b9c (patch)
treef1570845bc658569fd41719e96de15f244bfa1c4
parent43929e8e0c33d7810e19a952918e38429f4da412 (diff)
downloadgdb-424cdd679ca21af1b3afd0dccd717f6023fe4b9c.tar.gz
2009-08-06 Hui Zhu <teawater@gmail.com>
Michael Snyder <msnyder@vmware.com> (Add record dump/load commands: draft version, not ready for release). * record.c (RECORD_FILE_MAGIC): New constant. (record_arch_list_add_reg): Use xcalloc instead of xmalloc. (record_exec_entry): Pre-zero the register buffer. (cmd_record_fd_cleanup): New function (dead). (cmd_record_dump): New function. (bfdcore_read): New function. (cmd_record_load): New function. (_initialize_record): Add 'record dump' and 'record load' commands.
-rw-r--r--gdb/ChangeLog15
-rw-r--r--gdb/record.c665
2 files changed, 669 insertions, 11 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 4a4f06bf0a9..ab4c3edc4bd 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,4 +1,17 @@
-2009-08-06 Michael Snyder <msnyder@msnyder-server.eng.vmware.com>
+2009-08-06 Hui Zhu <teawater@gmail.com>
+ Michael Snyder <msnyder@vmware.com>
+ (Add record dump/load commands: draft version, not ready for release).
+
+ * record.c (RECORD_FILE_MAGIC): New constant.
+ (record_arch_list_add_reg): Use xcalloc instead of xmalloc.
+ (record_exec_entry): Pre-zero the register buffer.
+ (cmd_record_fd_cleanup): New function (dead).
+ (cmd_record_dump): New function.
+ (bfdcore_read): New function.
+ (cmd_record_load): New function.
+ (_initialize_record): Add 'record dump' and 'record load' commands.
+
+2009-08-06 Michael Snyder <msnyder@vmware.com>
* gcore.c (create_gcore_bfd): New function, abstracted
from gcore_command for export.
diff --git a/gdb/record.c b/gdb/record.c
index 324a2d8138e..8c58f2a72c9 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -23,16 +23,21 @@
#include "gdbthread.h"
#include "event-top.h"
#include "exceptions.h"
+#include "completer.h"
#include "record.h"
#include "checkpoint.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) /* Host to network order */
+
/* These are the core struct of record function.
An record_entry is a record of the value change of a register
@@ -249,7 +254,7 @@ record_arch_list_add_reg (struct regcache *regcache, int num)
num);
rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
- rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+ rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE);
rec->prev = NULL;
rec->next = NULL;
rec->type = record_reg;
@@ -441,6 +446,7 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
host_address_to_string (entry),
entry->u.reg.num);
+ memset (reg, 0, sizeof (reg));
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);
@@ -449,6 +455,7 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
case 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 (entry->u.mem.len);
@@ -465,6 +472,8 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
{
if (execution_direction == EXEC_REVERSE)
{
+ /* Read failed --
+ flag entry as not_accessible. */
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
warning (_("Process record: error reading memory at "
@@ -476,7 +485,7 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
error (_("Process record: error reading memory at "
"addr = %s len = %d."),
paddress (gdbarch, entry->u.mem.addr),
- entry->u.mem.len);
+ entry->u.mem.len);
}
else
{
@@ -485,6 +494,8 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
{
if (execution_direction == EXEC_REVERSE)
{
+ /* Write failed --
+ flag entry as not_accessible. */
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
warning (_("Process record: error writing memory at "
@@ -496,11 +507,11 @@ record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
error (_("Process record: error writing memory at "
"addr = %s len = %d."),
paddress (gdbarch, entry->u.mem.addr),
- entry->u.mem.len);
+ entry->u.mem.len);
}
+ else
+ memcpy (entry->u.mem.val, mem, entry->u.mem.len);
}
-
- memcpy (entry->u.mem.val, mem, entry->u.mem.len);
}
}
break;
@@ -803,9 +814,9 @@ record_wait (struct target_ops *ops,
break;
}
- record_exec_entry (regcache, gdbarch, record_list);
+ record_exec_entry (regcache, gdbarch, record_list);
- if (record_list->type == record_end)
+ if (record_list->type == record_end)
{
if (record_debug > 1)
fprintf_unfiltered (gdb_stdlog,
@@ -1024,6 +1035,7 @@ record_store_registers (struct target_ops *ops, struct regcache *regcache,
record_registers_change (regcache, regno);
}
+
record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
regcache, regno);
}
@@ -1183,6 +1195,628 @@ cmd_record_start (char *args, int from_tty)
execute_command ("target record", from_tty);
}
+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
+
+/* Record log save-file format
+ Version 1
+
+ Header:
+ 4 bytes: magic number RECORD_FILE_MAGIC.
+ 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
+ 16 bytes: register value
+ record_mem:
+ 1 byte: record type (record_mem)
+ 8 bytes: memory address
+ 8 bytes: memory length
+ n bytes: memory value (n == memory length)
+
+ Version 2 (proposed)
+
+ Header:
+ 4 bytes: magic number RECORD_FILE_MAGIC.
+ NOTE: be sure to change whenever this file format changes!
+ n bytes: architecture...
+ 4 bytes: size of register snapshot
+ n bytes: register snapshot
+ 4 bytes: number of section crcs
+ n bytes: section names with crcs
+
+ Records:
+ See version 1.
+ */
+
+/* Dump the execution log to a file. */
+
+#include "elf-bfd.h"
+
+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 (current_target.to_stratum != record_stratum)
+ error (_("Process record is not started.\n"));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default recfile name is "rec.PID". */
+ sprintf (recfilename_buffer, "rec.%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);
+
+ /* Michael-style, elf core dump file. */
+ {
+ extern void write_gcore_file (bfd *);
+ extern bfd *create_gcore_bfd (char *);
+ bfd *obfd;
+ int dump_size = 0;
+ asection *osec = NULL;
+ struct record_entry *p;
+ int bfd_offset = 0;
+
+ /* Open the output file. */
+ obfd = create_gcore_bfd (recfilename);
+
+ /* Need a cleanup that will close the file (FIXME: delete it?). */
+ old_cleanups = make_cleanup_bfd_close (obfd);
+
+ /* 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 ();
+
+ /* Reverse execute to the begin of record list. */
+ for (; record_list && record_list != &record_first;
+ record_list = record_list->prev)
+ {
+ record_exec_entry (regcache, gdbarch, record_list);
+ }
+
+ /* Compute the size needed for the extra bfd section. */
+ dump_size = 4; /* magic cookie */
+ for (p = &record_first; p; p = p->next)
+ switch (p->type)
+ {
+ case record_end:
+ dump_size += 1;
+ break;
+ case record_reg:
+ dump_size += 1 + 8 + MAX_REGISTER_SIZE;
+ break;
+ case record_mem:
+ dump_size += 1 + 8 + 8 + p->u.mem.len;
+ break;
+ }
+
+ /* Make the new bfd section. */
+ osec = bfd_make_section_anyway (obfd, "precord");
+ bfd_set_section_size (obfd, osec, dump_size);
+ bfd_set_section_vma (obfd, osec, 0);
+ bfd_section_lma (obfd, osec) = 0;
+ bfd_set_section_flags (obfd, osec, SEC_ALLOC | SEC_HAS_CONTENTS);
+
+ /* Save corefile state. */
+ write_gcore_file (obfd);
+
+ /* Write out the record log (modified Hui method). */
+ /* 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);
+ if (bfd_set_section_contents (obfd, osec, &magic,
+ bfd_offset, sizeof (magic)))
+ bfd_offset += sizeof (magic);
+ else
+ error (_("Failed to write 'magic' to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+
+ /* Dump the entries into the new bfd section. */
+ for (p = &record_first; p; p = p->next)
+ {
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ tmpu8 = p->type;
+ if (bfd_set_section_contents (obfd, osec, &tmpu8,
+ bfd_offset, sizeof (tmpu8)))
+ bfd_offset += sizeof (tmpu8);
+ else
+ error (_("Failed to write 'type' to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+
+ switch (p->type)
+ {
+ case record_reg: /* reg */
+ tmpu64 = p->u.reg.num;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Writing register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
+ p->u.reg.num,
+ *(ULONGEST *) p->u.reg.val,
+ MAX_REGISTER_SIZE);
+ /* FIXME: register num does not need 8 bytes. */
+ if (bfd_set_section_contents (obfd, osec, &tmpu64,
+ bfd_offset, sizeof (tmpu64)))
+ bfd_offset += sizeof (tmpu64);
+ else
+ error (_("Failed to write regnum to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+
+ /* FIXME: add a len field, and write the smaller value. */
+ if (bfd_set_section_contents (obfd, osec,
+ p->u.reg.val,
+ bfd_offset,
+ MAX_REGISTER_SIZE))
+ bfd_offset += MAX_REGISTER_SIZE;
+ else
+ error (_("Failed to write regval to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+ break;
+ case record_mem: /* mem */
+ tmpu64 = p->u.mem.addr;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Writing memory 0x%08x (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+ (unsigned int) p->u.mem.addr,
+ p->u.mem.len);
+ if (bfd_set_section_contents (obfd, osec, &tmpu64,
+ bfd_offset, sizeof (tmpu64)))
+ bfd_offset += sizeof (tmpu64);
+ else
+ error (_("Failed to write memaddr to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+
+ tmpu64 = p->u.mem.len;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+
+ /* FIXME: len does not need 8 bytes. */
+ if (bfd_set_section_contents (obfd, osec, &tmpu64,
+ bfd_offset, sizeof (tmpu64)))
+ bfd_offset += sizeof (tmpu64);
+ else
+ error (_("Failed to write memlen to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+
+ if (bfd_set_section_contents (obfd, osec,
+ p->u.mem.val,
+ bfd_offset,
+ p->u.mem.len))
+ bfd_offset += p->u.mem.len;
+ else
+ error (_("Failed to write memval to %s (%s)"),
+ recfilename, bfd_errmsg (bfd_get_error ()));
+ 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;
+ }
+ }
+
+ /* Now forward-execute back to the saved entry. */
+ for (record_list = &record_first;
+ record_list && record_list != cur_record_list;
+ record_list = record_list->next)
+ {
+ record_exec_entry (regcache, gdbarch, record_list);
+ }
+ /* Clean-ups will close the output file and free malloc memory. */
+ do_cleanups (old_cleanups);
+ }
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Saved recfile %s.\n", recfilename);
+}
+
+static int
+bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
+{
+ int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
+
+ if (ret)
+ *offset += len;
+ return ret;
+}
+
+/* Load the execution log from a file. */
+
+#include "gdbcore.h"
+#include <ctype.h>
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+ extern void nullify_last_target_wait_ptid (void);
+ int recfd;
+ uint32_t magic;
+ struct cleanup *old_cleanups;
+ struct cleanup *old_cleanups2;
+ struct record_entry *rec;
+ int insn_number = 0;
+
+ if (!args || (args && !*args))
+ error (_("Argument for filename required.\n"));
+
+ /* Open the load file. */
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Restoring recording from file '%s'\n"), args);
+#if 1
+ /* Michael-style elf core dump file. */
+ {
+ bfd *core_bfd;
+ asection *osec;
+ extern bfd *load_corefile (char *, int);
+
+ /* Restore corefile regs and mem sections. */
+ core_bfd = load_corefile (args, from_tty);
+ old_cleanups = make_cleanup_bfd_close (core_bfd);
+
+ /* Now need to find our special note section. */
+ osec = bfd_get_section_by_name (core_bfd, "null0");
+ printf_filtered ("Find precord section %s.\n",
+ osec ? "succeeded" : "failed");
+
+ if (osec)
+ {
+ int i, len;
+ int bfd_offset = 0;
+
+ printf_filtered ("osec name = '%s'\n",
+ bfd_section_name (core_bfd, osec));
+ len = (int) bfd_section_size (core_bfd, osec);
+ printf_filtered ("osec size = %d\n", len);
+
+ /* Check the magic code. */
+ if (!bfdcore_read (core_bfd, osec, &magic,
+ sizeof (magic), &bfd_offset))
+ error (_("Failed to read 'magic' from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s', version mis-match / file format error."),
+ args);
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+ magic);
+ if (current_target.to_stratum != record_stratum)
+ {
+ /* FIXME need cleanup! We might error out. */
+ cmd_record_start (NULL, from_tty);
+ printf_unfiltered (_("Auto start process record.\n"));
+ }
+
+ /* Load the entries in recfd to the record_arch_list_head and
+ record_arch_list_tail. */
+ /* FIXME free old records? */
+ record_list_release (record_arch_list_tail);
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ old_cleanups2 = make_cleanup (record_message_cleanups, 0);
+
+ while (1)
+ {
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ if (!bfdcore_read (core_bfd, osec, &tmpu8,
+ sizeof (tmpu8), &bfd_offset))
+ break;
+
+ switch (tmpu8)
+ {
+ case record_reg: /* reg */
+ /* FIXME: abstract out into an 'insert' function. */
+ rec = (struct record_entry *)
+ xmalloc (sizeof (struct record_entry));
+ rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+ /* Get num. */
+ /* FIXME: register num does not need 8 bytes. */
+ if (!bfdcore_read (core_bfd, osec, &tmpu64,
+ sizeof (tmpu64), &bfd_offset))
+ error (_("Failed to read regnum from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.reg.num = tmpu64;
+
+ /* Get val. */
+ if (!bfdcore_read (core_bfd, osec, rec->u.reg.val,
+ MAX_REGISTER_SIZE, &bfd_offset))
+ error (_("Failed to read regval from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
+ rec->u.reg.num,
+ *(ULONGEST *) rec->u.reg.val,
+ 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. */
+ if (!bfdcore_read (core_bfd, osec, &tmpu64,
+ sizeof (tmpu64), &bfd_offset))
+ error (_("Failed to read memaddr from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.mem.addr = tmpu64;
+
+ /* Get len. */
+ /* FIXME: len does not need 8 bytes. */
+ if (!bfdcore_read (core_bfd, osec, &tmpu64,
+ sizeof (tmpu64), &bfd_offset))
+ error (_("Failed to read memlen from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+ 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. */
+ if (!bfdcore_read (core_bfd, osec, rec->u.mem.val,
+ rec->u.mem.len, &bfd_offset))
+ error (_("Failed to read memval from %s (%s)"),
+ args, bfd_errmsg (bfd_get_error ()));
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"),
+ (unsigned int) rec->u.mem.addr,
+ rec->u.mem.len);
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ /* FIXME: restore the contents of record_end rec. */
+ rec = (struct record_entry *)
+ xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading record_end (one byte)\n"));
+ record_arch_list_add (rec);
+ insn_number ++;
+ break;
+
+ default:
+ error (_("Format of '%s' is not right."), args);
+ break;
+
+ }
+ }
+ }
+
+ discard_cleanups (old_cleanups2);
+
+ /* Add record_arch_list_head to the end of record list. (??? FIXME)*/
+ 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);
+ }
+#endif
+
+#if 0
+ /* Hui-style binary dump file. */
+ recfd = open (args, O_RDONLY | O_BINARY);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for load."), args);
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Check the magic code. */
+ if (read (recfd, &magic, 4) != 4)
+ error (_("Failed to read '%s' for load."), args);
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s' is not a record dump."), args);
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+ magic);
+
+ if (current_target.to_stratum != record_stratum)
+ {
+ /* FIXME need cleanup! We might error out. */
+ cmd_record_start (NULL, from_tty);
+ printf_unfiltered (_("Auto start process record.\n"));
+ }
+
+ /* 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_message_cleanups, 0);
+
+ while (1)
+ {
+ int ret;
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ ret = read (recfd, &tmpu8, 1);
+ if (ret < 0)
+ error (_("Failed to read '%s' for load."), args);
+ if (ret == 0)
+ break;
+
+ switch (tmpu8)
+ {
+ case record_reg: /* reg */
+ /* FIXME: abstract out into an 'insert' function. */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+ /* Get num. */
+ /* FIXME: register num does not need 8 bytes. */
+ if (read (recfd, &tmpu64, 8) != 8)
+ error (_("Failed to read '%s' for load."), args);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+
+ rec->u.reg.num = tmpu64;
+ /* Get val. */
+ if (read (recfd, rec->u.reg.val,
+ MAX_REGISTER_SIZE) != MAX_REGISTER_SIZE)
+ error (_("Failed to read '%s' for load."), args);
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
+ rec->u.reg.num,
+ *(ULONGEST *) rec->u.reg.val,
+ 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. */
+ if (read (recfd, &tmpu64, 8) != 8)
+ error (_("Failed to read '%s' for load."), args);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+
+ rec->u.mem.addr = tmpu64;
+ /* Get len. */
+ /* FIXME: len does not need 8 bytes. */
+ if (read (recfd, &tmpu64, 8) != 8)
+ error (_("Failed to read '%s' for load."), args);
+ 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. */
+ if (read (recfd, rec->u.mem.val,
+ rec->u.mem.len) != rec->u.mem.len)
+ error (_("Failed to read '%s' for load."), args);
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"),
+ (unsigned int) rec->u.mem.addr,
+ rec->u.mem.len);
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ /* FIXME: restore the contents of record_end rec. */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+ if (record_debug)
+ fprintf_unfiltered (gdb_stdlog, _("\
+ Reading record_end (one byte)\n"));
+ record_arch_list_add (rec);
+ insn_number ++;
+ break;
+
+ default:
+ error (_("Format of '%s' is not right."), args);
+ 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);
+#endif
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+ registers_changed ();
+ reinit_frame_cache ();
+ nullify_last_target_wait_ptid ();
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
/* Truncate the record log from the present point
of replay until the end. */
@@ -1392,6 +2026,8 @@ record_restore_checkpoint (struct checkpoint_info *cp, int from_tty)
void
_initialize_record (void)
{
+ struct cmd_list_element *c;
+
/* Init record_first. */
record_first.prev = NULL;
record_first.next = NULL;
@@ -1425,6 +2061,15 @@ _initialize_record (void)
"info record ", 0, &infolist);
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
+ c = add_cmd ("dump", class_obscure, cmd_record_dump,
+ _("Dump the execution log to a file.\n\
+Argument is optional filename. Default filename is 'rec.<process_id>'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ c = add_cmd ("load", class_obscure, cmd_record_load,
+ _("Load the execution log from a file. Argument is filename."),
+ &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."),
@@ -1439,15 +2084,15 @@ _initialize_record (void)
/* Record instructions number limit command. */
add_setshow_boolean_cmd ("stop-at-limit", no_class,
- &record_stop_at_limit, _("\
+ &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\
When ON, if the record/replay buffer becomes full, ask user what to do.\n\
When OFF, if the record/replay buffer becomes full,\n\
delete the oldest recorded instruction to make room for each new one."),
- NULL, NULL,
- &set_record_cmdlist, &show_record_cmdlist);
+ NULL, NULL,
+ &set_record_cmdlist, &show_record_cmdlist);
add_setshow_zinteger_cmd ("insn-number-max", no_class,
&record_insn_max_num,
_("Set record/replay buffer limit."),