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 (file format etc)



msnyder:
If you look at the code that dumps the file (and I had
to add a bunch of printfs and stuff to figure it out), you'll
see that the dump file looks like this:

4 byte magic number
Series of the following:
1) 1 byte tag (record_end), or
2) 1 byte tag (record_rec) followed by
  8 byte number (register id), followed by
  MAX_REGISTER_SIZE byte value (register value): or
3) 1 byte tag (record_mem) followed by
  8 byte number (memory address) followed by
  8 byte number (memory length) followed by
  N byte buffer (memory value).

If you look for the #if (BYTE_ORDER == BIG_ENDIAN), this is
where Teawater is making the byte orders host-independent.

I was going to mention the ifdefs eventually. ;-)

Hui Zhu:
Cool.  It's very clear.  :)
Thanks Michael.

I make a new version patch to follow the cvs head.  And removed the ifdef.
Please help me review it.

OK, well, let me start out with some thoughts about the idea and the architecture.

It's a great idea to save and reload a recording log.
We need to document the file format, as I've informally
done above, maybe by placing a comment in the file.
I'll post a suggested comment soon.

Once that's done, it becomes obvious that we are only saving
state changes -- no snapshot of the starting state.  Then it
becomes obvious why you need to save and load a corefile as well --
that gives you the starting state snapshot, to go with your
saved state changes.

The trouble that I see now is that there is no insurance that
the starting state (core file) and the state changes (record file)
correspond to one another.  A user could get into trouble by
loading one starting state and a DIFFERENT set of state changes.
And he would have no way of knowing why his debugging session
was producing nonsense results.

This is a little bit like the problem of storing separate
symbol files (separate from the executable file).  We need
a way to confirm that the two files correspond with one another.

I've given it a little thought, and I would like to hear
what others think.  Here are some of my ideas:

1) Store a pointer to the corefile in the record log file.
2) Store a checksum, hash, or CRC of the corefile in the log file.
3) Store a CRC of the loadable sections, plus a snapshot of the
   register set, in the log file.
4) Store both corefile and log file together in the same file.

Of course, every time we change the file format, we should
change the magic number to insure that a new-format gdb will
not attempt to read an old-format file.

Discussion?


2009-08-05 Hui Zhu <teawater@gmail.com>

Add dump and load command to process record and replay.

        * record.c (completer.h, arch-utils.h, gdbcore.h, exec.h,
        byteswap.h, netinet/in.h): Include files.
        (RECORD_IS_REPLAY): Return true if record_core is true.
        (RECORD_FILE_MAGIC): New macro.
        (record_core_buf_entry): New struct.
        (record_core, record_core_regbuf, record_core_start,
        record_core_end, record_core_buf_list,
        record_beneath_to_fetch_registers_ops,
        record_beneath_to_fetch_registers,
        record_beneath_to_store_registers_ops,
        record_beneath_to_store_registers,
        record_beneath_to_has_execution_ops,
        record_beneath_to_has_execution,
        record_beneath_to_prepare_to_store): New variables.
        (record_list_release_first_insn): Change function
        record_list_release_first to this name.
        (record_arch_list_cleanups): New function.
        (record_message_cleanups): Removed.
        (record_message): Change to call record_arch_list_cleanups
        and record_list_release_first_insn.
        (record_exec_entry): New function.
        (record_open): Add support for target core.
        (record_close): Add support for target core.
        (record_wait): Call function 'record_exec_entry'.
        (record_kill): Add support for target core.
        (record_registers_change): Call record_list_release_first_insn.
        (record_fetch_registers): New function.
        (record_prepare_to_store): New function.
        (record_store_registers): Add support for target core.
        (record_xfer_partial): Add support for target core.
        (record_has_execution): New function.
        (init_record_ops): Set record_ops.to_fetch_registers,
        record_ops.to_prepare_to_store
        and record_ops.to_has_execution.
        (cmd_record_fd_cleanups): New function.
        (cmd_record_dump): New function.
        (cmd_record_load): New function.
        (set_record_insn_max_num): Call record_list_release_first_insn.
        (_initialize_record): Add commands "record dump"
        and "record load".

---
 record.c |  731 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 640 insertions(+), 91 deletions(-)

--- a/record.c
+++ b/record.c
@@ -23,14 +23,22 @@
 #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)
+     (record_list->next || execution_direction == EXEC_REVERSE || record_core)
+
+#define RECORD_FILE_MAGIC      htonl(0x20090726)

/* These are the core struct of record function.

@@ -78,9 +86,23 @@ 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 with core target.  */
+static int record_core = 0;
+static gdb_byte *record_core_regbuf;
+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;
@@ -103,6 +125,14 @@ static struct target_ops *record_beneath
 static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
                                         struct target_waitstatus *,
                                         int);
+static struct target_ops *record_beneath_to_fetch_registers_ops;
+static void (*record_beneath_to_fetch_registers) (struct target_ops *,
+                                                  struct regcache *,
+                                                 int regno);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+                                                  struct regcache *,
+                                                 int regno);
 static struct target_ops *record_beneath_to_store_registers_ops;
 static void (*record_beneath_to_store_registers) (struct target_ops *,
                                                   struct regcache *,
@@ -119,6 +149,9 @@ static int (*record_beneath_to_insert_br
                                                   struct bp_target_info *);
 static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
+static struct target_ops *record_beneath_to_has_execution_ops;
+static int (*record_beneath_to_has_execution) (struct target_ops *ops);
+static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache);

 static void
 record_list_release (struct record_entry *rec)
@@ -169,7 +202,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 +373,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 +419,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,6 +449,91 @@ 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))
+              {
+                 if ((execution_direction == EXEC_REVERSE && !record_core)
+                     || (execution_direction != EXEC_REVERSE && record_core))
+                  {
+                    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
+                  error (_("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))
+                  {
+                     if ((execution_direction == EXEC_REVERSE && !record_core)
+                         || (execution_direction != EXEC_REVERSE &&
record_core))
+                      {
+                        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
+                      error (_("Process record: error writing memory at "
+                               "addr = %s len = %d."),
+                             paddress (gdbarch, entry->u.mem.addr),
+                            entry->u.mem.len);
+                  }
+              }
+
+            memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+          }
+      }
+      break;
+    }
+}
+
 static void
 record_open (char *name, int from_tty)
 {
@@ -424,8 +542,13 @@ record_open (char *name, int from_tty)
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");

+  if (!strcmp (current_target.to_shortname, "core"))
+    record_core = 1;
+  else
+    record_core = 0;
+
   /* check exec */
-  if (!target_has_execution)
+  if (!target_has_execution && !record_core)
     error (_("Process record: the program is not being run."));
   if (non_stop)
     error (_("Process record target can't debug inferior in non-stop mode "
@@ -454,6 +577,8 @@ record_open (char *name, int from_tty)
   record_beneath_to_xfer_partial = NULL;
   record_beneath_to_insert_breakpoint = NULL;
   record_beneath_to_remove_breakpoint = NULL;
+  record_beneath_to_has_execution = NULL;
+  record_beneath_to_prepare_to_store = NULL;

   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -468,6 +593,11 @@ record_open (char *name, int from_tty)
          record_beneath_to_wait = t->to_wait;
          record_beneath_to_wait_ops = t;
         }
+      if (!record_beneath_to_fetch_registers)
+        {
+         record_beneath_to_fetch_registers = t->to_fetch_registers;
+         record_beneath_to_fetch_registers_ops = t;
+        }
       if (!record_beneath_to_store_registers)
         {
          record_beneath_to_store_registers = t->to_store_registers;
@@ -482,19 +612,51 @@ record_open (char *name, int from_tty)
        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 (!record_beneath_to_has_execution)
+        {
+          record_beneath_to_has_execution_ops = t;
+         record_beneath_to_has_execution = t->to_has_execution;
+        }
+      if (!record_beneath_to_prepare_to_store)
+       record_beneath_to_prepare_to_store = t->to_prepare_to_store;
     }
-  if (!record_beneath_to_resume)
+  if (!record_beneath_to_resume && !record_core)
     error (_("Process record can't get to_resume."));
-  if (!record_beneath_to_wait)
+  if (!record_beneath_to_wait && !record_core)
     error (_("Process record can't get to_wait."));
-  if (!record_beneath_to_store_registers)
+  if (!record_beneath_to_fetch_registers)
+    error (_("Process record can't get to_fetch_registers."));
+  if (!record_beneath_to_store_registers && !record_core)
     error (_("Process record can't get to_store_registers."));
   if (!record_beneath_to_xfer_partial)
     error (_("Process record can't get to_xfer_partial."));
-  if (!record_beneath_to_insert_breakpoint)
+  if (!record_beneath_to_insert_breakpoint && !record_core)
     error (_("Process record can't get to_insert_breakpoint."));
-  if (!record_beneath_to_remove_breakpoint)
+  if (!record_beneath_to_remove_breakpoint && !record_core)
     error (_("Process record can't get to_remove_breakpoint."));
+  if (!record_beneath_to_has_execution && !record_core)
+    error (_("Process record can't get to_has_execution."));
+  if (!record_beneath_to_prepare_to_store && !record_core)
+    error (_("Process record can't get to_prepare_to_store."));
+
+  if (record_core)
+    {
+      /* Get record_core_regbuf.  */
+      struct regcache *regcache = get_current_regcache ();
+      int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+      int i;
+
+      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))
+        error (_("\"%s\": Can't find sections: %s"),
+               bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+    }

push_target (&record_ops);

@@ -507,10 +669,26 @@ record_open (char *name, int from_tty)
 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.  */
+  xfree (record_core_regbuf);
+
+  /* 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;
@@ -712,76 +890,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,7 +1012,9 @@ record_kill (struct target_ops *ops)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");

   unpush_target (&record_ops);
-  target_kill ();
+
+  if (!record_core)
+    target_kill ();
 }

 /* Record registers change (by user or by GDB) to list as an instruction.  */
@@ -945,15 +1058,58 @@ 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++;
 }

 static void
+record_fetch_registers (struct target_ops *ops, struct regcache *regcache,
+                        int regno)
+{
+  if (record_core)
+    {
+      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);
+    }
+  else
+    record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops,
+                                       regcache, regno);
+}
+
+static void
+record_prepare_to_store (struct regcache *regcache)
+{
+  if (!record_core)
+    record_beneath_to_prepare_to_store (regcache);
+}
+
+static void
 record_store_registers (struct target_ops *ops, struct regcache *regcache,
                         int regno)
 {
+  if (record_core)
+    {
+      /* Debug with core.  */
+      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."));
+
+      return;
+    }
+
   if (!record_gdb_operation_disable)
     {
       if (RECORD_IS_REPLAY)
@@ -1000,6 +1156,7 @@ record_store_registers (struct target_op

       record_registers_change (regcache, regno);
     }
+
   record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
                                      regcache, regno);
 }
@@ -1015,7 +1172,7 @@ record_xfer_partial (struct target_ops *
 {
   if (!record_gdb_operation_disable
       && (object == TARGET_OBJECT_MEMORY
-         || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+         || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core)
     {
       if (RECORD_IS_REPLAY)
        {
@@ -1059,11 +1216,91 @@ 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++;
     }

+   if (record_core && object == TARGET_OBJECT_MEMORY)
+     {
+       /* Debug with core.  */
+       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 0;
+         }
+       else
+         error (_("You can't do that without a process to debug."));
+
+       return 0;
+     }
+
   return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
                                          object, annex, readbuf, writebuf,
                                          offset, len);
@@ -1113,6 +1350,15 @@ record_can_execute_reverse (void)
   return 1;
 }

+int
+record_has_execution (struct target_ops *ops)
+{
+  if (record_core)
+    return 1;
+
+  return record_beneath_to_has_execution (ops);
+}
+
 static void
 init_record_ops (void)
 {
@@ -1129,11 +1375,14 @@ init_record_ops (void)
   record_ops.to_mourn_inferior = record_mourn_inferior;
   record_ops.to_kill = record_kill;
   record_ops.to_create_inferior = find_default_create_inferior;
+  record_ops.to_fetch_registers = record_fetch_registers;
+  record_ops.to_prepare_to_store = record_prepare_to_store;
   record_ops.to_store_registers = record_store_registers;
   record_ops.to_xfer_partial = record_xfer_partial;
   record_ops.to_insert_breakpoint = record_insert_breakpoint;
   record_ops.to_remove_breakpoint = record_remove_breakpoint;
   record_ops.to_can_execute_reverse = record_can_execute_reverse;
+  record_ops.to_has_execution = record_has_execution;
   record_ops.to_stratum = record_stratum;
   record_ops.to_magic = OPS_MAGIC;
 }
@@ -1154,6 +1403,294 @@ cmd_record_start (char *args, int from_t
   execute_command ("target record", from_tty);
 }

+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+  int recfd = *(int *) recfdp;
+  close (recfd);
+}
+
+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 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);
+}
+
+/* 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 (current_target.to_stratum != record_stratum)
+    error (_("Process record is not started.\n"));
+
+  if (args && *args)
+    recfilename = args;
+  else
+    {
+      /* Default corefile name is "gdb_record.PID".  */
+      sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid));
+      recfilename = recfilename_buffer;
+    }
+
+  /* Open the dump file.  */
+  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 (cmd_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;
+  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 */
+              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)
+                {
+                  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;
+            }
+        }
+
+      /* 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);
+}
+
+/* Load the execution log from a file.  */
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+  int recfd;
+  uint32_t magic;
+  struct cleanup *old_cleanups;
+  struct cleanup *old_cleanups2;
+  struct record_entry *rec;
+  int insn_number = 0;
+
+  if (current_target.to_stratum != record_stratum)
+    {
+      cmd_record_start (NULL, from_tty);
+      printf_unfiltered (_("Auto start process record.\n"));
+    }
+
+  if (!args || (args && !*args))
+    error (_("Argument for filename required.\n"));
+
+  /* Open the load file.  */
+  recfd = open (args, O_RDONLY | O_BINARY);
+  if (recfd < 0)
+    error (_("Failed to open '%s' for loading execution records: %s"),
+           args, strerror (errno));
+  old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+  /* Check the magic code.  */
+  record_read_dump (args, recfd, &magic, 4);
+  if (magic != RECORD_FILE_MAGIC)
+    error (_("'%s' is not a valid dump of execution records."), args);
+
+  /* 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'."), args);
+      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 (args, recfd, &tmpu64, 8);
+          if (BYTE_ORDER == LITTLE_ENDIAN)
+            tmpu64 = bswap_64 (tmpu64);
+          rec->u.reg.num = tmpu64;
+          /* Get val.  */
+          record_read_dump (args, recfd, 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.  */
+          record_read_dump (args, recfd, &tmpu64, 8);
+          if (BYTE_ORDER == LITTLE_ENDIAN)
+            tmpu64 = bswap_64 (tmpu64);
+          rec->u.mem.addr = tmpu64;
+          /* Get len.  */
+          record_read_dump (args, 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 (args, recfd, rec->u.mem.val, rec->u.mem.len);
+          record_arch_list_add (rec);
+          break;
+
+        case record_end: /* end */
+          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."), 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);
+
+  /* Succeeded.  */
+  fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+}
+
 /* Truncate the record log from the present point
    of replay until the end.  */

@@ -1204,7 +1741,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 ();
     }
 }

@@ -1244,6 +1781,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;
@@ -1277,6 +1816,16 @@ _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 records to a file.\n\
+Argument is optional filename.  Default filename is
'gdb_record.<process_id>'."),
+               &record_cmdlist);
+  set_cmd_completer (c, filename_completer);
+  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);

   add_cmd ("delete", class_obscure, cmd_record_delete,
           _("Delete the rest of execution log and start recording it anew."),


------------------------------------------------------------------------


---
 record.c |  731 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 640 insertions(+), 91 deletions(-)

--- a/record.c
+++ b/record.c
@@ -23,14 +23,22 @@
#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)
+ (record_list->next || execution_direction == EXEC_REVERSE || record_core)
+
+#define RECORD_FILE_MAGIC htonl(0x20090726)
/* These are the core struct of record function.
@@ -78,9 +86,23 @@ 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 with core target. */
+static int record_core = 0;
+static gdb_byte *record_core_regbuf;
+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;
@@ -103,6 +125,14 @@ static struct target_ops *record_beneath
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
struct target_waitstatus *,
int);
+static struct target_ops *record_beneath_to_fetch_registers_ops;
+static void (*record_beneath_to_fetch_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
static struct target_ops *record_beneath_to_store_registers_ops;
static void (*record_beneath_to_store_registers) (struct target_ops *,
struct regcache *,
@@ -119,6 +149,9 @@ static int (*record_beneath_to_insert_br
struct bp_target_info *);
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
struct bp_target_info *);
+static struct target_ops *record_beneath_to_has_execution_ops;
+static int (*record_beneath_to_has_execution) (struct target_ops *ops);
+static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache);
static void
record_list_release (struct record_entry *rec)
@@ -169,7 +202,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 +373,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 +419,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,6 +449,91 @@ 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))
+ {
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE && record_core))
+ {
+ 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
+ error (_("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))
+ {
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE && record_core))
+ {
+ 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
+ error (_("Process record: error writing memory at "
+ "addr = %s len = %d."),
+ paddress (gdbarch, entry->u.mem.addr),
+ entry->u.mem.len);
+ }
+ }
+
+ memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+ }
+ }
+ break;
+ }
+}
+
static void
record_open (char *name, int from_tty)
{
@@ -424,8 +542,13 @@ record_open (char *name, int from_tty)
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+ if (!strcmp (current_target.to_shortname, "core"))
+ record_core = 1;
+ else
+ record_core = 0;
+
/* check exec */
- if (!target_has_execution)
+ if (!target_has_execution && !record_core)
error (_("Process record: the program is not being run."));
if (non_stop)
error (_("Process record target can't debug inferior in non-stop mode "
@@ -454,6 +577,8 @@ record_open (char *name, int from_tty)
record_beneath_to_xfer_partial = NULL;
record_beneath_to_insert_breakpoint = NULL;
record_beneath_to_remove_breakpoint = NULL;
+ record_beneath_to_has_execution = NULL;
+ record_beneath_to_prepare_to_store = NULL;
/* Set the beneath function pointers. */
for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -468,6 +593,11 @@ record_open (char *name, int from_tty)
record_beneath_to_wait = t->to_wait;
record_beneath_to_wait_ops = t;
}
+ if (!record_beneath_to_fetch_registers)
+ {
+ record_beneath_to_fetch_registers = t->to_fetch_registers;
+ record_beneath_to_fetch_registers_ops = t;
+ }
if (!record_beneath_to_store_registers)
{
record_beneath_to_store_registers = t->to_store_registers;
@@ -482,19 +612,51 @@ record_open (char *name, int from_tty)
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 (!record_beneath_to_has_execution)
+ {
+ record_beneath_to_has_execution_ops = t;
+ record_beneath_to_has_execution = t->to_has_execution;
+ }
+ if (!record_beneath_to_prepare_to_store)
+ record_beneath_to_prepare_to_store = t->to_prepare_to_store;
}
- if (!record_beneath_to_resume)
+ if (!record_beneath_to_resume && !record_core)
error (_("Process record can't get to_resume."));
- if (!record_beneath_to_wait)
+ if (!record_beneath_to_wait && !record_core)
error (_("Process record can't get to_wait."));
- if (!record_beneath_to_store_registers)
+ if (!record_beneath_to_fetch_registers)
+ error (_("Process record can't get to_fetch_registers."));
+ if (!record_beneath_to_store_registers && !record_core)
error (_("Process record can't get to_store_registers."));
if (!record_beneath_to_xfer_partial)
error (_("Process record can't get to_xfer_partial."));
- if (!record_beneath_to_insert_breakpoint)
+ if (!record_beneath_to_insert_breakpoint && !record_core)
error (_("Process record can't get to_insert_breakpoint."));
- if (!record_beneath_to_remove_breakpoint)
+ if (!record_beneath_to_remove_breakpoint && !record_core)
error (_("Process record can't get to_remove_breakpoint."));
+ if (!record_beneath_to_has_execution && !record_core)
+ error (_("Process record can't get to_has_execution."));
+ if (!record_beneath_to_prepare_to_store && !record_core)
+ error (_("Process record can't get to_prepare_to_store."));
+
+ if (record_core)
+ {
+ /* Get record_core_regbuf. */
+ struct regcache *regcache = get_current_regcache ();
+ int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ 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))
+ error (_("\"%s\": Can't find sections: %s"),
+ bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+ }
push_target (&record_ops);
@@ -507,10 +669,26 @@ record_open (char *name, int from_tty)
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. */
+ xfree (record_core_regbuf);
+
+ /* 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;
@@ -712,76 +890,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,7 +1012,9 @@ record_kill (struct target_ops *ops)
fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
unpush_target (&record_ops);
- target_kill ();
+
+ if (!record_core)
+ target_kill ();
}
/* Record registers change (by user or by GDB) to list as an instruction. */
@@ -945,15 +1058,58 @@ 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++;
}
static void
+record_fetch_registers (struct target_ops *ops, struct regcache *regcache,
+ int regno)
+{
+ if (record_core)
+ {
+ 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);
+ }
+ else
+ record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops,
+ regcache, regno);
+}
+
+static void
+record_prepare_to_store (struct regcache *regcache)
+{
+ if (!record_core)
+ record_beneath_to_prepare_to_store (regcache);
+}
+
+static void
record_store_registers (struct target_ops *ops, struct regcache *regcache,
int regno)
{
+ if (record_core)
+ {
+ /* Debug with core. */
+ 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."));
+
+ return;
+ }
+
if (!record_gdb_operation_disable)
{
if (RECORD_IS_REPLAY)
@@ -1000,6 +1156,7 @@ record_store_registers (struct target_op
record_registers_change (regcache, regno);
}
+
record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
regcache, regno);
}
@@ -1015,7 +1172,7 @@ record_xfer_partial (struct target_ops *
{
if (!record_gdb_operation_disable
&& (object == TARGET_OBJECT_MEMORY
- || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+ || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core)
{
if (RECORD_IS_REPLAY)
{
@@ -1059,11 +1216,91 @@ 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++;
}
+ if (record_core && object == TARGET_OBJECT_MEMORY)
+ {
+ /* Debug with core. */
+ 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 0;
+ }
+ else
+ error (_("You can't do that without a process to debug."));
+
+ return 0;
+ }
+
return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
object, annex, readbuf, writebuf,
offset, len);
@@ -1113,6 +1350,15 @@ record_can_execute_reverse (void)
return 1;
}
+int
+record_has_execution (struct target_ops *ops)
+{
+ if (record_core)
+ return 1;
+
+ return record_beneath_to_has_execution (ops);
+}
+
static void
init_record_ops (void)
{
@@ -1129,11 +1375,14 @@ init_record_ops (void)
record_ops.to_mourn_inferior = record_mourn_inferior;
record_ops.to_kill = record_kill;
record_ops.to_create_inferior = find_default_create_inferior;
+ record_ops.to_fetch_registers = record_fetch_registers;
+ record_ops.to_prepare_to_store = record_prepare_to_store;
record_ops.to_store_registers = record_store_registers;
record_ops.to_xfer_partial = record_xfer_partial;
record_ops.to_insert_breakpoint = record_insert_breakpoint;
record_ops.to_remove_breakpoint = record_remove_breakpoint;
record_ops.to_can_execute_reverse = record_can_execute_reverse;
+ record_ops.to_has_execution = record_has_execution;
record_ops.to_stratum = record_stratum;
record_ops.to_magic = OPS_MAGIC;
}
@@ -1154,6 +1403,294 @@ cmd_record_start (char *args, int from_t
execute_command ("target record", from_tty);
}
+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
+
+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 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);
+}
+
+/* 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 (current_target.to_stratum != record_stratum)
+ error (_("Process record is not started.\n"));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default corefile name is "gdb_record.PID". */
+ sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid));
+ recfilename = recfilename_buffer;
+ }
+
+ /* Open the dump file. */
+ 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 (cmd_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;
+ 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 */
+ 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)
+ {
+ 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;
+ }
+ }
+
+ /* 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);
+}
+
+/* Load the execution log from a file. */
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+ int recfd;
+ uint32_t magic;
+ struct cleanup *old_cleanups;
+ struct cleanup *old_cleanups2;
+ struct record_entry *rec;
+ int insn_number = 0;
+
+ if (current_target.to_stratum != record_stratum)
+ {
+ cmd_record_start (NULL, from_tty);
+ printf_unfiltered (_("Auto start process record.\n"));
+ }
+
+ if (!args || (args && !*args))
+ error (_("Argument for filename required.\n"));
+
+ /* Open the load file. */
+ recfd = open (args, O_RDONLY | O_BINARY);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for loading execution records: %s"),
+ args, strerror (errno));
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Check the magic code. */
+ record_read_dump (args, recfd, &magic, 4);
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s' is not a valid dump of execution records."), args);
+
+ /* 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'."), args);
+ 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 (args, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.reg.num = tmpu64;
+ /* Get val. */
+ record_read_dump (args, recfd, 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. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ rec->u.mem.addr = tmpu64;
+ /* Get len. */
+ record_read_dump (args, 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 (args, recfd, rec->u.mem.val, rec->u.mem.len);
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ 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."), 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);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+}
+
/* Truncate the record log from the present point
of replay until the end. */
@@ -1204,7 +1741,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 ();
}
}
@@ -1244,6 +1781,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;
@@ -1277,6 +1816,16 @@ _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 records to a file.\n\
+Argument is optional filename. Default filename is 'gdb_record.<process_id>'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ 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);
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]