Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Michael Snyder <msnyder@vmware.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
	  "teawater@gmail.com" <teawater@gmail.com>
Subject: Re: [RFA, 3 of 3] save/restore process record, part 3 (save/restore)
Date: Sat, 17 Oct 2009 22:19:00 -0000	[thread overview]
Message-ID: <4ADA4191.4060404@vmware.com> (raw)
In-Reply-To: <83my3phjew.fsf@gnu.org>

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

Eli Zaretskii wrote:
>> Date: Sat, 17 Oct 2009 11:36:35 -0700
>> From: Michael Snyder <msnyder@vmware.com>
>> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>, 
>>  "teawater@gmail.com" <teawater@gmail.com>
>>
>>>> +       NOTE: be sure to change whenever this file format changes!
>>>> +
>>>> +   Records:
>>>> +     record_end:
>>>> +       1 byte:  record type (record_end).
>>>                                 ^^^^^^^^^^
>>> This probably has some integer value, right?  Or does this indicate
>>> something other than an integer type?
>> It's an enum.  Why?  You think I should give its numeric
>> value in the comment, instead of its symbolic value?
> 
> Either the numeric value or a more explicit reference to the enum
> (e.g., say that it's an enum defined on such-and-such file).
> 
> IOW, imagine that someone reads these comments because they want to
> write a utility that reads these files, and ask yourself what would
> they be missing in this text.

OK, see revised patch.

>>>> +     record_reg:
>>>> +       1 byte:  record type (record_reg).
>>>> +       4 bytes: register id (network byte order).
>>>> +       n bytes: register value (n == register size).
>>>                                         ^^^^^^^^^^^^^
>>> How does one know what is the correct register size?
>> We get it from the current regcache and regnum.  What can I say about
>> it here, that wouldn't be arch-specific?
> 
> Well, saying it's the exact size of the register being recorded works
> for me.

OK.

> 
>> I mean, I could say:
>>
>>     This will generally be 4 bytes for x86, 8 bytes for x86_64.
> 
> That's good as an example.
> 
>> but that doesn't seem very useful or scalable.  Plus it will
>> be different for floating point regs, once we handle them too.
> 
> Would it make sense to use here some size that can accommodate all the
> known registers, to make the file format truly architecture
> independent?  Or would it be more trouble than it's worth?

That's what version 1 of the file format did.
I found it wasteful, hence this change.

> 
>>>> +  if (strcmp (current_target.to_shortname, "record") != 0)
>>>> +    error (_("Process record is not started.\n"));
>>> Suggest to add to the message text something that tells the user how
>>> to remedy this situation.  E.g., "Invoke FOO command first."
>> OK.  It's a target-specific command, that can only be used
>> with target 'record'.  How about this?
>>
>>    error (_("This command can only be used with target 'record'.\n"));
> 
> That's good, but maybe adding "Use 'record start' first." will be
> better?

OK.

> 
>> This is the same approach that is used by the "gcore" command.
>> How does "gcore" work with go32, if at all?
> 
> It doesn't.  DJGPP cannot generate core files.

Well, save/restore depends on core files, so I guess
it won't work in go32.


>> Thanks for all your suggestions, Eli.
>> Please see revised patch attached.
> 
> It's fine, except for this:
> 
>> +   Header:
>> +     4 bytes: magic number htonl(0x20090829).
> 
> I thought you agreed to replace this with "0x20090829 (network byte
> order)"?

Ah.  Yes -- you're looking at the version 1 header, but I do need
to change the comment for the version 2 header.

See revised patch below.



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

2009-10-16  Hui Zhu  <teawater@gmail.com>
	    Michael Snyder  <msnyder@msnyder-server.eng.vmware.com>

	* record.c (RECORD_FILE_MAGIC): New constant.
	(record_arch_list_cleanups): Renamed from record_message_cleanups.
	(bfdcore_read): New function.
	(netorder64): New function.
	(netorder32): New function.
	(netorder16): New function.
	(record_restore): New function.  Restore a saved record log.
	(bfdcore_write): New function.
	(cmd_record_restore): New function.
	(cmd_record_save): New function.  Save a record log to a file.
	(_initialize_record): Set up commands for save and restore.

--- record.2b.c	2009-10-17 11:30:35.000000000 -0700
+++ record.12.c	2009-10-17 15:09:48.000000000 -0700
@@ -23,10 +23,15 @@
 #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>
 
 #define DEFAULT_RECORD_INSN_MAX_NUM	200000
@@ -34,6 +39,8 @@
 #define RECORD_IS_REPLAY \
      (record_list->next || execution_direction == EXEC_REVERSE)
 
+#define RECORD_FILE_MAGIC	netorder32(0x20091016)
+
 /* These are the core structs of the process record functionality.
 
    A record_entry is a record of the value change of a register
@@ -482,24 +489,24 @@ record_check_insn_num (int set_terminal)
 	      if (q)
 		record_stop_at_limit = 0;
 	      else
-		error (_("Process record: inferior program stopped."));
+		error (_("Process record: stopped 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);
-}
-
 struct record_message_args {
   struct regcache *regcache;
   enum target_signal signal;
@@ -511,7 +518,7 @@ record_message (void *args)
   int ret;
   struct record_message_args *myargs = args;
   struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
-  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;
@@ -651,8 +658,8 @@ record_exec_insn (struct regcache *regca
               {
                 entry->u.mem.mem_entry_not_accessible = 1;
                 if (record_debug)
-                  warning (_("Process record: error reading memory at "
-                             "addr = %s len = %d."),
+                  warning ("Process record: error reading memory at "
+			   "addr = %s len = %d.",
 			   paddress (gdbarch, entry->u.mem.addr),
                            entry->u.mem.len);
               }
@@ -664,8 +671,8 @@ record_exec_insn (struct regcache *regca
                   {
                     entry->u.mem.mem_entry_not_accessible = 1;
                     if (record_debug)
-                      warning (_("Process record: error writing memory at "
-                                 "addr = %s len = %d."),
+                      warning ("Process record: error writing memory at "
+			       "addr = %s len = %d.",
                                paddress (gdbarch, entry->u.mem.addr),
                                entry->u.mem.len);
                   }
@@ -678,6 +685,215 @@ record_exec_insn (struct regcache *regca
     }
 }
 
+/* bfdcore_read -- read bytes from a core file section.  */
+
+static void
+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;
+  else
+    error (_("Failed to read %d bytes from core file %s ('%s').\n"),
+	   len, bfd_get_filename (obfd),
+	   bfd_errmsg (bfd_get_error ()));
+}
+
+static uint64_t
+netorder64 (uint64_t fromfile)
+{
+  return (BYTE_ORDER == LITTLE_ENDIAN) 
+    ? bswap_64 (fromfile) 
+    : fromfile;
+}
+
+static uint32_t
+netorder32 (uint32_t fromfile)
+{
+  return (BYTE_ORDER == LITTLE_ENDIAN) 
+    ? bswap_32 (fromfile) 
+    : fromfile;
+}
+
+static uint16_t
+netorder16 (uint16_t fromfile)
+{
+  return (BYTE_ORDER == LITTLE_ENDIAN) 
+    ? bswap_16 (fromfile) 
+    : fromfile;
+}
+
+/* Restore the execution log from a core_bfd file.  */
+
+static void
+record_restore (void)
+{
+  uint32_t magic;
+  struct cleanup *old_cleanups;
+  struct record_entry *rec;
+  asection *osec;
+  uint32_t osec_size;
+  int bfd_offset = 0;
+  struct regcache *regcache;
+
+  /* We restore the execution log from the open core bfd,
+     if there is one.  */
+  if (core_bfd == NULL)
+    return;
+
+  /* "record_restore" can only be called when record list is empty.  */
+  gdb_assert (record_first.next == NULL);
+
+  if (record_debug)
+    printf_filtered ("Restoring recording from core file.\n");
+
+  /* Now need to find our special note section.  */
+  osec = bfd_get_section_by_name (core_bfd, "null0");
+  osec_size = bfd_section_size (core_bfd, osec);
+  if (record_debug)
+    printf_filtered ("Find precord section %s.\n",
+		     osec ? "succeeded" : "failed");
+  if (!osec)
+    return;
+  if (record_debug)
+    printf_filtered ("%s", bfd_section_name (core_bfd, osec));
+
+  /* Check the magic code.  */
+  if (record_debug)
+    printf_filtered ("\
+  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
+		     magic);
+  bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
+  if (magic != RECORD_FILE_MAGIC)
+    error (_("Version mis-match or file format error in core file %s."),
+	   bfd_get_filename (core_bfd));
+
+  /* Restore the entries in recfd into 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);
+  regcache = get_current_regcache ();
+
+  while (1)
+    {
+      int ret;
+      uint8_t tmpu8;
+      uint32_t regnum, len, signal, count;
+      uint64_t addr;
+
+      /* We are finished when offset reaches osec_size.  */
+      if (bfd_offset >= osec_size)
+	break;
+      bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
+
+      switch (tmpu8)
+        {
+        case record_reg: /* reg */
+          if (record_debug)
+            printf_filtered ("\
+  Reading register %d (1 plus %d plus %d bytes)\n",
+			     rec->u.reg.num,
+			     sizeof (regnum),
+			     rec->u.reg.len);
+          /* Get register number to regnum.  */
+          bfdcore_read (core_bfd, osec, &regnum,
+			sizeof (regnum), &bfd_offset);
+	  regnum = netorder32 (regnum);
+
+          rec = record_reg_alloc (regcache, regnum);
+
+          /* Get val.  */
+          bfdcore_read (core_bfd, osec, record_get_loc (rec),
+			rec->u.reg.len, &bfd_offset);
+          break;
+
+        case record_mem: /* mem */
+          if (record_debug)
+            printf_filtered ("\
+  Reading memory %s (1 plus %d plus %d plus %d bytes)\n",
+			     paddress (get_current_arch (),
+				       rec->u.mem.addr),
+			     sizeof (addr),
+			     sizeof (len),
+			     rec->u.mem.len);
+          /* Get len.  */
+          bfdcore_read (core_bfd, osec, &len, 
+			sizeof (len), &bfd_offset);
+	  len = netorder32 (len);
+
+          /* Get addr.  */
+          bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  addr = netorder64 (addr);
+
+          rec = record_mem_alloc (addr, len);
+
+          /* Get val.  */
+          bfdcore_read (core_bfd, osec, record_get_loc (rec),
+			rec->u.mem.len, &bfd_offset);
+          break;
+
+        case record_end: /* end */
+          if (record_debug)
+            printf_filtered ("\
+  Reading record_end (1 + %d + %d bytes), offset == %s\n",
+			     sizeof (signal),
+			     sizeof (count),
+			     paddress (get_current_arch (),
+				       bfd_offset));
+          rec = record_end_alloc ();
+          record_insn_num ++;
+
+	  /* Get signal value.  */
+	  bfdcore_read (core_bfd, osec, &signal, 
+			sizeof (signal), &bfd_offset);
+	  signal = netorder32 (signal);
+	  rec->u.end.sigval = signal;
+
+	  /* Get insn count.  */
+	  bfdcore_read (core_bfd, osec, &count, 
+			sizeof (count), &bfd_offset);
+	  count = netorder32 (count);
+	  rec->u.end.insn_num = count;
+	  record_insn_count = count + 1;
+          break;
+
+        default:
+          error (_("Bad entry type in core file %s."),
+		 bfd_get_filename (core_bfd));
+          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;
+  record_arch_list_tail->next = NULL;
+  record_list = &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.  */
+  printf_filtered (_("Restored records from core file %s.\n"),
+		   bfd_get_filename (core_bfd));
+
+  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);
@@ -728,6 +944,7 @@ record_core_open_1 (char *name, int from
     }
 
   push_target (&record_core_ops);
+  record_restore ();
 }
 
 static void
@@ -735,9 +952,6 @@ record_open_1 (char *name, int from_tty)
 {
   struct target_ops *t;
 
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
-
   /* check exec */
   if (!target_has_execution)
     error (_("Process record: the program is not being run."));
@@ -779,6 +993,12 @@ record_open (char *name, int from_tty)
     error (_("Process record target already running.  Use \"record stop\" to "
              "stop record target first."));
 
+  /* Reset */
+  record_insn_num = 0;
+  record_insn_count = 0;
+  record_list = &record_first;
+  record_list->next = NULL;
+
   /* Reset the tmp beneath pointers.  */
   tmp_to_resume_ops = NULL;
   tmp_to_resume = NULL;
@@ -822,17 +1042,6 @@ record_open (char *name, int from_tty)
   if (!tmp_to_xfer_partial)
     error (_("Could not find 'to_xfer_partial' method on the target stack."));
 
-  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_insn_count = 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;
@@ -844,6 +1053,11 @@ record_open (char *name, int from_tty)
   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 (current_target.to_stratum == core_stratum)
+    record_core_open_1 (name, from_tty);
+  else
+    record_open_1 (name, from_tty);
 }
 
 static void
@@ -1114,8 +1328,7 @@ record_wait (struct target_ops *ops,
 		    {
 		      if (record_debug)
 			fprintf_unfiltered (gdb_stdlog,
-					    "Process record: break "
-					    "at %s.\n",
+					    "Process record: break at %s.\n",
 					    paddress (gdbarch, tmp_pc));
 		      if (gdbarch_decr_pc_after_break (gdbarch)
 			  && execution_direction == EXEC_FORWARD
@@ -1190,8 +1403,7 @@ static void
 record_mourn_inferior (struct target_ops *ops)
 {
   if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: "
-			            "record_mourn_inferior\n");
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_mourn_inferior\n");
 
   unpush_target (&record_ops);
   target_mourn_inferior ();
@@ -1345,8 +1557,8 @@ record_xfer_partial (struct target_ops *
 	  record_list_release (record_arch_list_tail);
 	  if (record_debug)
 	    fprintf_unfiltered (gdb_stdlog,
-				_("Process record: failed to record "
-				  "execution log."));
+				"Process record: failed to record "
+				"execution log.");
 	  return -1;
 	}
       if (record_arch_list_add_end ())
@@ -1354,8 +1566,8 @@ record_xfer_partial (struct target_ops *
 	  record_list_release (record_arch_list_tail);
 	  if (record_debug)
 	    fprintf_unfiltered (gdb_stdlog,
-				_("Process record: failed to record "
-				  "execution log."));
+				"Process record: failed to record "
+				"execution log.");
 	  return -1;
 	}
       record_list->next = record_arch_list_head;
@@ -1642,6 +1854,297 @@ cmd_record_start (char *args, int from_t
   execute_command ("target record", from_tty);
 }
 
+/* Record log save-file format
+   Version 1 (never released)
+
+   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, see enum record_type).
+     record_reg:
+       1 byte:  record type (record_reg, see enum record_type).
+       8 bytes: register id (network byte order).
+       MAX_REGISTER_SIZE bytes: register value.
+     record_mem:
+       1 byte:  record type (record_mem, see enum record_type).
+       8 bytes: memory length (network byte order).
+       8 bytes: memory address (network byte order).
+       n bytes: memory value (n == memory length).
+
+   Version 2
+     4 bytes: magic number netorder32(0x20091016).
+       NOTE: be sure to change whenever this file format changes!
+
+   Records:
+     record_end:
+       1 byte:  record type (record_end, see enum record_type).
+       4 bytes: signal
+       4 bytes: instruction count
+     record_reg:
+       1 byte:  record type (record_reg, see enum record_type).
+       4 bytes: register id (network byte order).
+       n bytes: register value (n == actual register size).
+                (eg. 4 bytes for x86 general registers).
+     record_mem:
+       1 byte:  record type (record_mem, see enum record_type).
+       4 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 void
+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;
+  else
+    error (_("Failed to write %d bytes to core file %s ('%s').\n"),
+	   len, bfd_get_filename (obfd),
+	   bfd_errmsg (bfd_get_error ()));
+}
+
+/* Restore the execution log from a file.  We use a modified elf
+   corefile format, with an extra section for our data.  */
+
+static void
+cmd_record_restore (char *args, int from_tty)
+{
+  core_file_command (args, from_tty);
+  record_open (args, from_tty);
+}
+
+static void
+record_save_cleanups (void *data)
+{
+  bfd *obfd = data;
+  char *pathname = xstrdup (bfd_get_filename (obfd));
+  bfd_close (obfd);
+  unlink (pathname);
+  xfree (pathname);
+}
+
+/* Save the execution log to a file.  We use a modified elf corefile
+   format, with an extra section for our data.  */
+
+static void
+cmd_record_save (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 save_size = 0;
+  asection *osec = NULL;
+  int bfd_offset = 0;
+
+  if (strcmp (current_target.to_shortname, "record") != 0)
+    error (_("This command can only be used with target 'record'.\n"
+	     "Use 'target record' first.\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 save file.  */
+  if (record_debug)
+    printf_filtered ("Saving execution log to core file '%s'\n", recfilename);
+
+  /* Open the output file.  */
+  obfd = create_gcore_bfd (recfilename);
+  old_cleanups = make_cleanup (record_save_cleanups, 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_insn (regcache, gdbarch, record_list);
+
+      if (record_list->prev)
+        record_list = record_list->prev;
+    }
+
+  /* Compute the size needed for the extra bfd section.  */
+  save_size = 4;	/* magic cookie */
+  for (record_list = record_first.next; record_list;
+       record_list = record_list->next)
+    switch (record_list->type)
+      {
+      case record_end:
+	save_size += 1 + 4 + 4;
+	break;
+      case record_reg:
+	save_size += 1 + 4 + record_list->u.reg.len;
+	break;
+      case record_mem:
+	save_size += 1 + 4 + 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: %s"),
+	   recfilename,
+           bfd_errmsg (bfd_get_error ()));
+  bfd_set_section_size (obfd, osec, save_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)
+    printf_filtered ("\
+  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
+		     magic);
+  bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
+
+  /* Save the entries to recfd and forward execute to the end of
+     record list.  */
+  record_list = &record_first;
+  while (1)
+    {
+      /* Save entry.  */
+      if (record_list != &record_first)
+        {
+	  uint8_t type;
+	  uint32_t regnum, len, signal, count;
+          uint64_t addr;
+
+	  type = record_list->type;
+          bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
+
+          switch (record_list->type)
+            {
+            case record_reg: /* reg */
+              if (record_debug)
+		printf_filtered ("\
+  Writing register %d (1 plus %d plus %d bytes)\n",
+				 record_list->u.reg.num,
+				 sizeof (regnum),
+				 record_list->u.reg.len);
+
+              /* Write regnum.  */
+              regnum = netorder32 (record_list->u.reg.num);
+              bfdcore_write (obfd, osec, &regnum,
+			     sizeof (regnum), &bfd_offset);
+
+              /* Write regval.  */
+              bfdcore_write (obfd, osec, record_get_loc (record_list),
+			     record_list->u.reg.len, &bfd_offset);
+              break;
+
+            case record_mem: /* mem */
+	      if (record_debug)
+		printf_filtered ("\
+  Writing memory %s (1 plus %d plus %d plus %d bytes)\n",
+				 paddress (gdbarch,
+					   record_list->u.mem.addr),
+				 sizeof (addr),
+				 sizeof (len),
+				 record_list->u.mem.len);
+
+	      /* Write memlen.  */
+	      len = netorder32 (record_list->u.mem.len);
+	      bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
+
+	      /* Write memaddr.  */
+	      addr = netorder64 (record_list->u.mem.addr);
+	      bfdcore_write (obfd, osec, &addr, 
+			     sizeof (addr), &bfd_offset);
+
+	      /* Write memval.  */
+	      bfdcore_write (obfd, osec, record_get_loc (record_list),
+			     record_list->u.mem.len, &bfd_offset);
+              break;
+
+              case record_end:
+                if (record_debug)
+                  printf_filtered ("\
+  Writing record_end (1 + %d + %d bytes)\n", 
+				   sizeof (signal),
+				   sizeof (count));
+		/* Write signal value.  */
+		signal = netorder32 (record_list->u.end.sigval);
+		bfdcore_write (obfd, osec, &signal,
+			       sizeof (signal), &bfd_offset);
+
+		/* Write insn count.  */
+		count = netorder32 (record_list->u.end.insn_num);
+		bfdcore_write (obfd, osec, &count,
+			       sizeof (count), &bfd_offset);
+                break;
+            }
+        }
+
+      /* Execute entry.  */
+      record_exec_insn (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_insn (regcache, gdbarch, record_list);
+
+      if (record_list->prev)
+        record_list = record_list->prev;
+    }
+
+  do_cleanups (set_cleanups);
+  do_cleanups (old_cleanups);
+
+  /* Succeeded.  */
+  printf_filtered (_("Saved core file %s with execution log.\n"),
+		   recfilename);
+}
+
 /* Truncate the record log from the present point
    of replay until the end.  */
 
@@ -1749,6 +2252,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;
@@ -1767,10 +2272,12 @@ _initialize_record (void)
 			    NULL, show_record_debug, &setdebuglist,
 			    &showdebuglist);
 
-  add_prefix_cmd ("record", class_obscure, cmd_record_start,
+  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,
 		  "set record ", 0, &setlist);
@@ -1784,6 +2291,17 @@ _initialize_record (void)
 		  "info record ", 0, &infolist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
 
+  c = add_cmd ("save", class_obscure, cmd_record_save,
+	       _("Save the execution log to a file.\n\
+Argument is optional filename.\n\
+Default filename is 'gdb_record.<process_id>'."),
+	       &record_cmdlist);
+
+  c = add_cmd ("restore", class_obscure, cmd_record_restore,
+	       _("Restore the execution log from a file.\n\
+Argument is filename.  File must be created with 'record save'."),
+	       &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."),

  reply	other threads:[~2009-10-17 22:19 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-17  1:32 Michael Snyder
2009-10-17  8:31 ` Eli Zaretskii
2009-10-17 18:42   ` Michael Snyder
2009-10-17 20:11     ` Eli Zaretskii
2009-10-17 22:19       ` Michael Snyder [this message]
2009-10-18  2:23         ` Hui Zhu
2009-10-18  3:59           ` Michael Snyder
2009-10-18  4:06         ` Eli Zaretskii
2009-10-18  4:10           ` Michael Snyder
2009-10-19  4:34             ` Hui Zhu
2009-10-19 18:00               ` Michael Snyder
2009-10-20  2:47                 ` Hui Zhu
2009-10-20 20:03                   ` Michael Snyder
2009-10-20 20:05                     ` Michael Snyder
2009-10-21  2:36                       ` Hui Zhu
2009-10-22 19:42                         ` Michael Snyder
2009-10-22 20:40                           ` Paul Pluzhnikov
2009-10-22 21:00                             ` Michael Snyder
2009-10-22 21:25                               ` Paul Pluzhnikov
2009-10-23  0:45                                 ` Paul Pluzhnikov
2009-10-23  1:05                                   ` Paul Pluzhnikov
2009-10-23  5:35                                     ` Hui Zhu
2009-10-23  8:52                                     ` Pierre Muller
2009-10-23 10:08                                       ` Eli Zaretskii
2009-10-23 14:42                                         ` Hui Zhu
2009-10-23 14:54                                           ` Pedro Alves
2009-10-31 14:57                                             ` Pedro Alves
2009-11-01  9:54                                               ` Hui Zhu
2009-10-23 14:58                                           ` Pedro Alves
2009-10-23 14:46                                         ` Andreas Schwab
2009-10-23 14:55                                           ` Eli Zaretskii
2009-10-23 15:35                                             ` Andreas Schwab
2009-10-23 15:48                                               ` Eli Zaretskii
2009-10-23 16:31                                                 ` Andreas Schwab
2009-10-23 15:09                                         ` Pierre Muller
2009-10-23  5:35                           ` Hui Zhu
2009-10-23 15:56                             ` Michael Snyder
2009-10-23 16:01                               ` 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=4ADA4191.4060404@vmware.com \
    --to=msnyder@vmware.com \
    --cc=eliz@gnu.org \
    --cc=gdb-patches@sourceware.org \
    --cc=teawater@gmail.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