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 Wed, Aug 5, 2009 at 04:00, Michael Snyder<msnyder@vmware.com> wrote:
> Eli Zaretskii wrote:
>>>
>>> From: Hui Zhu <teawater@gmail.com>
>>> Date: Tue, 4 Aug 2009 10:07:11 +0800
>>> Cc: msnyder@vmware.com, gdb-patches@sourceware.org
>>>
>>>> By the way, does it make sense to make the dump file portable, so that
>>>> another host that supports debugging the same target could then use
>>>> it?
>>>>
>>> Yes, the dump file is portable like core file.
>>
>> Well, maybe I'm missing something important, but isn't the dump file a
>> series of struct's defined as this:
>>
>> ? ?struct record_entry
>> ? ?{
>> ? ? ?struct record_entry *prev;
>> ? ? ?struct record_entry *next;
>> ? ? ?enum record_type type;
>> ? ? ?union
>> ? ? ?{
>> ? ? ? ?/* reg */
>> ? ? ? ?struct record_reg_entry reg;
>> ? ? ? ?/* mem */
>> ? ? ? ?struct record_mem_entry mem;
>> ? ? ?} u;
>> ? ?};
>>
>> ? ?If so, then the dump file uses host's native pointers, so it is not
>> portable to a different host. ?Right?
>
> No. ?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. ? ;-)
>

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.

Thanks,
Hui

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

Attachment: prec-dump.txt
Description: Text document


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