Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Michael Snyder <msnyder@vmware.com>
To: "tromey@redhat.com" <tromey@redhat.com>
Cc: Hui Zhu <teawater@gmail.com>,
	  "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: Re: [RFA/RFC] Add dump and load command to process record (file    format etc)
Date: Wed, 12 Aug 2009 22:38:00 -0000	[thread overview]
Message-ID: <4A8342AC.6070507@vmware.com> (raw)
In-Reply-To: <m3ab25rrtr.fsf@fleche.redhat.com>

[-- Attachment #1: Type: text/plain, Size: 913 bytes --]

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


[-- Attachment #2: dumpload3.txt --]
[-- Type: text/plain, Size: 39307 bytes --]

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);

  reply	other threads:[~2009-08-12 22:33 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-08-01  7:31 [RFA/RFC] Add dump and load command to process record and replay Hui Zhu
2009-08-01  9:57 ` Eli Zaretskii
2009-08-01 19:20 ` Michael Snyder
2009-08-02  3:18   ` Michael Snyder
2009-08-02  5:58     ` Hui Zhu
2009-08-03  4:12       ` Hui Zhu
2009-08-03 18:29         ` Eli Zaretskii
2009-08-04  1:58           ` Hui Zhu
2009-08-04  2:07           ` Hui Zhu
2009-08-04 18:26             ` Eli Zaretskii
2009-08-04 20:01               ` Michael Snyder
2009-08-05  9:21                 ` Hui Zhu
2009-08-05 20:19                   ` [RFA/RFC] Add dump and load command to process record (file format etc) Michael Snyder
2009-08-06  2:17                     ` Hui Zhu
2009-08-12 14:11                       ` Michael Snyder
2009-08-12 15:16                         ` Tom Tromey
2009-08-12 22:38                           ` Michael Snyder [this message]
2009-08-16  0:04                             ` Hui Zhu
2009-08-05 21:23                   ` [RFA/RFC] Add dump and load command to process record and replay Michael Snyder
2009-08-06  3:14                     ` Eli Zaretskii
2009-08-06 14:16                       ` Hui Zhu
2009-08-07  3:27                         ` Michael Snyder
2009-08-07  3:29                           ` Hui Zhu
2009-08-07  3:34                             ` Michael Snyder
2009-08-07  4:06                               ` Hui Zhu
2009-08-07  8:41                                 ` Eli Zaretskii
2009-08-07  9:53                                   ` Hui Zhu
2009-08-07 12:51                                     ` Eli Zaretskii
2009-08-07 16:22                                       ` Hui Zhu
2009-08-07 17:42                                   ` Michael Snyder
2009-08-08 13:28                                     ` Hui Zhu
2009-08-10  3:09                                       ` Michael Snyder
2009-08-22 17:39                                         ` Hui Zhu
2009-08-23  1:14                                           ` Hui Zhu
2009-08-23 23:43                                           ` Michael Snyder
2009-08-24  8:20                                             ` Hui Zhu
2009-08-24 18:32                                               ` Michael Snyder
2009-08-25  8:47                                                 ` Hui Zhu
2009-08-26  1:40                                                   ` Michael Snyder
2009-08-26  2:59                                                     ` Michael Snyder
2009-08-29 15:53                                                       ` Hui Zhu
2009-08-29 18:06                                                         ` Eli Zaretskii
2009-08-29 18:28                                                           ` Hui Zhu
2009-08-29 20:26                                                             ` Eli Zaretskii
2009-08-29 20:39                                                               ` Michael Snyder
2009-08-30  3:03                                                                 ` Eli Zaretskii
2009-08-30  5:36                                                                   ` Hui Zhu
2009-08-30 23:40                                                                     ` Eli Zaretskii
2009-08-31  7:10                                                                       ` Hui Zhu
2009-09-05  3:30                                                                         ` Michael Snyder
2009-09-06 16:29                                                                           ` Hui Zhu
2009-08-29 20:05                                                           ` Michael Snyder
2009-08-29 20:33                                                             ` Eli Zaretskii
2009-08-29 21:20                                                               ` Michael Snyder

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4A8342AC.6070507@vmware.com \
    --to=msnyder@vmware.com \
    --cc=gdb-patches@sourceware.org \
    --cc=teawater@gmail.com \
    --cc=tromey@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox