[Discussion/Prec] The record speed up plan (Make speed like without prec)

Hui Zhu teawater@gmail.com
Wed May 19 07:19:00 GMT 2010


Hi,

This is a demo.

Still not support segment register, system call and some others.

Thanks,
Hui

On Fri, Apr 30, 2010 at 21:23, Hui Zhu <teawater@gmail.com> wrote:
> Hello,
>
> I think the record speed is the biggest trouble of prec.
> After I did a long think and a lot of test around with it.  I got a
> idea.  Actually, I have began the code work.
>
> I found that the big trouble is prec let the inferior just step.  It
> make inferior speed very low.  Because the setp need a lot of context
> works.
> So I think let inferior continue can make it speed up.  But How to
> record the change of each step?
>
> Some physicists said all the things in the world have execution rules.
>  So use the current stat of this thing, we will get what will happen
> in the future.  Looks most of rules are still not found.  :)
>
> But lucky for us that insns exec rules we know.  So most of insns
> (There a some special, I will talk it later), if we have the a
> inferior value(memory and reg), we can get the each value of next
> insn.
> So if we can record the all the value of a inferior A(or all the value
> that will be change, but to get it will need parse the insns that will
> be exec, this is not easy.) , we can let the inferior exec without
> step.  If the user want reverse exec, get the each step value from A.
> Then the record speed will very faster than before.
>
> But this way have a 2 question.
> 1.  How to record all the status of a inferior? For the linux,
> checkpoint already use fork to record the inferior.  So prec will use
> it too.
> And when we want get the old status of inferior step by step, we can
> let the forked process step by step.  That will easy by parse the insn
> and know what will happen.
>
> 2.  How to handle special insns that we will not know what will happen
> after it exec?
> The first type of this insns is system call.  Linux have catchpoint
> that make inferior stop before and after syscall.  Then we can record
> the change after the system call.
> The other insn is like rdtsc, I don't know howto get the feature value
> of this type.  My current idea with them is give up all the record
> after this insn.
> If user need, insert special breakpoint for it.  Next time, inferior
> will stop on this insn, then prec can record the value after it exec.
>
> BTW, I call this new function pre_record, I agree with you if you said
> this name is ugly. :)
>
> Please tell me your opinions about my idea.  That will help me a lot.  Thanks.
>
> Thanks,
> Hui
>
-------------- next part --------------
---
 infrun.c |    2 +-
 record.c |   38 ++++++++++++++++++++------------------
 record.h |    2 +-
 3 files changed, 22 insertions(+), 20 deletions(-)

--- a/infrun.c
+++ b/infrun.c
@@ -2731,7 +2731,7 @@ adjust_pc_after_break (struct execution_
       struct cleanup *old_cleanups = NULL;
 
       if (RECORD_IS_USED)
-	old_cleanups = record_gdb_operation_disable_set ();
+	old_cleanups = record_disable_set ();
 
       /* When using hardware single-step, a SIGTRAP is reported for both
 	 a completed single-step and a software breakpoint.  Need to
--- a/record.c
+++ b/record.c
@@ -671,16 +671,16 @@ record_message_wrapper_safe (struct regc
 /* Set to 1 if record_store_registers and record_xfer_partial
    doesn't need record.  */
 
-static int record_gdb_operation_disable = 0;
+static int record_disable = 0;
 
 struct cleanup *
-record_gdb_operation_disable_set (void)
+record_disable_set (void)
 {
   struct cleanup *old_cleanups = NULL;
 
   old_cleanups =
-    make_cleanup_restore_integer (&record_gdb_operation_disable);
-  record_gdb_operation_disable = 1;
+    make_cleanup_restore_integer (&record_disable);
+  record_disable = 1;
 
   return old_cleanups;
 }
@@ -1007,7 +1007,8 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      record_message (get_current_regcache (), signal);
+      if (!record_disable)
+        record_message (get_current_regcache (), signal);
       record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
                                 signal);
     }
@@ -1061,8 +1062,6 @@ record_wait (struct target_ops *ops,
 	     ptid_t ptid, struct target_waitstatus *status,
 	     int options)
 {
-  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
-
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog,
 			"Process record: record_wait "
@@ -1071,9 +1070,9 @@ record_wait (struct target_ops *ops,
 
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_step)
+      if (record_resume_step || record_disable)
 	{
-	  /* This is a single step.  */
+	  /* This is a single step or record is disabled.  */
 	  return record_beneath_to_wait (record_beneath_to_wait_ops,
 					 ptid, status, options);
 	}
@@ -1082,6 +1081,7 @@ record_wait (struct target_ops *ops,
 	  /* This is not a single step.  */
 	  ptid_t ret;
 	  CORE_ADDR tmp_pc;
+          struct cleanup *set_cleanups = record_disable_set ();
 
 	  while (1)
 	    {
@@ -1145,6 +1145,7 @@ record_wait (struct target_ops *ops,
 	      break;
 	    }
 
+          do_cleanups (set_cleanups);
 	  return ret;
 	}
     }
@@ -1157,6 +1158,7 @@ record_wait (struct target_ops *ops,
       int first_record_end = 1;
       struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
       CORE_ADDR tmp_pc;
+      struct cleanup *set_cleanups = record_disable_set ();
 
       record_hw_watchpoint = 0;
       status->kind = TARGET_WAITKIND_STOPPED;
@@ -1309,10 +1311,10 @@ replay_out:
       else
 	status->value.sig = TARGET_SIGNAL_TRAP;
 
+      do_cleanups (set_cleanups);
       discard_cleanups (old_cleanups);
     }
 
-  do_cleanups (set_cleanups);
   return inferior_ptid;
 }
 
@@ -1436,7 +1438,7 @@ static void
 record_store_registers (struct target_ops *ops, struct regcache *regcache,
                         int regno)
 {
-  if (!record_gdb_operation_disable)
+  if (!record_disable)
     {
       if (RECORD_IS_REPLAY)
 	{
@@ -1495,7 +1497,7 @@ record_xfer_partial (struct target_ops *
 		     const char *annex, gdb_byte *readbuf,
 		     const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
 {
-  if (!record_gdb_operation_disable
+  if (!record_disable
       && (object == TARGET_OBJECT_MEMORY
 	  || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
     {
@@ -1561,7 +1563,7 @@ record_insert_breakpoint (struct gdbarch
 {
   if (!RECORD_IS_REPLAY)
     {
-      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
+      struct cleanup *old_cleanups = record_disable_set ();
       int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
 
       do_cleanups (old_cleanups);
@@ -1580,7 +1582,7 @@ record_remove_breakpoint (struct gdbarch
 {
   if (!RECORD_IS_REPLAY)
     {
-      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
+      struct cleanup *old_cleanups = record_disable_set ();
       int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
 
       do_cleanups (old_cleanups);
@@ -1735,7 +1737,7 @@ record_core_store_registers (struct targ
                              struct regcache *regcache,
                              int regno)
 {
-  if (record_gdb_operation_disable)
+  if (record_disable)
     regcache_raw_collect (regcache, regno,
                           record_core_regbuf + MAX_REGISTER_SIZE * regno);
   else
@@ -1752,7 +1754,7 @@ record_core_xfer_partial (struct target_
 {
   if (object == TARGET_OBJECT_MEMORY)
     {
-      if (record_gdb_operation_disable || !writebuf)
+      if (record_disable || !writebuf)
 	{
 	  struct target_section *p;
 
@@ -2375,7 +2377,7 @@ cmd_record_save (char *args, int from_tt
   gdbarch = get_regcache_arch (regcache);
 
   /* Disable the GDB operation record.  */
-  set_cleanups = record_gdb_operation_disable_set ();
+  set_cleanups = record_disable_set ();
 
   /* Reverse execute to the begin of record list.  */
   while (1)
@@ -2549,7 +2551,7 @@ static void
 record_goto_insn (struct record_entry *entry,
 		  enum exec_direction_kind dir)
 {
-  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct cleanup *set_cleanups = record_disable_set ();
   struct regcache *regcache = get_current_regcache ();
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
--- a/record.h
+++ b/record.h
@@ -27,6 +27,6 @@ extern int record_debug;
 extern int record_arch_list_add_reg (struct regcache *regcache, int num);
 extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
 extern int record_arch_list_add_end (void);
-extern struct cleanup *record_gdb_operation_disable_set (void);
+extern struct cleanup *record_disable_set (void);
 
 #endif /* _RECORD_H_ */
-------------- next part --------------
---
 gdbarch.c         |   33 ++++++
 gdbarch.h         |    8 +
 gdbarch.sh        |    3 
 i386-linux-tdep.c |    1 
 linux-nat.c       |   17 ++-
 linux-record.c    |   68 +++++++++++++
 linux-record.h    |    2 
 record.c          |  267 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 record.h          |    2 
 9 files changed, 378 insertions(+), 23 deletions(-)

--- a/gdbarch.c
+++ b/gdbarch.c
@@ -251,6 +251,7 @@ struct gdbarch
   int sofun_address_maybe_missing;
   gdbarch_process_record_ftype *process_record;
   gdbarch_process_record_signal_ftype *process_record_signal;
+  gdbarch_pre_process_record_ftype *pre_process_record;
   gdbarch_target_signal_from_host_ftype *target_signal_from_host;
   gdbarch_target_signal_to_host_ftype *target_signal_to_host;
   gdbarch_get_siginfo_type_ftype *get_siginfo_type;
@@ -398,6 +399,7 @@ struct gdbarch startup_gdbarch =
   0,  /* sofun_address_maybe_missing */
   0,  /* process_record */
   0,  /* process_record_signal */
+  0,  /* pre_process_record */
   default_target_signal_from_host,  /* target_signal_from_host */
   default_target_signal_to_host,  /* target_signal_to_host */
   0,  /* get_siginfo_type */
@@ -673,6 +675,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of sofun_address_maybe_missing, invalid_p == 0 */
   /* Skip verify of process_record, has predicate */
   /* Skip verify of process_record_signal, has predicate */
+  /* Skip verify of pre_process_record, has predicate */
   /* Skip verify of target_signal_from_host, invalid_p == 0 */
   /* Skip verify of target_signal_to_host, invalid_p == 0 */
   /* Skip verify of get_siginfo_type, has predicate */
@@ -1009,6 +1012,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "gdbarch_dump: pointer_to_address = <%s>\n",
                       host_address_to_string (gdbarch->pointer_to_address));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_pre_process_record_p() = %d\n",
+                      gdbarch_pre_process_record_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: pre_process_record = <%s>\n",
+                      host_address_to_string (gdbarch->pre_process_record));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_print_float_info_p() = %d\n",
                       gdbarch_print_float_info_p (gdbarch));
   fprintf_unfiltered (file,
@@ -3439,6 +3448,30 @@ set_gdbarch_process_record_signal (struc
   gdbarch->process_record_signal = process_record_signal;
 }
 
+int
+gdbarch_pre_process_record_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->pre_process_record != NULL;
+}
+
+int
+gdbarch_pre_process_record (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->pre_process_record != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_pre_process_record called\n");
+  return gdbarch->pre_process_record (gdbarch);
+}
+
+void
+set_gdbarch_pre_process_record (struct gdbarch *gdbarch,
+                                gdbarch_pre_process_record_ftype pre_process_record)
+{
+  gdbarch->pre_process_record = pre_process_record;
+}
+
 enum target_signal
 gdbarch_target_signal_from_host (struct gdbarch *gdbarch, int signo)
 {
--- a/gdbarch.h
+++ b/gdbarch.h
@@ -853,6 +853,14 @@ typedef int (gdbarch_process_record_sign
 extern int gdbarch_process_record_signal (struct gdbarch *gdbarch, struct regcache *regcache, enum target_signal signal);
 extern void set_gdbarch_process_record_signal (struct gdbarch *gdbarch, gdbarch_process_record_signal_ftype *process_record_signal);
 
+/* Pre process record */
+
+extern int gdbarch_pre_process_record_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_pre_process_record_ftype) (struct gdbarch *gdbarch);
+extern int gdbarch_pre_process_record (struct gdbarch *gdbarch);
+extern void set_gdbarch_pre_process_record (struct gdbarch *gdbarch, gdbarch_pre_process_record_ftype *pre_process_record);
+
 /* Signal translation: translate inferior's signal (host's) number into
    GDB's representation. */
 
--- a/gdbarch.sh
+++ b/gdbarch.sh
@@ -728,6 +728,9 @@ M:int:process_record:struct regcache *re
 # Return -1 if something goes wrong, 0 otherwise.
 M:int:process_record_signal:struct regcache *regcache, enum target_signal signal:regcache, signal
 
+# Pre process record
+M:int:pre_process_record:void:
+
 # Signal translation: translate inferior's signal (host's) number into
 # GDB's representation.
 m:enum target_signal:target_signal_from_host:int signo:signo::default_target_signal_from_host::0
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -693,6 +693,7 @@ i386_linux_init_abi (struct gdbarch_info
   tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET;
 
   set_gdbarch_process_record (gdbarch, i386_process_record);
+  set_gdbarch_pre_process_record (gdbarch, linux_pre_record);
   set_gdbarch_process_record_signal (gdbarch, i386_linux_record_signal);
 
   /* Initialize the i386_linux_record_tdep.  */
--- a/linux-nat.c
+++ b/linux-nat.c
@@ -2143,6 +2143,8 @@ linux_handle_syscall_trap (struct lwp_in
    event should be ignored and we should wait again.  If STOPPING is
    true, the new LWP remains stopped, otherwise it is continued.  */
 
+extern int linux_pre_recording;
+
 static int
 linux_handle_extended_wait (struct lwp_info *lp, int status,
 			    int stopping)
@@ -2180,7 +2182,8 @@ linux_handle_extended_wait (struct lwp_i
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
       if (event == PTRACE_EVENT_FORK
-	  && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+	  && (linux_fork_checkpointing_p (GET_PID (lp->ptid))
+              || linux_pre_recording))
 	{
 	  struct fork_info *fp;
 
@@ -2192,10 +2195,14 @@ linux_handle_extended_wait (struct lwp_i
 	     physically remove the breakpoints from the child.  */
 	  detach_breakpoints (new_pid);
 
-	  /* Retain child fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (new_pid);
-	  if (!fp)
-	    fp = add_fork (new_pid);
+	  /* Retain child fork in ptrace (stopped) state if this is
+             checkpoint.  */
+          if (!linux_pre_recording)
+            {
+	      fp = find_fork_pid (new_pid);
+	      if (!fp)
+	        fp = add_fork (new_pid);
+            }
 
 	  /* Report as spurious, so that infrun doesn't want to follow
 	     this fork.  We're actually doing an infcall in
--- a/linux-record.c
+++ b/linux-record.c
@@ -21,9 +21,21 @@
 #include "target.h"
 #include "gdbtypes.h"
 #include "regcache.h"
+#include "inferior.h"
+#include "infcall.h"
+#include "objfiles.h"
 #include "record.h"
 #include "linux-record.h"
 
+//#include "arch-utils.h"
+//
+//#include "gdbcmd.h"
+//
+//
+//#include "gdb_assert.h"
+//#include "gdb_string.h"
+#include "linux-fork.h"
+
 /* These macros are the values of the first argument of system call
    "sys_ptrace".  The values of these macros were obtained from Linux
    Kernel source.  */
@@ -2243,3 +2255,59 @@ record_linux_system_call (enum gdb_sysca
 
   return 0;
 }
+
+int linux_pre_recording = 0;
+
+int
+linux_pre_record (struct gdbarch *gdbarch)
+{
+  struct objfile *fork_objf;
+  struct value *fork_fn = NULL, *ret;
+  struct cleanup *old_cleanups;
+  struct cleanup *self_cleanups;
+  pid_t pid;
+  ptid_t ptid, record_ptid;
+  struct regcache *record_regcache;
+  extern void linux_nat_switch_fork (ptid_t new_ptid);
+
+  /* Get the fork_fn.  */
+  if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
+    fork_fn = find_function_in_inferior ("fork", &fork_objf);
+  if (!fork_fn)
+    if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
+      fork_fn = find_function_in_inferior ("fork", &fork_objf);
+  if (!fork_fn)
+    return -1;
+  ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
+
+  /* Tell record.c that the following inferior change doesn't need record. */
+  old_cleanups = record_disable_set ();
+
+  /* Tell target that this is linux pre-record.  */
+  self_cleanups = make_cleanup_restore_integer (&linux_pre_recording);
+  linux_pre_recording = 1;
+
+  ret = call_function_by_hand (fork_fn, 0, &ret);
+
+  do_cleanups (self_cleanups);
+
+  pid = value_as_long (ret);
+  if (pid < 0)
+    return -1;
+  ptid = ptid_build (pid, pid, 0);
+
+  /* Set the current regcache to ptid.  */
+  record_ptid = inferior_ptid;
+  record_regcache = regcache_dup (get_current_regcache ());
+  linux_nat_switch_fork (ptid);
+  regcache_cpy (get_current_regcache (), record_regcache);
+  linux_nat_switch_fork (record_ptid);
+  regcache_xfree (record_regcache);
+
+  do_cleanups (old_cleanups);
+
+  record_arch_list_add_ptid (ptid);
+  record_arch_list_add_end ();
+
+  return 0;
+}
--- a/linux-record.h
+++ b/linux-record.h
@@ -537,4 +537,6 @@ enum gdb_syscall {
 extern int record_linux_system_call (enum gdb_syscall num, 
 				     struct regcache *regcache,
 				     struct linux_record_tdep *tdep);
+
+extern int linux_pre_record (struct gdbarch *gdbarch);
 #endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -106,7 +106,8 @@ enum record_type
 {
   record_end = 0,
   record_reg,
-  record_mem
+  record_mem,
+  record_ptid,
 };
 
 /* This is the data structure that makes up the execution log.
@@ -146,6 +147,8 @@ struct record_entry
     struct record_mem_entry mem;
     /* end */
     struct record_end_entry end;
+    /* ptid */
+    ptid_t ptid;
   } u;
 };
 
@@ -159,6 +162,14 @@ struct record_core_buf_entry
   bfd_byte *buf;
 };
 
+/* The arch level interface choice inferior stepi or continue.  */
+
+int record_step;
+
+/* The step of record_resume.  */
+
+static int record_resume_step = 0;
+
 /* Record buf with core target.  */
 static gdb_byte *record_core_regbuf = NULL;
 static struct target_section *record_core_start;
@@ -200,6 +211,9 @@ static ULONGEST record_insn_count;
 static struct target_ops record_ops;
 static struct target_ops record_core_ops;
 
+/* The swith of record pre.  */
+static int record_pre = 1;
+
 /* The beneath function pointers.  */
 static struct target_ops *record_beneath_to_resume_ops;
 static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
@@ -288,6 +302,16 @@ record_mem_release (struct record_entry 
   xfree (rec);
 }
 
+/* XXX: Free a record_ptid record entry.  */
+#include <sys/ptrace.h>
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+  gdb_assert (rec->type == record_ptid);
+  ptrace (PTRACE_KILL, PIDGET (rec->u.ptid), 0, 0);
+  xfree (rec);
+}
+
 /* Alloc a record_end record entry.  */
 
 static inline struct record_entry *
@@ -324,6 +348,9 @@ record_entry_release (struct record_entr
   case record_mem:
     record_mem_release (rec);
     break;
+  case record_ptid:
+    record_ptid_release (rec);
+    break;
   case record_end:
     record_end_release (rec);
     break;
@@ -512,6 +539,28 @@ record_arch_list_add_mem (CORE_ADDR addr
   return 0;
 }
 
+/* Record the value of a ptid to record_arch_list.  */
+
+int
+record_arch_list_add_ptid (ptid_t ptid)
+{
+  struct record_entry *rec;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: add ptid = %s to "
+			"record list.\n",
+			target_pid_to_str (ptid));
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_ptid;
+  rec->u.ptid = ptid;
+
+  record_arch_list_add (rec);
+
+  return 0;
+}
+
 /* Add a record_end type struct record_entry to record_arch_list.  */
 
 int
@@ -576,7 +625,7 @@ record_arch_list_cleanups (void *ignore)
 static int
 record_message (struct regcache *regcache, enum target_signal signal)
 {
-  int ret;
+  int ret = -1;
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
@@ -586,6 +635,8 @@ record_message (struct regcache *regcach
   /* Check record_insn_num.  */
   record_check_insn_num (1);
 
+  record_step = 1;
+
   /* If gdb sends a signal value to target_resume,
      save it in the 'end' field of the previous instruction.
 
@@ -608,11 +659,20 @@ record_message (struct regcache *regcach
      But we should still deliver the signal to gdb during the replay,
      if we delivered it during the recording.  Therefore we should
      record the signal during record_wait, not record_resume.  */
-  if (record_list != &record_first)    /* FIXME better way to check */
-    {
-      gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = signal;
-    }
+  {
+    struct record_entry *rec;
+
+    if (RECORD_IS_REPLAY)
+      rec = record_list->prev;
+    else
+      rec = record_list;
+
+    if (rec != &record_first)    /* FIXME better way to check */
+      {
+        gdb_assert (rec->type == record_end);
+        rec->u.end.sigval = signal;
+      }
+  }
 
   if (signal == TARGET_SIGNAL_0
       || !gdbarch_process_record_signal_p (gdbarch))
@@ -631,9 +691,19 @@ record_message (struct regcache *regcach
 
   discard_cleanups (old_cleanups);
 
-  record_list->next = record_arch_list_head;
-  record_arch_list_head->prev = record_list;
-  record_list = record_arch_list_tail;
+  if (RECORD_IS_REPLAY)
+    {
+      record_list->prev->next = record_arch_list_head;
+      record_arch_list_head->prev = record_list->prev;
+      record_list->prev = record_arch_list_tail;
+      record_arch_list_tail->next = record_list;
+    }
+  else
+    {
+      record_list->next = record_arch_list_head;
+      record_arch_list_head->prev = record_list;
+      record_list = record_arch_list_tail;
+    }
 
   if (record_insn_num == record_insn_max_num && record_insn_max_num)
     record_list_release_first ();
@@ -774,6 +844,129 @@ record_exec_insn (struct regcache *regca
           }
       }
       break;
+
+    case record_ptid:
+      {
+        struct frame_info *fi;
+        int i;
+        CORE_ADDR current_stop_pc = stop_pc;
+        ptid_t current_ptid = inferior_ptid;
+        struct regcache *current_reg;
+        int current_frame_count = 0;
+        struct frame_id *current_frame_id;
+        struct regcache *record_reg;
+        extern void linux_nat_switch_fork (ptid_t new_ptid);
+        extern void nullify_last_target_wait_ptid ();
+
+        set_executing (inferior_ptid, 0);
+
+        /* Get current_reg.  */
+        current_reg = regcache_dup (get_current_regcache ());
+        /* Get current_frame_count.  */
+        for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+          current_frame_count ++;
+        /* Get current_frame_reg.  */
+        current_frame_id = alloca (current_frame_count
+                                    * sizeof (struct frame_id));
+        i = 0;
+        for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+          {
+            current_frame_id[i] = get_frame_id (fi);
+            i ++;
+          }
+
+        /* Switch to record_ptid.  */
+        linux_nat_switch_fork (record_list->u.ptid);
+        registers_changed ();
+        reinit_frame_cache ();
+        record_reg = get_current_regcache ();
+        stop_pc = regcache_read_pc (record_reg);
+        nullify_last_target_wait_ptid ();
+
+        while (1)
+          {
+            struct target_waitstatus status;
+
+            /* Check if the record_ptid is in the same place of
+               current_ptid.  */
+            if (stop_pc == current_stop_pc)
+              {
+                int not_same = 0;
+                struct regcache *record_reg = get_current_regcache ();
+                gdb_byte current[MAX_REGISTER_SIZE], record[MAX_REGISTER_SIZE];
+
+                /* Check current_reg.  */
+                for (i = gdbarch_num_regs (gdbarch) - 1; i >= 0; i --)
+                  {
+                    if (!regcache_valid_p (current_reg,i))
+                      continue;
+
+                    memset (current, 0, MAX_REGISTER_SIZE);
+                    memset (record, 0, MAX_REGISTER_SIZE);
+
+                    regcache_raw_read (current_reg, i, current);
+                    regcache_raw_read (record_reg, i, record);
+                    if (memcmp (current, record, MAX_REGISTER_SIZE))
+                      {
+                        not_same = 1;
+                        break;
+                      }
+                  }
+                if (not_same)
+                  goto keep_exec;
+
+                /* Check current_frame_id.  */
+                i = 0;
+                for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+                  {
+                    if (i >= current_frame_count)
+                      {
+                        not_same = 1;
+                        break;
+                      }
+
+                    if (!frame_id_eq (get_frame_id (fi), current_frame_id[i]))
+                      {
+                        not_same = 1;
+                        break;
+                      }
+
+                    i ++;
+                  }
+                if (not_same)
+                  goto keep_exec;
+
+                break;
+              }
+
+keep_exec:
+            if (!record_message_wrapper_safe (record_reg, TARGET_SIGNAL_0))
+  	      break;
+            record_beneath_to_resume (record_beneath_to_resume_ops,
+                                      record_list->u.ptid, 1,
+                                      TARGET_SIGNAL_0);
+            record_beneath_to_wait (record_beneath_to_wait_ops,
+				    record_list->u.ptid, &status, 0);
+
+            /* Update stop_pc.  */
+            registers_changed ();
+            record_reg = get_current_regcache ();
+            stop_pc = regcache_read_pc (record_reg);
+          }
+
+        /* Release.  */
+        regcache_xfree (current_reg);
+
+        /* Switch to current_ptid.  */
+        linux_nat_switch_fork (current_ptid);
+        registers_changed ();
+        reinit_frame_cache ();
+        stop_pc = regcache_read_pc (get_current_regcache ());
+        nullify_last_target_wait_ptid ();
+
+        set_executing (inferior_ptid, 1);
+      }
+      break;
     }
 }
 
@@ -872,6 +1065,31 @@ record_open_1 (char *name, int from_tty)
     error (_("Could not find 'to_stopped_data_address' method on the target stack."));
 
   push_target (&record_ops);
+
+  /* Do pre record.  */
+  if (record_pre)
+    {
+      int ret;
+      struct gdbarch *gdbarch = get_current_arch ();
+
+      record_arch_list_head = NULL;
+      record_arch_list_tail = NULL;
+
+      if (gdbarch_pre_process_record_p (gdbarch))
+        ret = gdbarch_pre_process_record (gdbarch);
+      if (ret == 0)
+        {
+          record_list->next = record_arch_list_head;
+          record_arch_list_head->prev = record_list;
+          record_list = record_arch_list_tail;
+        }
+      else
+        {
+          fprintf_unfiltered (gdb_stdlog,
+                              _("Auto close the record pre.\n"));
+          record_pre = 0;
+        }
+    }
 }
 
 /* "to_open" target method.  Open the process record target.  */
@@ -879,6 +1097,7 @@ record_open_1 (char *name, int from_tty)
 static void
 record_open (char *name, int from_tty)
 {
+  int ret = -1;
   struct target_ops *t;
 
   if (record_debug)
@@ -995,8 +1214,6 @@ record_close (int quitting)
     }
 }
 
-static int record_resume_step = 0;
-
 /* "to_resume" target method.  Resume the process record target.  */
 
 static void
@@ -1007,10 +1224,12 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      if (!record_disable)
+      if (record_disable || record_pre)
+        record_step = step;
+      else
         record_message (get_current_regcache (), signal);
-      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
-                                signal);
+      record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+                                record_step, signal);
     }
 }
 
@@ -1070,7 +1289,8 @@ record_wait (struct target_ops *ops,
 
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_step || record_disable)
+      if (record_resume_step || record_disable
+          || record_pre)
 	{
 	  /* This is a single step or record is disabled.  */
 	  return record_beneath_to_wait (record_beneath_to_wait_ops,
@@ -1135,7 +1355,7 @@ record_wait (struct target_ops *ops,
   			}
 
 		      record_beneath_to_resume (record_beneath_to_resume_ops,
-						ptid, 1,
+						ptid, record_step,
 						TARGET_SIGNAL_0);
 		      continue;
 		    }
@@ -1219,7 +1439,8 @@ record_wait (struct target_ops *ops,
 
           record_exec_insn (regcache, gdbarch, record_list);
 
-	  if (record_list->type == record_end)
+	  if (record_list->type == record_end
+              && record_list->prev->type != record_ptid)
 	    {
 	      if (record_debug > 1)
 		fprintf_unfiltered (gdb_stdlog,
@@ -2724,6 +2945,16 @@ record/replay buffer.  Zero means unlimi
 			    set_record_insn_max_num,
 			    NULL, &set_record_cmdlist, &show_record_cmdlist);
 
+  add_setshow_boolean_cmd ("pre", no_class,
+			   &record_pre, _("\
+Set whether record/replay pre work."), _("\
+Show whether record/replay pre work."), _("\
+Default is ON.\n\
+When ON, if the record/replay pre will work.\n\
+When OFF, if the record/replay pre will not work."),
+			   NULL, NULL,
+			   &set_record_cmdlist, &show_record_cmdlist);
+
   add_cmd ("goto", class_obscure, cmd_record_goto, _("\
 Restore the program to its state at instruction number N.\n\
 Argument is instruction number, as shown by 'info record'."),
--- a/record.h
+++ b/record.h
@@ -23,9 +23,11 @@
 #define RECORD_IS_USED	(current_target.to_stratum == record_stratum)
 
 extern int record_debug;
+extern int record_step;
 
 extern int record_arch_list_add_reg (struct regcache *regcache, int num);
 extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
+extern int record_arch_list_add_ptid (ptid_t ptid);
 extern int record_arch_list_add_end (void);
 extern struct cleanup *record_disable_set (void);
 


More information about the Gdb mailing list