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]

[RFA/RFC] Add dump and load command to process record and replay


Hi,

This patch add command record dump and record load to make prec can
dump execution log to a file and load it later.  And load can work
with core file.

And I include "mem_entry_not_accessible"(not_replay) patch to this
patch because when replay with core file, some address in lib cannot
be read and write.

For example:
./gdb ./a.out
GNU gdb (GDB) 6.8.50.20090801-cvs
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Setting up the environment for debugging gdb.
Function "internal_error" not defined.
Make breakpoint pending on future shared library load? (y or [n])
[answered N; input not from terminal]
Function "info_command" not defined.
Make breakpoint pending on future shared library load? (y or [n])
[answered N; input not from terminal]
/home/teawater/gdb/bgdbno/gdb/.gdbinit:8: Error in sourced command file:
No breakpoint number 0.
(gdb) start
Temporary breakpoint 1 at 0x80483c1: file 1.c, line 20.
Starting program: /home/teawater/gdb/bgdbno/gdb/a.out

Temporary breakpoint 1, main () at 1.c:20
20	       int     b = 0;
(gdb) gcore
Saved corefile core.11945
(gdb) record
(gdb) n
During symbol reading, incomplete CFI data; unspecified registers
(e.g., eax) at 0x80483be.
21	       int     c = 1;
(gdb)
24		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
a = 0 b = 0 c = 1
25	       b = cool ();
(gdb)
a = 3
26		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
a = 3 b = 3 c = 1
29	       c += 1;
(gdb)
30		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
a = 3 b = 3 c = 2
31	       a -= 2;
(gdb)
32		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
a = 1 b = 3 c = 2
35	return (0);
(gdb)
36	}
(gdb) rc
Continuing.

No more reverse-execution history.
main () at 1.c:20
20	       int     b = 0;
(gdb) record dump
Saved recfile rec.11945.
(gdb) quit


After that, you get core.11945 and rec.11945.
Then you can replay 11945 execution log with them.


./gdb ./a.out ./core.11945
GNU gdb (GDB) 6.8.50.20090801-cvs
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `/home/teawater/gdb/bgdbno/gdb/a.out'.
Program terminated with signal 5, Trace/breakpoint trap.
#0  main () at 1.c:20
20	       int     b = 0;
Setting up the environment for debugging gdb.
Function "internal_error" not defined.
Make breakpoint pending on future shared library load? (y or [n])
[answered N; input not from terminal]
Function "info_command" not defined.
Make breakpoint pending on future shared library load? (y or [n])
[answered N; input not from terminal]
/home/teawater/gdb/bgdbno/gdb/.gdbinit:8: Error in sourced command file:
No breakpoint number 0.
(gdb) record load ./rec.11945
Auto start process record.
Loaded recfile ./rec.11945.
(gdb) p b
During symbol reading, incomplete CFI data; unspecified registers
(e.g., eax) at 0x80483be.
$1 = -1208017488
(gdb) n
21	       int     c = 1;
(gdb) p b
$2 = 0
(gdb) n
24		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
25	       b = cool ();
(gdb) p b
$3 = 0
(gdb) n
26		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb)
29	       c += 1;
(gdb) p c
$4 = 1
(gdb) n
30		printf ("a = %d b = %d c = %d\n", a, b, c);
(gdb) p c
$5 = 2
(gdb) c
Continuing.

No more reverse-execution history.
main () at 1.c:36
36	}
(gdb) rc
Continuing.

No more reverse-execution history.
main () at 1.c:20
20	       int     b = 0;
(gdb) p c
$6 = 134513849
(gdb) quit
The program is running.  Quit anyway (and kill it)? (y or n) y


Please send your comments about it.  Thanks a lot.

Hui

2009-08-01  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,
        byteswap.h, netinet/in.h): Include files.
	(RECORD_IS_REPLAY): Return true if record_core is true.
	(RECORD_FILE_MAGIC): New macro.
	(record_mem_entry): New field 'mem_entry_not_accessible'.
	(record_core_buf_entry): New struct.
	(record_core, record_core_regbuf, record_core_start,
	record_core_end, record_core_buf_list,
	record_beneath_to_fetch_registers_ops,
	record_beneath_to_fetch_registers,
	record_beneath_to_store_registers_ops,
	record_beneath_to_store_registers,
	record_beneath_to_has_execution_ops,
	record_beneath_to_has_execution,
	record_beneath_to_prepare_to_store): New variables.
	(record_list_release_first_insn): Change function
	record_list_release_first to this name.
	(record_arch_list_add_mem): Initialize
	mem_entry_not_accessible to 0.
	(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): New function.
	(record_open): Add support for target core.
	(record_close): Add support for target core.
	(record_wait): Call record_exec_entry.
	(record_kill): Add support for target core.
	(record_registers_change): Call record_list_release_first_insn.
	(record_fetch_registers): New function.
	(record_prepare_to_store): New function.
	(record_store_registers): Add support for target core.
	(record_xfer_partial): Add support for target core.
	(record_has_execution): New function.
	(init_record_ops): Set record_ops.to_fetch_registers,
	record_ops.to_prepare_to_store
	and record_ops.to_has_execution.
	(cmd_record_fd_cleanups): New function.
	(cmd_record_dump): New function.
	(cmd_record_load): New function.
	(set_record_insn_max_num): Call record_list_release_first_insn.
	(_initialize_record): Add commands "record dump"
	and "record load".

---
 record.c |  709 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 641 insertions(+), 68 deletions(-)

--- a/record.c
+++ b/record.c
@@ -23,14 +23,22 @@
 #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 <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)
+     (record_list->next || execution_direction == EXEC_REVERSE || record_core)
+
+#define RECORD_FILE_MAGIC	htonl(0x20090726)

 /* These are the core struct of record function.

@@ -51,6 +59,7 @@ struct record_mem_entry
 {
   CORE_ADDR addr;
   int len;
+  int mem_entry_not_accessible;
   gdb_byte *val;
 };

@@ -75,9 +84,23 @@ 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 with core target.  */
+static int record_core = 0;
+static gdb_byte *record_core_regbuf;
+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;
@@ -100,6 +123,14 @@ static struct target_ops *record_beneath
 static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
 					 struct target_waitstatus *,
 					 int);
+static struct target_ops *record_beneath_to_fetch_registers_ops;
+static void (*record_beneath_to_fetch_registers) (struct target_ops *,
+                                                  struct regcache *,
+						  int regno);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+                                                  struct regcache *,
+						  int regno);
 static struct target_ops *record_beneath_to_store_registers_ops;
 static void (*record_beneath_to_store_registers) (struct target_ops *,
                                                   struct regcache *,
@@ -116,6 +147,9 @@ static int (*record_beneath_to_insert_br
 						   struct bp_target_info *);
 static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
 						   struct bp_target_info *);
+static struct target_ops *record_beneath_to_has_execution_ops;
+static int (*record_beneath_to_has_execution) (struct target_ops *ops);
+static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache);

 static void
 record_list_release (struct record_entry *rec)
@@ -166,7 +200,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;
@@ -275,6 +309,7 @@ record_arch_list_add_mem (CORE_ADDR addr
   rec->type = record_mem;
   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))
     {
@@ -336,30 +371,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;
@@ -382,7 +417,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++;

@@ -412,6 +447,92 @@ 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)
+          record_list->u.mem.mem_entry_not_accessible = 0;
+        else
+          {
+            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 && !record_core)
+                    || (execution_direction != EXEC_REVERSE && record_core))
+                  {
+                    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 && !record_core)
+                        || (execution_direction != EXEC_REVERSE &&
record_core))
+                      {
+                        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);
+                  }
+              }
+
+            memcpy (entry->u.mem.val, mem, entry->u.mem.len);
+          }
+      }
+      break;
+    }
+}
+
 static void
 record_open (char *name, int from_tty)
 {
@@ -420,8 +541,13 @@ record_open (char *name, int from_tty)
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");

+  if (!strcmp (current_target.to_shortname, "core"))
+    record_core = 1;
+  else
+    record_core = 0;
+
   /* check exec */
-  if (!target_has_execution)
+  if (!target_has_execution && !record_core)
     error (_("Process record: the program is not being run."));
   if (non_stop)
     error (_("Process record target can't debug inferior in non-stop mode "
@@ -450,6 +576,8 @@ record_open (char *name, int from_tty)
   record_beneath_to_xfer_partial = NULL;
   record_beneath_to_insert_breakpoint = NULL;
   record_beneath_to_remove_breakpoint = NULL;
+  record_beneath_to_has_execution = NULL;
+  record_beneath_to_prepare_to_store = NULL;

   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -464,6 +592,11 @@ record_open (char *name, int from_tty)
 	  record_beneath_to_wait = t->to_wait;
 	  record_beneath_to_wait_ops = t;
         }
+      if (!record_beneath_to_fetch_registers)
+        {
+	  record_beneath_to_fetch_registers = t->to_fetch_registers;
+	  record_beneath_to_fetch_registers_ops = t;
+        }
       if (!record_beneath_to_store_registers)
         {
 	  record_beneath_to_store_registers = t->to_store_registers;
@@ -478,19 +611,51 @@ record_open (char *name, int from_tty)
 	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 (!record_beneath_to_has_execution)
+        {
+          record_beneath_to_has_execution_ops = t;
+	  record_beneath_to_has_execution = t->to_has_execution;
+        }
+      if (!record_beneath_to_prepare_to_store)
+	record_beneath_to_prepare_to_store = t->to_prepare_to_store;
     }
-  if (!record_beneath_to_resume)
+  if (!record_beneath_to_resume && !record_core)
     error (_("Process record can't get to_resume."));
-  if (!record_beneath_to_wait)
+  if (!record_beneath_to_wait && !record_core)
     error (_("Process record can't get to_wait."));
-  if (!record_beneath_to_store_registers)
+  if (!record_beneath_to_fetch_registers)
+    error (_("Process record can't get to_fetch_registers."));
+  if (!record_beneath_to_store_registers && !record_core)
     error (_("Process record can't get to_store_registers."));
   if (!record_beneath_to_xfer_partial)
     error (_("Process record can't get to_xfer_partial."));
-  if (!record_beneath_to_insert_breakpoint)
+  if (!record_beneath_to_insert_breakpoint && !record_core)
     error (_("Process record can't get to_insert_breakpoint."));
-  if (!record_beneath_to_remove_breakpoint)
+  if (!record_beneath_to_remove_breakpoint && !record_core)
     error (_("Process record can't get to_remove_breakpoint."));
+  if (!record_beneath_to_has_execution && !record_core)
+    error (_("Process record can't get to_has_execution."));
+  if (!record_beneath_to_prepare_to_store && !record_core)
+    error (_("Process record can't get to_prepare_to_store."));
+
+  if (record_core)
+    {
+      /* Get record_core_regbuf.  */
+      struct regcache *regcache = get_current_regcache ();
+      int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+      int i;
+
+      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))
+        error (_("\"%s\": Can't find sections: %s"),
+               bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+    }

   push_target (&record_ops);

@@ -503,10 +668,26 @@ record_open (char *name, int from_tty)
 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.  */
+  xfree (record_core_regbuf);
+
+  /* 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;
@@ -708,53 +889,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 */
-	      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))
-		error (_("Process record: error reading memory at "
-			 "addr = %s len = %d."),
-		       paddress (gdbarch, record_list->u.mem.addr),
-		       record_list->u.mem.len);
-
-	      if (target_write_memory
-		  (record_list->u.mem.addr, record_list->u.mem.val,
-		   record_list->u.mem.len))
-		error (_
-		       ("Process record: error writing memory at "
-			"addr = %s len = %d."),
-		       paddress (gdbarch, record_list->u.mem.addr),
-		       record_list->u.mem.len);
+          record_exec_entry (regcache, gdbarch, record_list);

-	      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,
@@ -874,7 +1011,9 @@ record_kill (struct target_ops *ops)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");

   unpush_target (&record_ops);
-  target_kill ();
+
+  if (!record_core)
+    target_kill ();
 }

 /* Record registers change (by user or by GDB) to list as an instruction.  */
@@ -918,15 +1057,58 @@ 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++;
 }

 static void
+record_fetch_registers (struct target_ops *ops, struct regcache *regcache,
+                        int regno)
+{
+  if (record_core)
+    {
+      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);
+    }
+  else
+    record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops,
+                                       regcache, regno);
+}
+
+static void
+record_prepare_to_store (struct regcache *regcache)
+{
+  if (!record_core)
+    record_beneath_to_prepare_to_store (regcache);
+}
+
+static void
 record_store_registers (struct target_ops *ops, struct regcache *regcache,
                         int regno)
 {
+  if (record_core)
+    {
+      /* Debug with core.  */
+      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."));
+
+      return;
+    }
+
   if (!record_gdb_operation_disable)
     {
       if (RECORD_IS_REPLAY)
@@ -973,6 +1155,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);
 }
@@ -988,7 +1171,7 @@ record_xfer_partial (struct target_ops *
 {
   if (!record_gdb_operation_disable
       && (object == TARGET_OBJECT_MEMORY
-	  || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+	  || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core)
     {
       if (RECORD_IS_REPLAY)
 	{
@@ -1032,11 +1215,91 @@ 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++;
     }

+   if (record_core && object == TARGET_OBJECT_MEMORY)
+     {
+       /* Debug with core.  */
+       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 0;
+         }
+       else
+         error (_("You can't do that without a process to debug."));
+
+       return 0;
+     }
+
   return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
                                          object, annex, readbuf, writebuf,
                                          offset, len);
@@ -1086,6 +1349,15 @@ record_can_execute_reverse (void)
   return 1;
 }

+int
+record_has_execution (struct target_ops *ops)
+{
+  if (record_core)
+    return 1;
+
+  return record_beneath_to_has_execution (ops);
+}
+
 static void
 init_record_ops (void)
 {
@@ -1102,11 +1374,14 @@ init_record_ops (void)
   record_ops.to_mourn_inferior = record_mourn_inferior;
   record_ops.to_kill = record_kill;
   record_ops.to_create_inferior = find_default_create_inferior;
+  record_ops.to_fetch_registers = record_fetch_registers;
+  record_ops.to_prepare_to_store = record_prepare_to_store;
   record_ops.to_store_registers = record_store_registers;
   record_ops.to_xfer_partial = record_xfer_partial;
   record_ops.to_insert_breakpoint = record_insert_breakpoint;
   record_ops.to_remove_breakpoint = record_remove_breakpoint;
   record_ops.to_can_execute_reverse = record_can_execute_reverse;
+  record_ops.to_has_execution = record_has_execution;
   record_ops.to_stratum = record_stratum;
   record_ops.to_magic = OPS_MAGIC;
 }
@@ -1127,6 +1402,293 @@ cmd_record_start (char *args, int from_t
   execute_command ("target record", from_tty);
 }

+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+  int recfd = *(int *) recfdp;
+  close (recfd);
+}
+
+/* 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;
+
+  if (current_target.to_stratum != record_stratum)
+    error (_("Process record is not started.\n"));
+
+  if (args && *args)
+    recfilename = args;
+  else
+    {
+      /* Default corefile name is "rec.PID".  */
+      sprintf (recfilename_buffer, "rec.%d", PIDGET (inferior_ptid));
+      recfilename = recfilename_buffer;
+    }
+
+  /* Open the dump file.  */
+  recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+                S_IRUSR | S_IWUSR);
+  if (recfd < 0)
+    error (_("Failed to open '%s' for dump."), recfilename);
+  old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+  /* 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 ();
+
+  /* Write the magic code.  */
+  magic = RECORD_FILE_MAGIC;
+  if (write (recfd, &magic, 4) != 4)
+    error (_("Failed to write '%s' for dump."), recfilename);
+
+  /* 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;
+    }
+
+  /* Dump the entries to recfd and forward execute to the end of
+     record list.  */
+  while (1)
+    {
+      /* Dump entry.  */
+      if (record_list != &record_first)
+        {
+          uint8_t tmpu8;
+          uint64_t tmpu64;
+
+          tmpu8 = record_list->type;
+          if (write (recfd, &tmpu8, 1) != 1)
+            error (_("Failed to write '%s' for dump."), recfilename);
+
+          switch (record_list->type)
+            {
+            case record_reg: /* reg */
+              tmpu64 = record_list->u.reg.num;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+              tmpu64 = bswap_64 (tmpu64);
+#endif
+              if (write (recfd, &tmpu64, 8) != 8)
+                error (_("Failed to write '%s' for dump."), recfilename);
+
+              if (write (recfd, record_list->u.reg.val,
+                         MAX_REGISTER_SIZE) != MAX_REGISTER_SIZE)
+                error (_("Failed to write '%s' for dump."), recfilename);
+              break;
+            case record_mem: /* mem */
+              if (!record_list->u.mem.mem_entry_not_accessible)
+                {
+                  tmpu64 = record_list->u.mem.addr;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+                  tmpu64 = bswap_64 (tmpu64);
+#endif
+                  if (write (recfd, &tmpu64, 8) != 8)
+                    error (_("Failed to write '%s' for dump."), recfilename);
+
+                  tmpu64 = record_list->u.mem.len;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+                  tmpu64 = bswap_64 (tmpu64);
+#endif
+                  if (write (recfd, &tmpu64, 8) != 8)
+                    error (_("Failed to write '%s' for dump."), recfilename);
+
+                  if (write (recfd, record_list->u.mem.val,
+                             record_list->u.mem.len) != record_list->u.mem.len)
+                    error (_("Failed to write '%s' for dump."), recfilename);
+                }
+              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);
+}
+
+/* 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;
+
+  if (current_target.to_stratum != record_stratum)
+    {
+      cmd_record_start (NULL, from_tty);
+      printf_unfiltered (_("Auto start process record.\n"));
+    }
+
+  if (!args || (args && !*args))
+    error (_("Argument for filename required.\n"));
+
+  /* Open the load file.  */
+  recfd = open (args, O_RDONLY | O_BINARY);
+  if (recfd < 0)
+    error (_("Failed to open '%s' for load."), args);
+  old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+  /* Check the magic code.  */
+  if (read (recfd, &magic, 4) != 4)
+    error (_("Failed to read '%s' for load."), args);
+  if (magic != RECORD_FILE_MAGIC)
+    error (_("'%s' is not a record dump."), args);
+
+  /* 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;
+  old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0);
+
+  while (1)
+    {
+      int ret;
+      uint8_t tmpu8;
+      uint64_t tmpu64;
+
+      ret = read (recfd, &tmpu8, 1);
+      if (ret < 0)
+        error (_("Failed to read '%s' for load."), args);
+      if (ret == 0)
+        break;
+
+      switch (tmpu8)
+        {
+        case record_reg: /* reg */
+          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;
+          /* Get num.  */
+          if (read (recfd, &tmpu64, 8) != 8)
+            error (_("Failed to read '%s' for load."), args);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+          tmpu64 = bswap_64 (tmpu64);
+#endif
+          rec->u.reg.num = tmpu64;
+          /* Get val.  */
+          if (read (recfd, rec->u.reg.val,
+                    MAX_REGISTER_SIZE) != MAX_REGISTER_SIZE)
+            error (_("Failed to read '%s' for load."), args);
+          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 (read (recfd, &tmpu64, 8) != 8)
+            error (_("Failed to read '%s' for load."), args);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+          tmpu64 = bswap_64 (tmpu64);
+#endif
+          rec->u.mem.addr = tmpu64;
+          /* Get len.  */
+          if (read (recfd, &tmpu64, 8) != 8)
+            error (_("Failed to read '%s' for load."), args);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+          tmpu64 = bswap_64 (tmpu64);
+#endif
+          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 (read (recfd, rec->u.mem.val,
+                    rec->u.mem.len) != rec->u.mem.len)
+            error (_("Failed to read '%s' for load."), args);
+          record_arch_list_add (rec);
+          break;
+
+        case record_end: /* end */
+          rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+          rec->prev = NULL;
+          rec->next = NULL;
+          rec->type = record_end;
+          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.  */
+  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);
+}
+
 /* Truncate the record log from the present point
    of replay until the end.  */

@@ -1177,7 +1739,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 ();
     }
 }

@@ -1217,6 +1779,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 +1814,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."),


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

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

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

--- a/doc/gdb.texinfo
+++ b/doc/gdb.texinfo
@@ -5190,6 +5190,22 @@ 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}]
+Produce a record execution log of the inferior process.  The optional
+argument @var{file} specifies the file name where to put the record dump.
+If not specified, the file name defaults to @file{rec.@var{pid}}, where
+@var{pid} is the inferior process ID.
+
+@kindex record load
+@kindex rec load
+@item record load @var{file}
+@itemx rec dump @var{file}
+Load process record execution log from @var{file}.
+It can work with @code{core-file}.
 @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]