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


On Sun, Aug 2, 2009 at 13:58, Hui Zhu<teawater@gmail.com> wrote:
> OK. ?I will post new patches for them, memory and dump.
>
> Thanks,
> Hui
>
> On Sun, Aug 2, 2009 at 11:17, Michael Snyder<msnyder@vmware.com> wrote:
>> Michael Snyder wrote:
>>
>>> 3) I don't really understand how core files fit into this,
>>> but I'd love to discuss that idea in a separate patch thread.
>>
>> Oh, sorry -- I see how they're related now.
>> Very clever, actually.
>>
>> I'd still like to see them submitted separately, though,
>> because:
>> 1) the dump/restore part of the patch is cleaner and
>> closer to being acceptable, and can really be used on
>> its own, while
>> 2) the corefile part of the patch is kind of messy and
>> prototype-ish, and I think needs much more discussion and
>> cleaning up before it will be ready.
>>
>

Hi Eli and Michael,

I make a new patch according to your mail.  It depend on patch in
http://sourceware.org/ml/gdb-patches/2009-08/msg00015.html

Please help me review it.

Thanks,
Hui


2009-08-03  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): Add support for target core.
	(record_open): Add support for target core.
	(record_close): Add support for target core.
	(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 |  587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 563 insertions(+), 24 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.

@@ -76,9 +84,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;
@@ -101,6 +123,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 *,
@@ -117,6 +147,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)
@@ -167,7 +200,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;
@@ -338,30 +371,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;
@@ -384,7 +417,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++;

@@ -453,7 +486,8 @@ record_exec_entry (struct regcache *regc

             if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
               {
-                if (execution_direction == EXEC_REVERSE)
+                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)
@@ -473,7 +507,8 @@ record_exec_entry (struct regcache *regc
                 if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
                                          entry->u.mem.len))
                   {
-                    if (execution_direction == EXEC_REVERSE)
+                    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)
@@ -505,8 +540,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 "
@@ -535,6 +575,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)
@@ -549,6 +591,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;
@@ -563,19 +610,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);

@@ -588,10 +667,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;
@@ -915,7 +1010,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.  */
@@ -959,15 +1056,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)
@@ -1014,6 +1154,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);
 }
@@ -1029,7 +1170,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)
 	{
@@ -1073,11 +1214,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);
@@ -1127,6 +1348,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)
 {
@@ -1143,11 +1373,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;
 }
@@ -1168,6 +1401,300 @@ 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);
+#endif
+              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);
+#endif
+                  record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+                  tmpu64 = record_list->u.mem.len;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+                  tmpu64 = bswap_64 (tmpu64);
+#endif
+                  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);
+#endif
+          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);
+#endif
+          rec->u.mem.addr = tmpu64;
+          /* Get len.  */
+          record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+          tmpu64 = bswap_64 (tmpu64);
+#endif
+          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.  */

@@ -1218,7 +1745,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 ();
     }
 }

@@ -1258,6 +1785,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;
@@ -1291,6 +1820,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."),


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

	* gdb.texinfo (Process Record and Replay): Document the
	"record dump" and "record dump" commands.

---
 doc/gdb.texinfo |   15 +++++++++++++++
 1 file changed, 15 insertions(+)

--- a/doc/gdb.texinfo
+++ b/doc/gdb.texinfo
@@ -5190,6 +5190,21 @@ When record target runs in replay mode (
 subsequent execution log and begin to record a new execution log starting
 from the current address.  This means you will abandon the previously
 recorded ``future'' and begin recording a new ``future''.
+
+@kindex record dump
+@kindex rec dump
+@item record dump [@var{file}]
+@itemx rec dump [@var{file}]
+Dump the execution records of the inferior process to a file.  The optional
+argument @var{file} specifies the file name where to put the record dump.
+If not specified, the file name defaults to @file{gdb_record.@var{pid}}, where
+@var{pid} is is the PID of the inferior process.
+
+@kindex record load
+@kindex rec load
+@item record load @var{file}
+@itemx rec dump @var{file}
+Load previously-dumped execution records from @var{file}.
 @end table

Attachment: prec-dump.txt
Description: Text document

Attachment: prec-dump-doc.txt
Description: Text document


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