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 26, 2009 at 09:35, Michael Snyder<msnyder@vmware.com> wrote:
> Michael Snyder wrote:
>>
>> Hui Zhu wrote:
>>>
>>> On Tue, Aug 25, 2009 at 01:51, Michael Snyder<msnyder@vmware.com> wrote:
>>>>
>>>> Have you considered combining this patch with the idea that I
>>>> proposed -- storing the record log in the core file? ?Seems like
>>>> that would be very easy now -- gdb already has the core file open,
>>>> and there is a global pointer to the BFD.
>>>> See gdbcore.h:extern bfd* core_bfd;
>>>
>>> Cool. ?If we just want record_core_ops support load, we can remove the
>>> cmd "record load". ?:)
>>>
>>>> In fact, just for fun, I've merged the two patches in my
>>>> local tree. ?It seems to work quite well. ?I can just post
>>>> it here if you like, or here's a hint to show you how it goes:
>>>
>>> Why not? ?Please post it. ?I can not wait to try it. ?:)
>>
>> Great -- attached.
>
> This is "quick and dirty", by the way.
> It's not meant to be finished!
>
>

Hi Michael,

I make a new patch that make the patches together.  Prec can load the
record log from a core file and debug it with core target.  To use it,
Call "record" after load the gdb_record.xxx file.

Please help me review it.

Thanks,
Hui

2009-08-29  Michael Snyder  <msnyder@vmware.com>
            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,
        elf-bfd.h, gcore.h, byteswap.h, netinet/in.h): Include files.
	(RECORD_FILE_MAGIC): New macro.
	(record_core_buf_entry): New struct.
	(record_core_ops): New target_ops.
	(record_list_release_first_insn): Change function
	record_list_release_first to this name.
	(record_reg_alloc, record_mem_alloc, record_end_alloc): New
	functions.
	(record_arch_list_add_reg): Call record_reg_alloc.
	(record_arch_list_add_mem): Call record_mem_alloc.
	(record_arch_list_add_end): Call record_end_alloc.
	(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, (bfdcore_read, record_load,
	record_core_open_1, record_open_1): New function.
	(record_open): Add support for record_core_ops.
	(record_close): Add support for record_core_ops.
	(record_wait): Call function 'record_exec_entry' and
	add support for target core.
	(record_registers_change): Call record_list_release_first_insn.
	(record_core_resume, record_core_resume, record_core_kill,
	record_core_fetch_registers, record_core_prepare_to_store,
	record_core_store_registers, record_core_xfer_partial,
	record_core_insert_breakpoint, record_core_remove_breakpoint,
	record_core_has_execution, init_record_core_ops,
	cmd_record_load, bfdcore_write,
	cmd_record_dump): New function.
	(cmd_record_stop): Add support for record_core_ops.
	(set_record_insn_max_num): Call record_list_release_first_insn.
	(_initialize_record): Add commands "record dump".
	* 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.

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

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

---
 gcore.c  |  309 ++++++++++++++----
 gcore.h  |   23 +
 record.c | 1072 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 1199 insertions(+), 205 deletions(-)

--- a/gcore.c
+++ b/gcore.c
@@ -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,8 +93,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 +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;

--- /dev/null
+++ b/gcore.h
@@ -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);
+
--- a/record.c
+++ b/record.c
@@ -23,15 +23,25 @@
 #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 "elf-bfd.h"
+#include "gcore.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(0x20090829)
+
 /* These are the core struct of record function.

    An record_entry is a record of the value change of a register
@@ -78,9 +88,22 @@ 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 buf with core target.  */
+static gdb_byte *record_core_regbuf = NULL;
+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;
@@ -94,6 +117,7 @@ static int record_insn_num = 0;

 /* The target_ops of process record.  */
 static struct target_ops record_ops;
+static struct target_ops record_core_ops;

 /* The beneath function pointers.  */
 static struct target_ops *record_beneath_to_resume_ops;
@@ -169,7 +193,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;
@@ -227,6 +251,56 @@ record_arch_list_add (struct record_entr
     }
 }

+/* Alloc a record_reg record entry. */
+
+static inline struct record_entry *
+record_reg_alloc (int num)
+{
+  struct record_entry *rec;
+
+  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;
+  rec->u.reg.num = num;
+
+  return rec;
+}
+
+/* Alloc a record_mem record entry. */
+
+static inline struct record_entry *
+record_mem_alloc (int len)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+  rec->u.mem.val = (gdb_byte *) xmalloc (len);
+  rec->prev = NULL;
+  rec->next = NULL;
+  rec->type = record_mem;
+  rec->u.mem.len = len;
+  rec->u.mem.mem_entry_not_accessible = 0;
+
+  return rec;
+}
+
+/* Alloc a record_mem record entry. */
+
+static inline struct record_entry *
+record_end_alloc (void)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+  rec->prev = NULL;
+  rec->next = NULL;
+  rec->type = record_end;
+
+  return rec;
+}
+
 /* Record the value of a register NUM to record_arch_list.  */

 int
@@ -240,12 +314,7 @@ record_arch_list_add_reg (struct regcach
 			"record list.\n",
 			num);

-  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;
-  rec->u.reg.num = num;
+  rec = record_reg_alloc (num);

   regcache_raw_read (regcache, num, rec->u.reg.val);

@@ -271,14 +340,8 @@ record_arch_list_add_mem (CORE_ADDR addr
   if (!addr)
     return 0;

-  rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
-  rec->u.mem.val = (gdb_byte *) xmalloc (len);
-  rec->prev = NULL;
-  rec->next = NULL;
-  rec->type = record_mem;
+  rec = record_mem_alloc (len);
   rec->u.mem.addr = addr;
-  rec->u.mem.len = len;
-  rec->u.mem.mem_entry_not_accessible = 0;

   if (target_read_memory (addr, rec->u.mem.val, len))
     {
@@ -308,10 +371,7 @@ record_arch_list_add_end (void)
     fprintf_unfiltered (gdb_stdlog,
 			"Process record: add end to arch list.\n");

-  rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
-  rec->prev = NULL;
-  rec->next = NULL;
-  rec->type = record_end;
+  rec = record_end_alloc ();

   record_arch_list_add (rec);

@@ -340,30 +400,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 +446,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,13 +476,297 @@ 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))
+              {
+                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
+              {
+                if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
+                                         entry->u.mem.len))
+                  {
+                    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
+                  memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+              }
+          }
+      }
+      break;
+    }
+}
+
+/* Load the execution log from core_bfd.  */
+
+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;
+}
+
 static void
-record_open (char *name, int from_tty)
+record_load (void)
 {
-  struct target_ops *t;
+  uint32_t magic;
+  struct cleanup *old_cleanups;
+  struct record_entry *rec;
+  asection *osec;
+  int bfd_offset = 0;
+
+  /* We load the execution log from the open core bfd,
+     if there is one.  */
+  if (core_bfd == NULL)
+    return;
+
+  /* "record_load" just can be called when record list is empty.  */
+  gdb_assert (record_first.next == NULL);

   if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+    fprintf_filtered (gdb_stdlog,
+		      _("Restoring recording from core file.\n"));
+
+  /* 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)
+    return;
+  if (record_debug)
+    fprintf_filtered (gdb_stdlog, "osec name = '%s'\n",
+                      bfd_section_name (core_bfd, osec));
+
+  /* Check the magic code.  */
+  if (!bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset))
+    error (_("Failed to read 'magic' from core file (%s)"),
+           bfd_errmsg (bfd_get_error ()));
+  if (magic != RECORD_FILE_MAGIC)
+    error (_("Version mis-match or file format error."));
+  if (record_debug)
+    fprintf_filtered (gdb_stdlog, _("\
+Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
+                      magic);
+
+  /* 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;
+  record_insn_num = 0;
+  old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
+
+  while (1)
+    {
+      int ret;
+      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 */
+          /* Get num to tmpu64.  */
+          if (!bfdcore_read (core_bfd, osec, &tmpu64,
+                             sizeof (tmpu64), &bfd_offset))
+            error (_("Failed to read regnum from core file (%s)"),
+                   bfd_errmsg (bfd_get_error ()));
+          if (BYTE_ORDER == LITTLE_ENDIAN)
+            tmpu64 = bswap_64 (tmpu64);
+
+          rec = record_reg_alloc ((int) tmpu64);
+
+          /* Get val.  */
+          if (!bfdcore_read (core_bfd, osec, rec->u.reg.val,
+                             MAX_REGISTER_SIZE, &bfd_offset))
+            error (_("Failed to read regval from  core file (%s)"),
+                   bfd_errmsg (bfd_get_error ()));
+
+          if (record_debug)
+            fprintf_unfiltered (gdb_stdlog, _("\
+Reading register %d (1 plus 8 plus %d bytes)\n"),
+                                rec->u.reg.num,
+                                MAX_REGISTER_SIZE);
+          break;
+
+        case record_mem: /* mem */
+          /* Get len.  */
+          if (!bfdcore_read (core_bfd, osec, &tmpu64,
+                             sizeof (tmpu64), &bfd_offset))
+            error (_("Failed to read memlen from core file (%s)"),
+                   bfd_errmsg (bfd_get_error ()));
+          if (BYTE_ORDER == LITTLE_ENDIAN)
+            tmpu64 = bswap_64 (tmpu64);
+
+          rec = record_mem_alloc ((int) tmpu64);
+
+          /* Get addr.  */
+          if (!bfdcore_read (core_bfd, osec, &tmpu64,
+                             sizeof (tmpu64), &bfd_offset))
+            error (_("Failed to read memaddr from core file (%s)"),
+                   bfd_errmsg (bfd_get_error ()));
+          if (BYTE_ORDER == LITTLE_ENDIAN)
+            tmpu64 = bswap_64 (tmpu64);
+          rec->u.mem.addr = tmpu64;
+
+          /* Get val.  */
+          if (!bfdcore_read (core_bfd, osec, rec->u.mem.val,
+                             rec->u.mem.len, &bfd_offset))
+            error (_("Failed to read memval from core file (%s)"),
+                   bfd_errmsg (bfd_get_error ()));
+
+          if (record_debug)
+            fprintf_unfiltered (gdb_stdlog, _("\
+Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+                                        paddress (get_current_arch (),
+                                                  rec->u.mem.addr),
+                                        rec->u.mem.len);
+          break;
+
+        case record_end: /* end */
+          rec = record_end_alloc ();
+          record_insn_num ++;
+
+          if (record_debug)
+            fprintf_unfiltered (gdb_stdlog,
+                                _("Reading record_end (1 byte)\n"));
+          break;
+
+        default:
+          error (_("Format of core file is not right."));
+          break;
+        }
+
+      /* Add rec to record arch list.  */
+      record_arch_list_add (rec);
+    }
+
+  discard_cleanups (old_cleanups);
+
+  /* Add record_arch_list_head to the end of record list.  */
+  record_first.next = record_arch_list_head;
+  record_arch_list_head->prev = &record_first;
+
+  /* Update record_insn_max_num.  */
+  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);
+    }
+
+  /* Succeeded.  */
+  fprintf_filtered (gdb_stdout, "Loaded records from core file.\n");
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
+static struct target_ops *tmp_to_resume_ops;
+static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
+                              enum target_signal);
+static struct target_ops *tmp_to_wait_ops;
+static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
+                              struct target_waitstatus *,
+                              int);
+static struct target_ops *tmp_to_store_registers_ops;
+static void (*tmp_to_store_registers) (struct target_ops *,
+                                       struct regcache *,
+                                       int regno);
+static struct target_ops *tmp_to_xfer_partial_ops;
+static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
+                                       enum target_object object,
+                                       const char *annex,
+                                       gdb_byte *readbuf,
+                                       const gdb_byte *writebuf,
+                                       ULONGEST offset,
+                                       LONGEST len);
+static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
+                                        struct bp_target_info *);
+static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
+                                        struct bp_target_info *);
+
+static void
+record_core_open_1 (char *name, int from_tty)
+{
+  struct regcache *regcache = get_current_regcache ();
+  int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+  int i;
+
+  /* Get record_core_regbuf.  */
+  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))
+    {
+      xfree (record_core_regbuf);
+      record_core_regbuf = NULL;
+      error (_("\"%s\": Can't find sections: %s"),
+             bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+    }
+
+  push_target (&record_core_ops);
+}
+
+static void
+record_open_1 (char *name, int from_tty)
+{
+  struct target_ops *t;

   /* check exec */
   if (!target_has_execution)
@@ -438,6 +782,28 @@ record_open (char *name, int from_tty)
     error (_("Process record: the current architecture doesn't support "
 	     "record function."));

+  if (!tmp_to_resume)
+    error (_("Process record can't get to_resume."));
+  if (!tmp_to_wait)
+    error (_("Process record can't get to_wait."));
+  if (!tmp_to_store_registers)
+    error (_("Process record can't get to_store_registers."));
+  if (!tmp_to_insert_breakpoint)
+    error (_("Process record can't get to_insert_breakpoint."));
+  if (!tmp_to_remove_breakpoint)
+    error (_("Process record can't get to_remove_breakpoint."));
+
+  push_target (&record_ops);
+}
+
+static void
+record_open (char *name, int from_tty)
+{
+  struct target_ops *t;
+
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+
   /* Check if record target is already running.  */
   if (current_target.to_stratum == record_stratum)
     {
@@ -447,70 +813,102 @@ record_open (char *name, int from_tty)
 	return;
     }

-  /*Reset the beneath function pointers.  */
-  record_beneath_to_resume = NULL;
-  record_beneath_to_wait = NULL;
-  record_beneath_to_store_registers = NULL;
-  record_beneath_to_xfer_partial = NULL;
-  record_beneath_to_insert_breakpoint = NULL;
-  record_beneath_to_remove_breakpoint = NULL;
+  /* Reset the tmp beneath pointers.  */
+  tmp_to_resume_ops = NULL;
+  tmp_to_resume = NULL;
+  tmp_to_wait_ops = NULL;
+  tmp_to_wait = NULL;
+  tmp_to_store_registers_ops = NULL;
+  tmp_to_store_registers = NULL;
+  tmp_to_xfer_partial_ops = NULL;
+  tmp_to_xfer_partial = NULL;
+  tmp_to_insert_breakpoint = NULL;
+  tmp_to_remove_breakpoint = NULL;

   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
     {
-      if (!record_beneath_to_resume)
+      if (!tmp_to_resume)
         {
-	  record_beneath_to_resume = t->to_resume;
-	  record_beneath_to_resume_ops = t;
+	  tmp_to_resume = t->to_resume;
+	  tmp_to_resume_ops = t;
         }
-      if (!record_beneath_to_wait)
+      if (!tmp_to_wait)
         {
-	  record_beneath_to_wait = t->to_wait;
-	  record_beneath_to_wait_ops = t;
+	  tmp_to_wait = t->to_wait;
+	  tmp_to_wait_ops = t;
         }
-      if (!record_beneath_to_store_registers)
+      if (!tmp_to_store_registers)
         {
-	  record_beneath_to_store_registers = t->to_store_registers;
-	  record_beneath_to_store_registers_ops = t;
+	  tmp_to_store_registers = t->to_store_registers;
+	  tmp_to_store_registers_ops = t;
         }
-      if (!record_beneath_to_xfer_partial)
+      if (!tmp_to_xfer_partial)
         {
-	  record_beneath_to_xfer_partial = t->to_xfer_partial;
-	  record_beneath_to_xfer_partial_ops = t;
+	  tmp_to_xfer_partial = t->to_xfer_partial;
+	  tmp_to_xfer_partial_ops = t;
         }
-      if (!record_beneath_to_insert_breakpoint)
-	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 (!tmp_to_insert_breakpoint)
+	tmp_to_insert_breakpoint = t->to_insert_breakpoint;
+      if (!tmp_to_remove_breakpoint)
+	tmp_to_remove_breakpoint = t->to_remove_breakpoint;
     }
-  if (!record_beneath_to_resume)
-    error (_("Process record can't get to_resume."));
-  if (!record_beneath_to_wait)
-    error (_("Process record can't get to_wait."));
-  if (!record_beneath_to_store_registers)
-    error (_("Process record can't get to_store_registers."));
-  if (!record_beneath_to_xfer_partial)
+  if (!tmp_to_xfer_partial)
     error (_("Process record can't get to_xfer_partial."));
-  if (!record_beneath_to_insert_breakpoint)
-    error (_("Process record can't get to_insert_breakpoint."));
-  if (!record_beneath_to_remove_breakpoint)
-    error (_("Process record can't get to_remove_breakpoint."));

-  push_target (&record_ops);
+  if (current_target.to_stratum == core_stratum)
+    record_core_open_1 (name, from_tty);
+  else
+    record_open_1 (name, from_tty);

   /* Reset */
   record_insn_num = 0;
   record_list = &record_first;
   record_list->next = NULL;
+
+  /* Set the tmp beneath pointers to beneath pointers.  */
+  record_beneath_to_resume_ops = tmp_to_resume_ops;
+  record_beneath_to_resume = tmp_to_resume;
+  record_beneath_to_wait_ops = tmp_to_wait_ops;
+  record_beneath_to_wait = tmp_to_wait;
+  record_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
+  record_beneath_to_store_registers = tmp_to_store_registers;
+  record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops;
+  record_beneath_to_xfer_partial = tmp_to_xfer_partial;
+  record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
+  record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
+
+  if (strcmp (current_target.to_shortname, "record_core") == 0)
+    record_load ();
 }

 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.  */
+  if (record_core_regbuf)
+    {
+      xfree (record_core_regbuf);
+      record_core_regbuf = NULL;
+    }
+
+  /* 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;
@@ -584,7 +982,7 @@ record_wait (struct target_ops *ops,
 			"record_resume_step = %d\n",
 			record_resume_step);

-  if (!RECORD_IS_REPLAY)
+  if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
       if (record_resume_error)
 	{
@@ -712,76 +1110,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,6 +1232,7 @@ record_kill (struct target_ops *ops)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");

   unpush_target (&record_ops);
+
   target_kill ();
 }

@@ -945,7 +1277,7 @@ 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++;
 }
@@ -1058,7 +1390,7 @@ 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++;
     }
@@ -1138,6 +1470,191 @@ init_record_ops (void)
 }

 static void
+record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
+                    enum target_signal siggnal)
+{
+  record_resume_step = step;
+  record_resume_siggnal = siggnal;
+}
+
+static void
+record_core_kill (struct target_ops *ops)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
+
+  unpush_target (&record_core_ops);
+}
+
+static void
+record_core_fetch_registers (struct target_ops *ops,
+                             struct regcache *regcache,
+                             int regno)
+{
+  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);
+}
+
+static void
+record_core_prepare_to_store (struct regcache *regcache)
+{
+}
+
+static void
+record_core_store_registers (struct target_ops *ops,
+                             struct regcache *regcache,
+                             int regno)
+{
+  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."));
+}
+
+static LONGEST
+record_core_xfer_partial (struct target_ops *ops, enum target_object object,
+		          const char *annex, gdb_byte *readbuf,
+		          const gdb_byte *writebuf, ULONGEST offset,
+                          LONGEST len)
+{
+   if (object == TARGET_OBJECT_MEMORY)
+     {
+       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 -1;
+         }
+       else
+         error (_("You can't do that without a process to debug."));
+     }
+
+  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
+                                         object, annex, readbuf, writebuf,
+                                         offset, len);
+}
+
+static int
+record_core_insert_breakpoint (struct gdbarch *gdbarch,
+			       struct bp_target_info *bp_tgt)
+{
+  return 0;
+}
+
+static int
+record_core_remove_breakpoint (struct gdbarch *gdbarch,
+			       struct bp_target_info *bp_tgt)
+{
+  return 0;
+}
+
+int
+record_core_has_execution (struct target_ops *ops)
+{
+  return 1;
+}
+
+static void
+init_record_core_ops (void)
+{
+  record_core_ops.to_shortname = "record_core";
+  record_core_ops.to_longname = "Process record and replay target";
+  record_core_ops.to_doc =
+    "Log program while executing and replay execution from log.";
+  record_core_ops.to_open = record_open;
+  record_core_ops.to_close = record_close;
+  record_core_ops.to_resume = record_core_resume;
+  record_core_ops.to_wait = record_wait;
+  record_core_ops.to_kill = record_core_kill;
+  record_core_ops.to_fetch_registers = record_core_fetch_registers;
+  record_core_ops.to_prepare_to_store = record_core_prepare_to_store;
+  record_core_ops.to_store_registers = record_core_store_registers;
+  record_core_ops.to_xfer_partial = record_core_xfer_partial;
+  record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
+  record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
+  record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
+  record_core_ops.to_has_execution = record_core_has_execution;
+  record_core_ops.to_stratum = record_stratum;
+  record_core_ops.to_magic = OPS_MAGIC;
+}
+
+static void
 show_record_debug (struct ui_file *file, int from_tty,
 		   struct cmd_list_element *c, const char *value)
 {
@@ -1153,6 +1670,262 @@ 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 htonl(0x20090829).
+       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).
+       MAX_REGISTER_SIZE bytes: register value.
+     record_mem:
+       1 byte:  record type (record_mem).
+       8 bytes: memory length (network byte order).
+       8 bytes: memory address (network byte order).
+       n bytes: memory value (n == memory length).
+*/
+
+/* 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;
+  int bfd_offset = 0;
+
+  if (strcmp (current_target.to_shortname, "record") != 0)
+    error (_("Process record is not started.\n"));
+
+  if (args && *args)
+    recfilename = args;
+  else
+    {
+      /* Default recfile name is "gdb_record.PID".  */
+      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
+                "gdb_record.%d", PIDGET (inferior_ptid));
+      recfilename = recfilename_buffer;
+    }
+
+  /* Open the dump file.  */
+  if (record_debug)
+    fprintf_unfiltered (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.  */
+  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;
+    }
+
+  /* Compute the size needed for the extra bfd section.  */
+  dump_size = 4;	/* magic cookie */
+  for (record_list = &record_first; record_list;
+       record_list = record_list->next)
+    switch (record_list->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 + record_list->u.mem.len;
+	break;
+      }
+
+  /* Make the new bfd section.  */
+  osec = bfd_make_section_anyway_with_flags (obfd, "precord",
+                                             SEC_HAS_CONTENTS
+                                             | SEC_READONLY);
+  if (osec == NULL)
+    error (_("Failed to create 'precord' section for corefile: %s"),
+           bfd_errmsg (bfd_get_error ()));
+  bfd_set_section_size (obfd, osec, dump_size);
+  bfd_set_section_vma (obfd, osec, 0);
+  bfd_set_section_alignment (obfd, osec, 0);
+  bfd_section_lma (obfd, osec) = 0;
+
+  /* Save corefile state.  */
+  write_gcore_file (obfd);
+
+  /* Write out the record log.  */
+  /* 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 to recfd and forward execute to the end of
+     record list.  */
+  record_list = &record_first;
+  while (1)
+    {
+      /* Dump entry.  */
+      if (record_list != &record_first)
+        {
+          uint8_t tmpu8;
+          uint64_t tmpu64;
+
+          tmpu8 = record_list->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 (record_list->type)
+            {
+            case record_reg: /* reg */
+              if (record_debug)
+                fprintf_unfiltered (gdb_stdlog, _("\
+Writing register %d (1 plus 8 plus %d bytes)\n"),
+                                    record_list->u.reg.num,
+                                    MAX_REGISTER_SIZE);
+
+              /* Write regnum.  */
+              tmpu64 = record_list->u.reg.num;
+              if (BYTE_ORDER == LITTLE_ENDIAN)
+                tmpu64 = bswap_64 (tmpu64);
+              if (!bfdcore_write (obfd, osec, &tmpu64,
+                                  sizeof (tmpu64), &bfd_offset))
+                error (_("Failed to write regnum to %s (%s)"),
+              recfilename, bfd_errmsg (bfd_get_error ()));
+
+              /* Write regval.  */
+              if (!bfdcore_write (obfd, osec, record_list->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 */
+              if (!record_list->u.mem.mem_entry_not_accessible)
+                {
+                  if (record_debug)
+                    fprintf_unfiltered (gdb_stdlog, _("\
+Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
+                                        paddress (gdbarch,
+                                                  record_list->u.mem.addr),
+                                        record_list->u.mem.len);
+
+                  /* Write memlen.  */
+                  tmpu64 = record_list->u.mem.len;
+                  if (BYTE_ORDER == LITTLE_ENDIAN)
+                    tmpu64 = bswap_64 (tmpu64);
+                  if (!bfdcore_write (obfd, osec, &tmpu64,
+                                      sizeof (tmpu64), &bfd_offset))
+                    error (_("Failed to write memlen to %s (%s)"),
+                           recfilename, bfd_errmsg (bfd_get_error ()));
+
+                  /* Write memaddr.  */
+                  tmpu64 = record_list->u.mem.addr;
+                  if (BYTE_ORDER == LITTLE_ENDIAN)
+                    tmpu64 = bswap_64 (tmpu64);
+                  if (!bfdcore_write (obfd, osec, &tmpu64,
+                                      sizeof (tmpu64), &bfd_offset))
+                    error (_("Failed to write memaddr to %s (%s)"),
+                           recfilename, bfd_errmsg (bfd_get_error ()));
+
+                  /* Write memval.  */
+                  if (!bfdcore_write (obfd, osec, record_list->u.mem.val,
+                                      record_list->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_unfiltered (gdb_stdlog,
+                                      _("Writing record_end (1 byte)\n"));
+                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 recfile %s.\n"), recfilename);
+}
+
 /* Truncate the record log from the present point
    of replay until the end.  */

@@ -1185,7 +1958,12 @@ cmd_record_stop (char *args, int from_tt
     {
       if (!record_list || !from_tty || query (_("Delete recorded log and "
 	                                        "stop recording?")))
-	unpush_target (&record_ops);
+        {
+          if (strcmp (current_target.to_shortname, "record") == 0)
+	    unpush_target (&record_ops);
+          else
+            unpush_target (&record_core_ops);
+        }
     }
   else
     printf_unfiltered (_("Process record is not started.\n"));
@@ -1203,7 +1981,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 ();
     }
 }

@@ -1243,6 +2021,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;
@@ -1250,6 +2030,8 @@ _initialize_record (void)

   init_record_ops ();
   add_target (&record_ops);
+  init_record_core_ops ();
+  add_target (&record_core_ops);

   add_setshow_zinteger_cmd ("record", no_class, &record_debug,
 			    _("Set debugging of record/replay feature."),
@@ -1259,9 +2041,10 @@ _initialize_record (void)
 			    NULL, show_record_debug, &setdebuglist,
 			    &showdebuglist);

-  add_prefix_cmd ("record", class_obscure, cmd_record_start,
-		  _("Abbreviated form of \"target record\" command."),
- 		  &record_cmdlist, "record ", 0, &cmdlist);
+  c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
+		      _("Abbreviated form of \"target record\" command."),
+ 		      &record_cmdlist, "record ", 0, &cmdlist);
+  set_cmd_completer (c, filename_completer);
   add_com_alias ("rec", "record", class_obscure, 1);
   add_prefix_cmd ("record", class_support, set_record_command,
 		  _("Set record options"), &set_record_cmdlist,
@@ -1276,6 +2059,11 @@ _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);

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


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

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

Attachment: prec-dump.txt
Description: Text document

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


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