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)


Tom Tromey wrote:
"Michael" == Michael Snyder <msnyder@vmware.com> writes:

A few little nits in this patch. Most of these appear more than once.


Michael> +extern bfd *
Michael> +create_gcore_bfd (char *filename)
Michael>  {

Don't use "extern" on definitions.
Michael> +static bfd_vma
Michael> +call_target_sbrk (int sbrk_arg)


New functions should have an explanatory comment.

Michael> +#include <fcntl.h>
Michael> +#ifndef O_BINARY
Michael> +#define O_BINARY 0
Michael> +#endif
Michael> +
Michael> +#include "regcache.h"
Michael> +#include "regset.h"

I think adding #includes in the middle of a file is ugly.

Also, I think the O_BINARY define isn't needed -- it looks to me that
defs.h does this.

Tom, thanks for the review.
Added gcore.h to export those externs, and moved one decl into inferior.h (always meant to do that for linux-fork).


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.

	* gcore.h: New file.
	* inferior.h: Export nullify_last_target_wait_ptid from infrun.c
	for use by record.c and linux-fork.c.
	* linux-fork.c (fork_load_infrun_state): Delete extern decl.

Index: gcore.h
===================================================================
RCS file: gcore.h
diff -N gcore.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gcore.h	12 Aug 2009 22:28:43 -0000
@@ -0,0 +1,23 @@
+/* Support for reading/writing gcore files.
+
+   Copyright (C) 2009, Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+extern bfd *create_gcore_bfd (char *filename);
+extern void write_gcore_file (bfd *obfd);
+extern bfd *load_corefile (char *filename, int from_tty);
+
Index: inferior.h
===================================================================
RCS file: /cvs/src/src/gdb/inferior.h,v
retrieving revision 1.135
diff -u -p -r1.135 inferior.h
--- inferior.h	28 Jun 2009 00:20:22 -0000	1.135
+++ inferior.h	12 Aug 2009 22:28:43 -0000
@@ -236,6 +236,8 @@ extern void get_last_target_status(ptid_
 
 extern void follow_inferior_reset_breakpoints (void);
 
+extern void nullify_last_target_wait_ptid (void);
+
 /* Throw an error indicating the current thread is running.  */
 extern void error_is_running (void);
 
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	12 Aug 2009 22:28:43 -0000
@@ -25,10 +25,14 @@
 #include "gdbcore.h"
 #include "objfiles.h"
 #include "symfile.h"
-
+#include "arch-utils.h"
+#include "completer.h"
+#include "gcore.h"
 #include "cli/cli-decode.h"
-
 #include "gdb_assert.h"
+#include <fcntl.h>
+#include "regcache.h"
+#include "regset.h"
 
 /* The largest amount of memory to read from the target at once.  We
    must throttle it to limit the amount of memory used by GDB during
@@ -40,45 +44,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)
+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).  */
+
+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,9 +93,47 @@ 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 +236,50 @@ derive_stack_segment (bfd_vma *bottom, b
   return 1;
 }
 
+/* call_target_sbrk --
+   helper function for derive_heap_segment and load_core_segment.  */
+
+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 +287,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 +325,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 +345,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 +564,141 @@ gcore_memory_sections (bfd *obfd)
   return 1;
 }
 
+struct load_core_args_params {
+  int from_tty;
+  bfd_vma top_of_heap;
+};
+
+/* load_core_segments -- iterator function for bfd_map_over_sections.  */
+
+static void
+load_core_segment (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_segment", 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);
+    }
+}
+
+/* load_corefile -- reads a corefile, copies its memory sections
+   into target memory, and its registers into target regcache.  */
+
+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_segment, (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	12 Aug 2009 22:28:43 -0000
@@ -23,15 +23,24 @@
 #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>
+#include "elf-bfd.h"
+#include "gdbcore.h"
+#include <ctype.h>
+#include "gcore.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 +155,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 +255,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 +430,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 +815,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 +1035,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 +1190,448 @@ 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.
+ */
+
+/* bfdcore_write -- write bytes into a core file section.  */
+
+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;
+}
+
+/* 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;
+  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);
+}
+
+/* bfdcore_read -- read bytes from a core file section.  */
+
+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.  */
+
+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;
+  bfd *core_bfd;
+  asection *osec;
+
+  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 +1722,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 +1757,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 +1780,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: linux-fork.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-fork.c,v
retrieving revision 1.29
diff -u -p -r1.29 linux-fork.c
--- linux-fork.c	2 Jul 2009 22:24:07 -0000	1.29
+++ linux-fork.c	12 Aug 2009 22:28:43 -0000
@@ -235,7 +235,6 @@ call_lseek (int fd, off_t offset, int wh
 static void
 fork_load_infrun_state (struct fork_info *fp)
 {
-  extern void nullify_last_target_wait_ptid ();
   int i;
 
   linux_nat_switch_fork (fp->ptid);

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