This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFA/RFC] Add dump and load command to process record and replay


Hui Zhu wrote:
On Mon, Aug 24, 2009 at 07:21, Michael Snyder<msnyder@vmware.com> wrote:
Hui Zhu wrote:

Hi Michael,

I make a new version patch.  It has a lot of changes.
Remove record_core and add a new target record_core for core target to
make the code more clear.
Make the load together with record_open.

Please help me review it.
Hi Hui,

In this review, I'm going to comment only on the parts of the
patch that relate to the record_core_ops (ie. pushing the
record stratum on top of the core file stratum).

Those parts of the patch are much improved.  I like this
version a lot better.  Thanks for reworking it.



Hi Michael,

Thanks for your review. I make a new patch that update the memset code to:

Way cool, thanks!


Now I can comment on the dump/load part of the patch.
It does not seem as if previous issues have been addressed:
1) Core file and record file are separate, no way to verify
   that they correspond to each other.
2) Previous log not cleared when loading a new log.

Have you considered combining this patch with the idea that I
proposed -- storing the record log in the core file?  Seems like
that would be very easy now -- gdb already has the core file open,
and there is a global pointer to the BFD.
See gdbcore.h:extern bfd* core_bfd;

In fact, just for fun, I've merged the two patches in my
local tree.  It seems to work quite well.  I can just post
it here if you like, or here's a hint to show you how it goes:

static void
record_load (void)
{
  int recfd;
  uint32_t magic;
  struct cleanup *old_cleanups2;
  struct record_entry *rec;
  int insn_number = 0;
  asection *osec;
  void nullify_last_target_wait_ptid (void);

  /* We load the execution log from the open core bfd,
     if there is one.  */
  if (core_bfd == NULL)
    return;

  if (record_debug)
    fprintf_filtered (gdb_stdlog,
                      _("Restoring recording from core file.\n"));

  /* Now need to find our special note section.  */
  osec = bfd_get_section_by_name (core_bfd, "null0");


            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."),


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]