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)




Hui Zhu wrote:
On 2009-08-06, Michael Snyder <msnyder@vmware.com> wrote:

 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.

I think this way is easy to implement.


 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.

For all of this way, I think we have a problem is when we dump the log, we don't know where is the core file. Maybe we can extend the record dump command, to let it point out where is the core file or let it call gcore too.

OK, I've written an extension to gcore.c which exports some functions which we can call from record.c, to save and load core files for snapshots.

Using this, we can then create an extra section in the core
file, and save our record log in there.

That way, we are assured of consistency between the start state
and the record log, because they are both in the same file.

What do you think?

2009-08-11  Hui Zhu  <teawater@gmail.com>
	    Michael Snyder  <msnyder@vmware.com>
	    
	* record.c (RECORD_FILE_MAGIC): New constant.
	(cmd_record_dump): New function.
	(cmd_record_load): New function.
	(bfdcore_read): New function.
	(bfdcore_write): New function.
	(record_exec_entry): New function, abstracted from record_wait.
	(record_wait): Call record_exec_entry.
	(_initialize_record): Add 'record dump' and 'record load' commands.
	(record_arch_list_add_reg): Use xcalloc instead of xmalloc.
	(record_list_release): Finish releasing record list.

	* gcore.c (create_gcore_bfd): New function, abstracted 
	from gcore_command for export.
	(write_gcore_file): New function, abstracted from 
	gcore_command for export.
	(gcore_command): Call helper functions (above).

	(call_target_sbrk): New function, abstracted from 
	derive_heap_segment.
	(derive_heap_segment): Call helper function (above).

	(load_core_segments): New function.
	(load_corefile): New function.

Index: gcore.c
===================================================================
RCS file: /cvs/src/src/gdb/gcore.c,v
retrieving revision 1.34
diff -u -p -r1.34 gcore.c
--- gcore.c	2 Jul 2009 17:21:06 -0000	1.34
+++ gcore.c	11 Aug 2009 18:44:27 -0000
@@ -25,6 +25,8 @@
 #include "gdbcore.h"
 #include "objfiles.h"
 #include "symfile.h"
+#include "arch-utils.h"
+#include "completer.h"
 
 #include "cli/cli-decode.h"
 
@@ -40,45 +42,27 @@ static enum bfd_architecture default_gco
 static unsigned long default_gcore_mach (void);
 static int gcore_memory_sections (bfd *);
 
-/* Generate a core file from the inferior process.  */
+/* create_gcore_bfd -- helper for gcore_command (exported).  */
 
-static void
-gcore_command (char *args, int from_tty)
+extern bfd *
+create_gcore_bfd (char *filename)
 {
-  struct cleanup *old_chain;
-  char *corefilename, corefilename_buffer[40];
-  asection *note_sec = NULL;
-  bfd *obfd;
-  void *note_data = NULL;
-  int note_size = 0;
-
-  /* No use generating a corefile without a target process.  */
-  if (!target_has_execution)
-    noprocess ();
-
-  if (args && *args)
-    corefilename = args;
-  else
-    {
-      /* Default corefile name is "core.PID".  */
-      sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid));
-      corefilename = corefilename_buffer;
-    }
-
-  if (info_verbose)
-    fprintf_filtered (gdb_stdout,
-		      "Opening corefile '%s' for output.\n", corefilename);
-
-  /* Open the output file.  */
-  obfd = bfd_openw (corefilename, default_gcore_target ());
+  bfd *obfd = bfd_openw (filename, default_gcore_target ());
   if (!obfd)
-    error (_("Failed to open '%s' for output."), corefilename);
-
-  /* Need a cleanup that will close the file (FIXME: delete it?).  */
-  old_chain = make_cleanup_bfd_close (obfd);
-
+    error (_("Failed to open '%s' for output."), filename);
   bfd_set_format (obfd, bfd_core);
   bfd_set_arch_mach (obfd, default_gcore_arch (), default_gcore_mach ());
+  return obfd;
+}
+
+/* write_gcore_file -- helper for gcore_command (exported).  */
+
+extern void
+write_gcore_file (bfd *obfd)
+{
+  void *note_data = NULL;
+  int note_size = 0;
+  asection *note_sec = NULL;
 
   /* An external target method must build the notes section.  */
   note_data = target_make_corefile_notes (obfd, &note_size);
@@ -107,8 +91,46 @@ gcore_command (char *args, int from_tty)
   if (note_data != NULL && note_size != 0)
     {
       if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size))
-	warning (_("writing note section (%s)"), bfd_errmsg (bfd_get_error ()));
+	warning (_("writing note section (%s)"), 
+		 bfd_errmsg (bfd_get_error ()));
     }
+}
+
+/* gcore_command -- implements the 'gcore' command.
+   Generate a core file from the inferior process.  */
+
+static void
+gcore_command (char *args, int from_tty)
+{
+  struct cleanup *old_chain;
+  char *corefilename, corefilename_buffer[40];
+  bfd *obfd;
+
+  /* No use generating a corefile without a target process.  */
+  if (!target_has_execution)
+    noprocess ();
+
+  if (args && *args)
+    corefilename = args;
+  else
+    {
+      /* Default corefile name is "core.PID".  */
+      sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid));
+      corefilename = corefilename_buffer;
+    }
+
+  if (info_verbose)
+    fprintf_filtered (gdb_stdout,
+		      "Opening corefile '%s' for output.\n", corefilename);
+
+  /* Open the output file.  */
+  obfd = create_gcore_bfd (corefilename);
+
+  /* Need a cleanup that will close the file (FIXME: delete it?).  */
+  old_chain = make_cleanup_bfd_close (obfd);
+
+  /* Call worker function.  */
+  write_gcore_file (obfd);
 
   /* Succeeded.  */
   fprintf_filtered (gdb_stdout, "Saved corefile %s\n", corefilename);
@@ -212,6 +234,47 @@ derive_stack_segment (bfd_vma *bottom, b
   return 1;
 }
 
+static bfd_vma
+call_target_sbrk (int sbrk_arg)
+{
+  struct objfile *sbrk_objf;
+  struct gdbarch *gdbarch;
+  bfd_vma top_of_heap;
+  struct value *target_sbrk_arg;
+  struct value *sbrk_fn, *ret;
+  bfd_vma tmp;
+
+  if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL)
+    {
+      sbrk_fn = find_function_in_inferior ("sbrk", &sbrk_objf);
+      if (sbrk_fn == NULL)
+	return (bfd_vma) 0;
+    }
+  else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL)
+    {
+      sbrk_fn = find_function_in_inferior ("_sbrk", &sbrk_objf);
+      if (sbrk_fn == NULL)
+	return (bfd_vma) 0;
+    }
+  else
+    return (bfd_vma) 0;
+
+  gdbarch = get_objfile_arch (sbrk_objf);
+  target_sbrk_arg = value_from_longest (builtin_type (gdbarch)->builtin_int, 
+					sbrk_arg);
+  gdb_assert (target_sbrk_arg);
+  ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg);
+  if (ret == NULL)
+    return (bfd_vma) 0;
+
+  tmp = value_as_long (ret);
+  if ((LONGEST) tmp <= 0 || (LONGEST) tmp == 0xffffffff)
+    return (bfd_vma) 0;
+
+  top_of_heap = tmp;
+  return top_of_heap;
+}
+
 /* Derive a reasonable heap segment for ABFD by looking at sbrk and
    the static data sections.  Store its limits in *BOTTOM and *TOP.
    Return non-zero if successful.  */
@@ -219,12 +282,10 @@ derive_stack_segment (bfd_vma *bottom, b
 static int
 derive_heap_segment (bfd *abfd, bfd_vma *bottom, bfd_vma *top)
 {
-  struct objfile *sbrk_objf;
   struct gdbarch *gdbarch;
   bfd_vma top_of_data_memory = 0;
   bfd_vma top_of_heap = 0;
   bfd_size_type sec_size;
-  struct value *zero, *sbrk;
   bfd_vma sec_vaddr;
   asection *sec;
 
@@ -259,29 +320,9 @@ derive_heap_segment (bfd *abfd, bfd_vma 
 	}
     }
 
-  /* Now get the top-of-heap by calling sbrk in the inferior.  */
-  if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL)
-    {
-      sbrk = find_function_in_inferior ("sbrk", &sbrk_objf);
-      if (sbrk == NULL)
-	return 0;
-    }
-  else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL)
-    {
-      sbrk = find_function_in_inferior ("_sbrk", &sbrk_objf);
-      if (sbrk == NULL)
-	return 0;
-    }
-  else
-    return 0;
-
-  gdbarch = get_objfile_arch (sbrk_objf);
-  zero = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-  gdb_assert (zero);
-  sbrk = call_function_by_hand (sbrk, 1, &zero);
-  if (sbrk == NULL)
+  top_of_heap = call_target_sbrk (0);
+  if (top_of_heap == (bfd_vma) 0)
     return 0;
-  top_of_heap = value_as_long (sbrk);
 
   /* Return results.  */
   if (top_of_heap > top_of_data_memory)
@@ -299,13 +340,15 @@ static void
 make_output_phdrs (bfd *obfd, asection *osec, void *ignored)
 {
   int p_flags = 0;
-  int p_type;
+  int p_type = 0;
 
   /* FIXME: these constants may only be applicable for ELF.  */
   if (strncmp (bfd_section_name (obfd, osec), "load", 4) == 0)
     p_type = PT_LOAD;
-  else
+  else if (strncmp (bfd_section_name (obfd, osec), "note", 4) == 0)
     p_type = PT_NOTE;
+  else
+    p_type = PT_NULL;
 
   p_flags |= PF_R;	/* Segment is readable.  */
   if (!(bfd_get_section_flags (obfd, osec) & SEC_READONLY))
@@ -516,6 +559,144 @@ gcore_memory_sections (bfd *obfd)
   return 1;
 }
 
+struct load_core_args_params {
+  int from_tty;
+  bfd_vma top_of_heap;
+};
+
+static void
+load_core_segments (bfd *abfd, asection *asect, void *arg)
+{
+  struct load_core_args_params *params = arg;
+  struct cleanup *old_chain;
+  char *memhunk;
+  int ret;
+
+  if ((bfd_section_size (abfd, asect) > 0) &&
+      (bfd_get_section_flags (abfd, asect) & SEC_LOAD) &&
+      !(bfd_get_section_flags (abfd, asect) & SEC_READONLY))
+    {
+      if (info_verbose && params->from_tty)
+	{
+	  printf_filtered (_("Load core section %s"), 
+			   bfd_section_name (abfd, asect));
+	  printf_filtered (_(", vma 0x%08lx to 0x%08lx"), 
+			   (unsigned long) bfd_section_vma (abfd, asect),
+			   (unsigned long) bfd_section_vma (abfd, asect) +
+			   (int) bfd_section_size (abfd, asect));
+	  printf_filtered (_(", size = %d"), 
+			   (int) bfd_section_size (abfd, asect));
+	  printf_filtered (_(".\n"));
+	}
+      /* Fixme cleanup? */
+      memhunk = xmalloc (bfd_section_size (abfd, asect));
+      bfd_get_section_contents (abfd, asect, memhunk, 0, 
+				bfd_section_size (abfd, asect));
+      if ((ret = target_write_memory (bfd_section_vma (abfd, asect), 
+				      memhunk, 
+				      bfd_section_size (abfd, asect))) != 0)
+	{
+	  print_sys_errmsg ("load_core_segments", ret);
+	  if ((LONGEST) params->top_of_heap < 
+	      (LONGEST) bfd_section_vma (abfd, asect) + 
+	      (LONGEST) bfd_section_size (abfd, asect))
+	    {
+	      int increment = bfd_section_vma (abfd, asect) +
+		bfd_section_size (abfd, asect) - params->top_of_heap;
+
+	      params->top_of_heap = call_target_sbrk (increment);
+	      if (params->top_of_heap == 0)
+		error ("sbrk failed, TOH = 0x%08lx", params->top_of_heap);
+	      else
+		printf_filtered ("Increase TOH to 0x%08lx and retry.\n",
+				 (unsigned long) params->top_of_heap);
+	      if (target_write_memory (bfd_section_vma (abfd, asect), 
+				       memhunk, 
+				       bfd_section_size (abfd, asect)) != 0)
+		{
+		  error ("Nope, still failed.");
+		}
+	    }
+	}
+      xfree (memhunk);
+    }
+}
+
+#include <fcntl.h>
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#include "regcache.h"
+#include "regset.h"
+
+extern bfd *
+load_corefile (char *filename, int from_tty)
+{
+  struct load_core_args_params params;
+  struct bfd_section *regsect;
+  const struct regset *regset;
+  struct regcache *regcache;
+  struct cleanup *old_chain;
+  struct gdbarch *gdbarch;
+  char *scratch_path;
+  int scratch_chan;
+  char *contents;
+  bfd *core_bfd;
+  int size;
+
+  scratch_chan = openp (getenv ("PATH"), OPF_TRY_CWD_FIRST, filename, 
+			O_BINARY | O_RDONLY | O_LARGEFILE, &scratch_path);
+  if (scratch_chan < 0)
+    perror_with_name (filename);
+
+  core_bfd = bfd_fdopenr (scratch_path, gnutarget, scratch_chan);
+  old_chain = make_cleanup_bfd_close (core_bfd);
+  if (!core_bfd)
+    perror_with_name (scratch_path);
+
+  if (!bfd_check_format (core_bfd, bfd_core))
+    error (_("\"%s\" is not a core file: %s"), 
+	   filename, bfd_errmsg (bfd_get_error ()));
+
+  params.from_tty = from_tty;
+  params.top_of_heap = call_target_sbrk (0);
+  if (params.top_of_heap == 0)
+    error (_("Couldn't get sbrk."));
+
+  bfd_map_over_sections (core_bfd, load_core_segments, (void *) &params);
+  /* Now need to get/set registers.  */
+  regsect = bfd_get_section_by_name (core_bfd, ".reg");
+
+  if (!regsect)
+    error (_("Couldn't find .reg section."));
+
+  size = bfd_section_size (core_bfd, regsect);
+  contents = alloca (size);
+  bfd_get_section_contents (core_bfd, regsect, contents, 0, size);
+
+  /* See FIXME kettenis/20031023 comment in corelow.c */
+  gdbarch = gdbarch_from_bfd (core_bfd);
+
+  if (gdbarch && gdbarch_regset_from_core_section_p (gdbarch))
+    {
+      regset = gdbarch_regset_from_core_section (gdbarch, ".reg", size);
+      if (!regset)
+	error (_("Failed to allocate regset."));
+
+      registers_changed ();
+      regcache = get_current_regcache ();
+      regset->supply_regset (regset, regcache, -1, contents, size);
+      reinit_frame_cache ();
+      target_store_registers (regcache, -1);
+    }
+  else
+    error (_("Failed to get regset from core section"));
+
+  discard_cleanups (old_chain);
+  return core_bfd;
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_gcore;
 
Index: record.c
===================================================================
RCS file: /cvs/src/src/gdb/record.c,v
retrieving revision 1.11
diff -u -p -r1.11 record.c
--- record.c	8 Aug 2009 01:57:44 -0000	1.11
+++ record.c	11 Aug 2009 18:44:27 -0000
@@ -23,15 +23,20 @@
 #include "gdbthread.h"
 #include "event-top.h"
 #include "exceptions.h"
+#include "completer.h"
 #include "record.h"
 
+#include <byteswap.h>
 #include <signal.h>
+#include <netinet/in.h>
 
 #define DEFAULT_RECORD_INSN_MAX_NUM	200000
 
 #define RECORD_IS_REPLAY \
      (record_list->next || execution_direction == EXEC_REVERSE)
 
+#define RECORD_FILE_MAGIC	htonl(0x20090726) /* Host to network order */
+
 /* These are the core struct of record function.
 
    An record_entry is a record of the value change of a register
@@ -146,6 +151,11 @@ record_list_release (struct record_entry
 
   if (rec != &record_first)
     xfree (rec);
+
+  record_list = &record_first;
+  record_arch_list_tail = NULL;
+  record_arch_list_tail = NULL;
+  record_insn_num = 0;
 }
 
 static void
@@ -241,7 +251,7 @@ record_arch_list_add_reg (struct regcach
 			num);
 
   rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
-  rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+  rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE);
   rec->prev = NULL;
   rec->next = NULL;
   rec->type = record_reg;
@@ -416,6 +426,95 @@ 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);
+
+        memset (reg, 0, sizeof (reg));
+        regcache_cooked_read (regcache, entry->u.reg.num, reg);
+        regcache_cooked_write (regcache, entry->u.reg.num, entry->u.reg.val);
+        memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE);
+      }
+      break;
+
+    case record_mem: /* mem */
+      {
+	/* Nothing to do if the entry is flagged not_accessible.  */
+        if (!record_list->u.mem.mem_entry_not_accessible)
+          {
+            gdb_byte *mem = alloca (entry->u.mem.len);
+
+            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)
+                  {
+		    /* Read failed -- 
+		       flag entry as not_accessible.  */
+                    record_list->u.mem.mem_entry_not_accessible = 1;
+                    if (record_debug)
+                      warning (_("Process record: error reading memory at "
+                                 "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)
+                      {
+			/* Write failed -- 
+			   flag entry as not_accessible.  */
+                        record_list->u.mem.mem_entry_not_accessible = 1;
+                        if (record_debug)
+                          warning (_("Process record: error writing memory at "
+                                     "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);
+                  }
+		else
+		  memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+              }
+          }
+      }
+      break;
+    }
+}
+
 static void
 record_open (char *name, int from_tty)
 {
@@ -712,76 +811,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);
-
-		  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
+          record_exec_entry (regcache, gdbarch, record_list);
+
+	  if (record_list->type == record_end)
 	    {
 	      if (record_debug > 1)
 		fprintf_unfiltered (gdb_stdlog,
@@ -999,6 +1031,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);
 }
@@ -1153,6 +1186,453 @@ cmd_record_start (char *args, int from_t
   execute_command ("target record", from_tty);
 }
 
+/* Record log save-file format
+   Version 1
+
+     Header:
+       4 bytes: magic number RECORD_FILE_MAGIC.
+                NOTE: be sure to change whenever this file format changes!
+
+     Records: 
+      record_end:
+       1 byte:  record type (record_end)
+      record_reg:
+       1 byte:  record type (record_reg)
+       8 bytes: register id (network byte order)
+      16 bytes: register value (target byte order)
+      record_mem:
+       1 byte:  record type (record_mem)
+       8 bytes: memory address (network byte order)
+       8 bytes: memory length (network byte order)
+       n bytes: memory value (n == memory length, target byte order)
+
+   Version 2 (proposed)
+
+     Header:
+       4 bytes: magic number RECORD_FILE_MAGIC.
+                NOTE: be sure to change whenever this file format changes!
+       n bytes: architecture...
+       4 bytes: size of register snapshot
+       n bytes: register snapshot
+       4 bytes: number of section crcs
+       n bytes: section names with crcs
+       
+     Records:
+       See version 1.
+ */
+
+/* Dump the execution log to a file.  */
+
+#include "elf-bfd.h"
+
+static int
+bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
+{
+  int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len);
+
+  if (ret)
+    *offset += len;
+  return ret;
+}
+
+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;
+  extern void write_gcore_file (bfd *);
+  extern bfd *create_gcore_bfd (char *);
+  bfd *obfd;
+  int dump_size = 0;
+  asection *osec = NULL;
+  struct record_entry *p;
+  int bfd_offset = 0;
+
+
+  if (current_target.to_stratum != record_stratum)
+    error (_("Process record is not started.\n"));
+
+  if (args && *args)
+    recfilename = args;
+  else
+    {
+      /* Default recfile name is "rec.PID".  */
+      sprintf (recfilename_buffer, "rec.%d", PIDGET (inferior_ptid));
+      recfilename = recfilename_buffer;
+    }
+
+  /* Open the dump file.  */
+  if (record_debug)
+    fprintf_filtered (gdb_stdlog, 
+		      _("Saving recording to file '%s'\n"), 
+		      recfilename);
+
+  /* Open the output file.  */
+  obfd = create_gcore_bfd (recfilename);
+
+  /* Need a cleanup that will close the file (FIXME: delete it?).  */
+  old_cleanups = make_cleanup_bfd_close (obfd);
+
+  /* Save the current record entry to "cur_record_list".  */
+  cur_record_list = record_list;
+
+  /* Get the values of regcache and gdbarch.  */
+  regcache = get_current_regcache ();
+  gdbarch = get_regcache_arch (regcache);
+
+  /* Disable the GDB operation record.  */
+  set_cleanups = record_gdb_operation_disable_set ();
+
+  /* Reverse execute to the begin of record list.  */
+  for (; record_list && record_list != &record_first; 
+       record_list = record_list->prev)
+    record_exec_entry (regcache, gdbarch, record_list);
+
+  /* Compute the size needed for the extra bfd section.  */
+  dump_size = 4;	/* magic cookie */
+  for (p = &record_first; p; p = p->next)
+    switch (p->type)
+      {
+      case record_end:
+	dump_size += 1;
+	break;
+      case record_reg:
+	dump_size += 1 + 8 + MAX_REGISTER_SIZE;
+	break;
+      case record_mem:
+	dump_size += 1 + 8 + 8 + p->u.mem.len;
+	break;
+      }
+
+  /* Make the new bfd section.  */
+  osec = bfd_make_section_anyway (obfd, "precord");
+  bfd_set_section_size (obfd, osec, dump_size);
+  bfd_set_section_vma (obfd, osec, 0);
+  bfd_section_lma (obfd, osec) = 0;
+  bfd_set_section_flags (obfd, osec, SEC_ALLOC | SEC_HAS_CONTENTS);
+
+  /* Save corefile state.  */
+  write_gcore_file (obfd);
+
+  /* Write out the record log (modified Hui method).  */
+  /* Write the magic code.  */
+  magic = RECORD_FILE_MAGIC;
+  if (record_debug)
+    fprintf_filtered (gdb_stdlog, _("\
+  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+		      magic);
+  if (!bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset))
+    error (_("Failed to write 'magic' to %s (%s)"), 
+	   recfilename, bfd_errmsg (bfd_get_error ()));
+
+  /* Dump the entries into the new bfd section.  */
+  for (p = &record_first; p; p = p->next)
+    {
+      uint8_t tmpu8;
+      uint64_t tmpu64;
+
+      tmpu8 = p->type;
+      if (!bfdcore_write (obfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset))
+	error (_("Failed to write 'type' to %s (%s)"), 
+	       recfilename, bfd_errmsg (bfd_get_error ()));
+
+      switch (p->type)
+	{
+	case record_reg: /* reg */
+	  tmpu64 = p->u.reg.num;
+	  if (BYTE_ORDER == LITTLE_ENDIAN)
+	    tmpu64 = bswap_64 (tmpu64);
+
+	  if (record_debug)
+	    fprintf_filtered (gdb_stdlog, _("\
+  Writing register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
+			      p->u.reg.num,
+			      *(ULONGEST *) p->u.reg.val, 
+			      MAX_REGISTER_SIZE);
+	  /* FIXME: register num does not need 8 bytes.  */
+	  if (!bfdcore_write (obfd, osec, &tmpu64, 
+			      sizeof (tmpu64), &bfd_offset))
+	    error (_("Failed to write regnum to %s (%s)"), 
+		   recfilename, bfd_errmsg (bfd_get_error ()));
+
+	  /* FIXME: add a len field, and write the smaller value.  */
+	  if (!bfdcore_write (obfd, osec, p->u.reg.val,
+					MAX_REGISTER_SIZE, &bfd_offset))
+	    error (_("Failed to write regval to %s (%s)"), 
+		   recfilename, bfd_errmsg (bfd_get_error ()));
+	  break;
+	case record_mem: /* mem */
+	  tmpu64 = p->u.mem.addr;
+	  if (BYTE_ORDER == LITTLE_ENDIAN)
+	    tmpu64 = bswap_64 (tmpu64);
+
+	  if (record_debug)
+	    fprintf_filtered (gdb_stdlog, _("\
+  Writing memory 0x%08x (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+			      (unsigned int) p->u.mem.addr,
+			      p->u.mem.len);
+	  if (!bfdcore_write (obfd, osec, &tmpu64, sizeof (tmpu64), &bfd_offset))
+	    error (_("Failed to write memaddr to %s (%s)"),
+		   recfilename, bfd_errmsg (bfd_get_error ()));
+
+	  tmpu64 = p->u.mem.len;
+	  if (BYTE_ORDER == LITTLE_ENDIAN)
+	    tmpu64 = bswap_64 (tmpu64);
+
+	  /* FIXME: len does not need 8 bytes.  */
+	  if (!bfdcore_write (obfd, osec, &tmpu64, sizeof (tmpu64), &bfd_offset))
+	    error (_("Failed to write memlen to %s (%s)"), 
+		   recfilename, bfd_errmsg (bfd_get_error ()));
+
+	  if (!bfdcore_write (obfd, osec, p->u.mem.val,
+			      p->u.mem.len, &bfd_offset))
+	    error (_("Failed to write memval to %s (%s)"),
+		   recfilename, bfd_errmsg (bfd_get_error ()));
+	  break;
+	case record_end:
+	  /* FIXME: record the contents of record_end rec.  */
+	  if (record_debug)
+	    fprintf_filtered (gdb_stdlog, _("\
+  Writing record_end (1 byte)\n"));
+	  break;
+	}
+    }
+
+  /* Now forward-execute back to the saved entry.  */
+  for (record_list = &record_first; 
+       record_list && record_list != cur_record_list; 
+       record_list = record_list->next)
+    record_exec_entry (regcache, gdbarch, record_list);
+
+  /* Clean-ups will close the output file and free malloc memory.  */
+  do_cleanups (old_cleanups);
+
+  /* Succeeded.  */
+  fprintf_filtered (gdb_stdout, "Saved recfile %s.\n", recfilename);
+}
+
+static int
+bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
+{
+  int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
+
+  if (ret)
+    *offset += len;
+  return ret;
+}
+
+/* Load the execution log from a file.  */
+
+#include "gdbcore.h"
+#include <ctype.h>
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+  extern void nullify_last_target_wait_ptid (void);
+  int recfd;
+  uint32_t magic;
+  struct cleanup *old_cleanups;
+  struct cleanup *old_cleanups2;
+  struct record_entry *rec;
+  int insn_number = 0;
+  bfd *core_bfd;
+  asection *osec;
+  extern bfd *load_corefile (char *, int);
+
+  if (!args || (args && !*args))
+    error (_("Argument for filename required.\n"));
+
+  /* Open the load file.  */
+  if (record_debug)
+    fprintf_filtered (gdb_stdlog, 
+		      _("Restoring recording from file '%s'\n"), args);
+
+  /* Restore corefile regs and mem sections.  */
+  core_bfd = load_corefile (args, from_tty);
+  old_cleanups = make_cleanup_bfd_close (core_bfd);
+
+  /* Now need to find our special note section.  */
+  osec = bfd_get_section_by_name (core_bfd, "null0");
+  printf_filtered ("Find precord section %s.\n",
+		   osec ? "succeeded" : "failed");
+
+  if (osec)
+    {
+      int i, len;
+      int bfd_offset = 0;
+
+      if (record_debug)
+	fprintf_filtered (gdb_stdlog, "osec name = '%s'\n",
+			  bfd_section_name (core_bfd, osec));
+      len = (int) bfd_section_size (core_bfd, osec);
+      printf_filtered ("osec size = %d\n", len);
+
+      /* Check the magic code.  */
+      if (!bfdcore_read (core_bfd, osec, &magic, 
+			 sizeof (magic), &bfd_offset))
+	error (_("Failed to read 'magic' from %s (%s)"),
+	       args, bfd_errmsg (bfd_get_error ()));
+
+      if (magic != RECORD_FILE_MAGIC)
+	error (_("'%s', version mis-match / file format error."), 
+	       args);
+
+      if (record_debug)
+	fprintf_filtered (gdb_stdlog, _("\
+  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+			  magic);
+      if (current_target.to_stratum != record_stratum)
+	{
+	  /* FIXME need cleanup!  We might error out.  */
+	  cmd_record_start (NULL, from_tty);
+	  printf_unfiltered (_("Auto start process record.\n"));
+	}
+
+      /* Free any existing record log, and load the entries in
+	 core_bfd to the new record log.  */
+      record_list_release (record_arch_list_tail);
+      old_cleanups2 = make_cleanup (record_message_cleanups, 0);
+
+      while (1)
+	{
+	  uint8_t tmpu8;
+	  uint64_t tmpu64;
+
+	  /* FIXME: Check offset for end-of-section.  */
+	  if (!bfdcore_read (core_bfd, osec, &tmpu8, 
+			     sizeof (tmpu8), &bfd_offset))
+	    break;
+
+	  switch (tmpu8)
+	    {
+	    case record_reg: /* reg */
+	      /* FIXME: abstract out into an 'insert' function.  */
+	      rec = (struct record_entry *) 
+		xmalloc (sizeof (struct record_entry));
+	      rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE);
+	      rec->prev = NULL;
+	      rec->next = NULL;
+	      rec->type = record_reg;
+	      /* Get num.  */
+	      /* FIXME: register num does not need 8 bytes.  */
+	      if (!bfdcore_read (core_bfd, osec, &tmpu64, 
+				 sizeof (tmpu64), &bfd_offset))
+		error (_("Failed to read regnum from %s (%s)"),
+		       args, bfd_errmsg (bfd_get_error ()));
+
+	      if (BYTE_ORDER == LITTLE_ENDIAN)
+		tmpu64 = bswap_64 (tmpu64);
+	      rec->u.reg.num = tmpu64;
+
+	      /* Get val.  */
+	      if (!bfdcore_read (core_bfd, osec, rec->u.reg.val,
+				 MAX_REGISTER_SIZE, &bfd_offset))
+		error (_("Failed to read regval from  %s (%s)"),
+		       args, bfd_errmsg (bfd_get_error ()));
+
+	      if (record_debug)
+		fprintf_filtered (gdb_stdlog, _("\
+  Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
+				  rec->u.reg.num, 
+				  *(ULONGEST *) rec->u.reg.val, 
+				  MAX_REGISTER_SIZE);
+	      record_arch_list_add (rec);
+	      break;
+
+	    case record_mem: /* mem */
+	      rec = (struct record_entry *) 
+		xmalloc (sizeof (struct record_entry));
+	      rec->prev = NULL;
+	      rec->next = NULL;
+	      rec->type = record_mem;
+	      /* Get addr.  */
+	      if (!bfdcore_read (core_bfd, osec, &tmpu64, 
+				 sizeof (tmpu64), &bfd_offset))
+		error (_("Failed to read memaddr from %s (%s)"),
+		       args, bfd_errmsg (bfd_get_error ()));
+	      if (BYTE_ORDER == LITTLE_ENDIAN)
+		tmpu64 = bswap_64 (tmpu64);
+	      rec->u.mem.addr = tmpu64;
+
+	      /* Get len.  */
+	      /* FIXME: len does not need 8 bytes.  */
+	      if (!bfdcore_read (core_bfd, osec, &tmpu64, 
+				 sizeof (tmpu64), &bfd_offset))
+		error (_("Failed to read memlen from %s (%s)"),
+		       args, bfd_errmsg (bfd_get_error ()));
+	      if (BYTE_ORDER == LITTLE_ENDIAN)
+		tmpu64 = bswap_64 (tmpu64);
+	      rec->u.mem.len = tmpu64;
+
+	      rec->u.mem.mem_entry_not_accessible = 0;
+	      rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len);
+	      /* Get val.  */
+	      if (!bfdcore_read (core_bfd, osec, rec->u.mem.val,
+				 rec->u.mem.len, &bfd_offset))
+		error (_("Failed to read memval from %s (%s)"),
+		       args, bfd_errmsg (bfd_get_error ()));
+	      if (record_debug)
+		fprintf_filtered (gdb_stdlog, _("\
+  Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"),
+				  (unsigned int) rec->u.mem.addr,
+				  rec->u.mem.len);
+	      record_arch_list_add (rec);
+	      break;
+
+	    case record_end: /* end */
+	      /* FIXME: restore the contents of record_end rec.  */
+	      rec = (struct record_entry *) 
+		xmalloc (sizeof (struct record_entry));
+	      rec->prev = NULL;
+	      rec->next = NULL;
+	      rec->type = record_end;
+	      if (record_debug)
+		fprintf_filtered (gdb_stdlog, _("\
+  Reading record_end (one byte)\n"));
+	      record_arch_list_add (rec);
+	      insn_number ++;
+	      break;
+
+	    default:
+	      error (_("Format of '%s' is not right."), args);
+	      break;
+	    }
+	}
+    }
+
+  discard_cleanups (old_cleanups2);
+
+  /* Add record_arch_list_head to the end of record list.  (??? FIXME)*/
+  for (rec = record_list; rec->next; rec = rec->next);
+  rec->next = record_arch_list_head;
+  record_arch_list_head->prev = rec;
+
+  /* Update record_insn_num and record_insn_max_num.  */
+  record_insn_num = insn_number;
+  if (record_insn_num > record_insn_max_num)
+    {
+      record_insn_max_num = record_insn_num;
+      warning (_("Auto increase record/replay buffer limit to %d."),
+	       record_insn_max_num);
+    }
+
+  do_cleanups (old_cleanups);
+
+  /* Succeeded.  */
+  fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+  registers_changed ();
+  reinit_frame_cache ();
+  nullify_last_target_wait_ptid ();
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
 /* Truncate the record log from the present point
    of replay until the end.  */
 
@@ -1243,6 +1723,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;
@@ -1276,6 +1758,15 @@ _initialize_record (void)
 		  "info record ", 0, &infolist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
 
+  c = add_cmd ("dump", class_obscure, cmd_record_dump,
+	       _("Dump the execution log to a file.\n\
+Argument is optional filename.  Default filename is 'rec.<process_id>'."),
+               &record_cmdlist);
+  set_cmd_completer (c, filename_completer);
+  c = add_cmd ("load", class_obscure, cmd_record_load,
+	       _("Load the execution log from a file.  Argument is filename."),
+               &record_cmdlist);
+  set_cmd_completer (c, filename_completer);
 
   add_cmd ("delete", class_obscure, cmd_record_delete,
 	   _("Delete the rest of execution log and start recording it anew."),
@@ -1290,15 +1781,15 @@ _initialize_record (void)
 
   /* Record instructions number limit command.  */
   add_setshow_boolean_cmd ("stop-at-limit", no_class,
-			    &record_stop_at_limit, _("\
+			   &record_stop_at_limit, _("\
 Set whether record/replay stops when record/replay buffer becomes full."), _("\
 Show whether record/replay stops when record/replay buffer becomes full."), _("\
 Default is ON.\n\
 When ON, if the record/replay buffer becomes full, ask user what to do.\n\
 When OFF, if the record/replay buffer becomes full,\n\
 delete the oldest recorded instruction to make room for each new one."),
-			    NULL, NULL,
-                            &set_record_cmdlist, &show_record_cmdlist);
+			   NULL, NULL,
+			   &set_record_cmdlist, &show_record_cmdlist);
   add_setshow_zinteger_cmd ("insn-number-max", no_class,
 			    &record_insn_max_num,
 			    _("Set record/replay buffer limit."),

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