Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFC] Prec multi-thread support
@ 2009-11-02 16:45 Hui Zhu
  2009-11-02 18:14 ` Michael Snyder
  2009-11-09 18:00 ` Tom Tromey
  0 siblings, 2 replies; 5+ messages in thread
From: Hui Zhu @ 2009-11-02 16:45 UTC (permalink / raw)
  To: gdb-patches ml; +Cc: Michael Snyder

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

Hi guys,

This patches just for discussions and comments.  Please help me with it.

Thanks,
Hui

[-- Attachment #2: prec-fix-error-handler.txt --]
[-- Type: text/plain, Size: 1797 bytes --]

---
 record.c |   29 ++++++++++-------------------
 1 file changed, 10 insertions(+), 19 deletions(-)

--- a/record.c
+++ b/record.c
@@ -954,7 +954,6 @@ record_close (int quitting)
 }
 
 static int record_resume_step = 0;
-static int record_resume_error;
 
 /* "to_resume" target method.  Resume the process record target.  */
 
@@ -966,15 +965,11 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      if (do_record_message (get_current_regcache (), signal))
-        {
-          record_resume_error = 0;
-        }
-      else
-        {
-          record_resume_error = 1;
-          return;
-        }
+      struct record_message_args args;
+
+      args.regcache = get_current_regcache ();
+      args.signal = signal;
+      record_message (&args);
       record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
                                 signal);
     }
@@ -1038,14 +1033,6 @@ record_wait (struct target_ops *ops,
 
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_error)
-	{
-	  /* If record_resume get error, return directly.  */
-	  status->kind = TARGET_WAITKIND_STOPPED;
-	  status->value.sig = TARGET_SIGNAL_ABRT;
-	  return inferior_ptid;
-	}
-
       if (record_resume_step)
 	{
 	  /* This is a single step.  */
@@ -1091,7 +1078,11 @@ record_wait (struct target_ops *ops,
 			 Therefore we will not return to gdb.
 		         Record the insn and resume.  */
 		      if (!do_record_message (regcache, TARGET_SIGNAL_0))
-			break;
+  			{
+                           status->kind = TARGET_WAITKIND_STOPPED;
+                           status->value.sig = TARGET_SIGNAL_0;
+                           break;
+  			}
 
 		      record_beneath_to_resume (record_beneath_to_resume_ops,
 						ptid, 1,

[-- Attachment #3: prec-x86-add-insn.txt --]
[-- Type: text/plain, Size: 734 bytes --]

---
 i386-tdep.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -4847,9 +4847,6 @@ reswitch:
       /* int3 */
       /* XXX */
     case 0xcc:
-      printf_unfiltered (_("Process record doesn't support instruction "
-			   "int3.\n"));
-      ir.addr -= 1;
       goto no_support;
       break;
 
@@ -4958,10 +4955,8 @@ reswitch:
 
       /* rdtsc */
     case 0x0f31:
-      printf_unfiltered (_("Process record doesn't support "
-			   "instruction rdtsc.\n"));
-      ir.addr -= 2;
-      goto no_support;
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM);
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM);
       break;
 
       /* sysenter */

[-- Attachment #4: prec-thread.txt --]
[-- Type: text/plain, Size: 33715 bytes --]

---
 amd64-linux-tdep.c |    4 
 gdbthread.h        |    2 
 i386-linux-tdep.c  |    4 
 i386-tdep.c        |   52 ++--
 i386-tdep.h        |    6 
 linux-record.c     |    7 
 linux-record.h     |    2 
 record.c           |  566 ++++++++++++++++++++++++++++++++++++++++++++---------
 record.h           |    1 
 9 files changed, 516 insertions(+), 128 deletions(-)

--- a/amd64-linux-tdep.c
+++ b/amd64-linux-tdep.c
@@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li
 #define RECORD_ARCH_GET_GS	0x1004
 
 static int
-amd64_linux_syscall_record (struct regcache *regcache)
+amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   ULONGEST syscall_native;
@@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca
     }
   else
     {
-      ret = record_linux_system_call (syscall_gdb, regcache,
+      ret = record_linux_system_call (syscall_gdb, addr, regcache,
                                       &amd64_linux_record_tdep);
       if (ret)
         return ret;
--- a/gdbthread.h
+++ b/gdbthread.h
@@ -37,6 +37,8 @@ struct thread_info
 				    kernel thread id, etc.  */
   int num;			/* Convenient handle (GDB thread id) */
 
+  int record_is_waiting;
+
   /* Non-zero means the thread is executing.  Note: this is different
      from saying that there is an active target and we are stopped at
      a breakpoint, for instance.  This is a real indicator whether the
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall)
 static struct linux_record_tdep i386_linux_record_tdep;
 
 static int
-i386_linux_intx80_sysenter_record (struct regcache *regcache)
+i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   LONGEST syscall_native;
@@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc
      return 0;
    }
 
-  ret = record_linux_system_call (syscall_gdb, regcache,
+  ret = record_linux_system_call (syscall_gdb, addr, regcache,
 				  &i386_linux_record_tdep);
   if (ret)
     return ret;
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor
 
   if (irp->override >= 0)
     {
-      warning (_("Process record ignores the memory change "
-                 "of instruction at address %s because it "
-                 "can't get the value of the segment register."),
-               paddress (gdbarch, irp->orig_addr));
+      if (record_debug)
+        warning (_("Process record ignores the memory change "
+                   "of instruction at address %s because it "
+                   "can't get the value of the segment register."),
+                 paddress (gdbarch, irp->orig_addr));
       return 0;
     }
 
@@ -4042,11 +4043,12 @@ reswitch:
     case 0xa3:
       if (ir.override >= 0)
         {
-	  warning (_("Process record ignores the memory change "
-                     "of instruction at address %s because "
-                     "it can't get the value of the segment "
-                     "register."),
-                   paddress (gdbarch, ir.orig_addr));
+          if (record_debug)
+	    warning (_("Process record ignores the memory change "
+                       "of instruction at address %s because "
+                       "it can't get the value of the segment "
+                       "register."),
+                     paddress (gdbarch, ir.orig_addr));
 	}
       else
 	{
@@ -4467,11 +4469,12 @@ reswitch:
           if (ir.aflag && (es != ds))
             {
               /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */
-              warning (_("Process record ignores the memory "
-                         "change of instruction at address %s "
-                         "because it can't get the value of the "
-                         "ES segment register."),
-                       paddress (gdbarch, ir.orig_addr));
+              if (record_debug)
+                warning (_("Process record ignores the memory "
+                           "change of instruction at address %s "
+                           "because it can't get the value of the "
+                           "ES segment register."),
+                         paddress (gdbarch, ir.orig_addr));
             }
           else
             {
@@ -4873,7 +4876,7 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -4975,7 +4978,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache,
+                                                            ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5000,7 +5004,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache,
+                                                           ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5136,12 +5141,13 @@ reswitch:
 	      /* sidt */
 	      if (ir.override >= 0)
 		{
-		  warning (_("Process record ignores the memory "
-                             "change of instruction at "
-                             "address %s because it can't get "
-                             "the value of the segment "
-                             "register."),
-                           paddress (gdbarch, ir.orig_addr));
+                  if (record_debug)
+		    warning (_("Process record ignores the memory "
+                               "change of instruction at "
+                               "address %s because it can't get "
+                               "the value of the segment "
+                               "register."),
+                             paddress (gdbarch, ir.orig_addr));
 		}
 	      else
 		{
--- a/i386-tdep.h
+++ b/i386-tdep.h
@@ -115,11 +115,11 @@ struct gdbarch_tdep
      in GDB is not same as I386 instructions.  */
   const int *record_regmap;
   /* Parse intx80 args.  */
-  int (*i386_intx80_record) (struct regcache *regcache);
+  int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse sysenter args.  */
-  int (*i386_sysenter_record) (struct regcache *regcache);
+  int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse syscall args.  */
-  int (*i386_syscall_record) (struct regcache *regcache);
+  int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr);
 };
 
 /* Floating-point registers.  */
--- a/linux-record.c
+++ b/linux-record.c
@@ -222,7 +222,7 @@ record_linux_msghdr (struct regcache *re
    Return -1 if something wrong.  */
 
 int
-record_linux_system_call (enum gdb_syscall syscall, 
+record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr,
 			  struct regcache *regcache,
                           struct linux_record_tdep *tdep)
 {
@@ -1209,10 +1209,13 @@ record_linux_system_call (enum gdb_sysca
 
     case gdb_sys_fsync:
     case gdb_sys_sigreturn:
-    case gdb_sys_clone:
     case gdb_sys_setdomainname:
       break;
 
+    case gdb_sys_clone:
+      record_step = 0;
+      break;
+
     case gdb_sys_newuname:
       regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest);
       if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest,
--- a/linux-record.h
+++ b/linux-record.h
@@ -534,7 +534,7 @@ enum gdb_syscall {
 
 /* Record a linux syscall.  */
 
-extern int record_linux_system_call (enum gdb_syscall num, 
+extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr,
 				     struct regcache *regcache,
 				     struct linux_record_tdep *tdep);
 #endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -30,6 +30,7 @@
 #include "record.h"
 #include "elf-bfd.h"
 #include "gcore.h"
+#include "observer.h"
 
 #include <signal.h>
 
@@ -60,6 +61,8 @@
 
 #define RECORD_FILE_MAGIC	netorder32(0x20091016)
 
+#define RECORD_THREAD	(record_ops.beneath->to_stratum == thread_stratum)
+
 /* These are the core structs of the process record functionality.
 
    A record_entry is a record of the value change of a register
@@ -106,7 +109,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.
@@ -144,6 +148,8 @@ struct record_entry
     struct record_reg_entry reg;
     /* mem */
     struct record_mem_entry mem;
+    /* ptid */
+    ptid_t ptid;
     /* end */
     struct record_end_entry end;
   } u;
@@ -196,10 +202,18 @@ static int record_insn_num = 0;
    than count of insns presently in execution log).  */
 static ULONGEST record_insn_count;
 
+/* In record mode, it's the step of next resume.  */
+int record_step;
+
+/* The current inferior_ptid that is recorded.  */
+static ptid_t record_prev_ptid;
+
 /* The target_ops of process record.  */
 static struct target_ops record_ops;
 static struct target_ops record_core_ops;
 
+static struct observer *record_threads_observer = NULL;
+
 /* 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,
@@ -285,6 +299,27 @@ record_mem_release (struct record_entry 
   xfree (rec);
 }
 
+/* Alloc a record_ptid record entry.  */
+
+static inline struct record_entry *
+record_ptid_alloc (void)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_ptid;
+
+  return rec;
+}
+
+/* Free a record_ptid record entry.  */
+
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+  xfree (rec);
+}
+
 /* Alloc a record_end record entry.  */
 
 static inline struct record_entry *
@@ -321,6 +356,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;
@@ -509,6 +547,24 @@ record_arch_list_add_mem (CORE_ADDR addr
   return 0;
 }
 
+static void
+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 = record_ptid_alloc ();
+
+  rec->u.ptid = *ptid;
+
+  record_arch_list_add (rec);
+}
+
 /* Add a record_end type struct record_entry to record_arch_list.  */
 
 int
@@ -570,7 +626,6 @@ record_arch_list_cleanups (void *ignore)
    record_arch_list, and add it to record_list.  */
 
 struct record_message_args {
-  struct regcache *regcache;
   enum target_signal signal;
 };
 
@@ -579,15 +634,21 @@ record_message (void *args)
 {
   int ret;
   struct record_message_args *myargs = args;
-  struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+  struct regcache *regcache;
+  struct gdbarch *gdbarch;
   struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
+  record_step = 1;
+
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
   /* Check record_insn_num.  */
   record_check_insn_num (1);
 
+  regcache = get_current_regcache ();
+  gdbarch = get_regcache_arch (regcache);
+
   /* If gdb sends a signal value to target_resume,
      save it in the 'end' field of the previous instruction.
 
@@ -610,20 +671,45 @@ record_message (void *args)
      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 */
+  if (ptid_equal (record_prev_ptid, inferior_ptid))
     {
-      gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = myargs->signal;
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          gdb_assert (record_list->type == record_end);
+          record_list->u.end.sigval = myargs->signal;
+        }
+    }
+  else
+    {
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          struct record_entry *rec;
+          for (rec = record_list; rec != record_first.next; rec = rec->prev)
+            {
+              if (rec->type == record_ptid
+                  && ptid_equal (rec->u.ptid, inferior_ptid))
+                {
+                  gdb_assert (rec->prev->type == record_end);
+                  rec->prev->u.end.sigval = myargs->signal;
+                  break;
+                }
+            }
+        }
+
+      /* If the inferior_ptid change (inferior_ptid is not same with
+         record_prev_ptid), record record_prev_ptid to record list.  */
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
     }
 
   if (myargs->signal == TARGET_SIGNAL_0
       || !gdbarch_process_record_signal_p (gdbarch))
     ret = gdbarch_process_record (gdbarch,
-				  myargs->regcache,
-				  regcache_read_pc (myargs->regcache));
+				  regcache,
+				  regcache_read_pc (regcache));
   else
     ret = gdbarch_process_record_signal (gdbarch,
-					 myargs->regcache,
+					 regcache,
 					 myargs->signal);
 
   if (ret > 0)
@@ -646,12 +732,10 @@ record_message (void *args)
 }
 
 static int
-do_record_message (struct regcache *regcache,
-		   enum target_signal signal)
+do_record_message (enum target_signal signal)
 {
   struct record_message_args args;
 
-  args.regcache = regcache;
   args.signal = signal;
   return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL);
 }
@@ -678,9 +762,12 @@ record_gdb_operation_disable_set (void)
    entries and memory entries, followed by an 'end' entry.  */
 
 static inline void
-record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
+record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p,
 		  struct record_entry *entry)
 {
+  struct regcache *regcache = *regcache_p;
+  struct gdbarch *gdbarch = *gdbarch_p;
+
   switch (entry->type)
     {
     case record_reg: /* reg */
@@ -744,9 +831,59 @@ record_exec_insn (struct regcache *regca
           }
       }
       break;
+
+    case record_ptid: /* ptid */
+      {
+        ptid_t tmp_ptid;
+
+        if (record_debug > 1)
+          fprintf_unfiltered (gdb_stdlog,
+                              "Process record: record_ptid %s to "
+                              "inferior ptid = %s.\n",
+                              host_address_to_string (entry),
+                              target_pid_to_str (entry->u.ptid));
+
+        if (!ptid_equal (entry->u.ptid, null_ptid))
+          {
+            inferior_ptid = entry->u.ptid;
+            *regcache_p = get_current_regcache ();
+            *gdbarch_p = get_regcache_arch (*regcache_p);
+          }
+        tmp_ptid = entry->u.ptid;
+        entry->u.ptid = record_prev_ptid;
+        record_prev_ptid = tmp_ptid;
+      }
+      break;
     }
 }
 
+static int record_thread_number;
+
+static int
+record_thread_number_count (struct thread_info *thread, void *unused)
+{
+  record_thread_number ++;
+
+  return 0;
+}
+
+static void
+record_thread_number_reset (void)
+{
+  record_thread_number = 0;
+
+  if (RECORD_THREAD)
+    iterate_over_threads (record_thread_number_count, NULL);
+}
+
+static void
+record_new_thread_handler (struct thread_info *tp)
+{
+  tp->record_is_waiting = 0;
+
+  record_thread_number_reset ();
+}
+
 static struct target_ops *tmp_to_resume_ops;
 static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
 			      enum target_signal);
@@ -838,6 +975,8 @@ record_open_1 (char *name, int from_tty)
     error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
 
   push_target (&record_ops);
+
+  record_threads_observer = observer_attach_new_thread (record_new_thread_handler);
 }
 
 /* "to_open" target method.  Open the process record target.  */
@@ -903,6 +1042,7 @@ record_open (char *name, int from_tty)
   record_insn_count = 0;
   record_list = &record_first;
   record_list->next = NULL;
+  record_prev_ptid = null_ptid;
 
   /* Set the tmp beneath pointers to beneath pointers.  */
   record_beneath_to_resume_ops = tmp_to_resume_ops;
@@ -920,6 +1060,8 @@ record_open (char *name, int from_tty)
     record_core_open_1 (name, from_tty);
   else
     record_open_1 (name, from_tty);
+
+  record_thread_number_reset ();
 }
 
 /* "to_close" target method.  Close the process record target.  */
@@ -932,6 +1074,12 @@ record_close (int quitting)
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
 
+  if (record_threads_observer)
+    {
+      observer_detach_new_thread (record_threads_observer);
+      record_threads_observer = NULL;
+    }
+
   record_list_release (record_list);
 
   /* Release record_core_regbuf.  */
@@ -954,6 +1102,8 @@ record_close (int quitting)
 }
 
 static int record_resume_step = 0;
+static ptid_t record_resume_ptid;
+static enum target_signal record_resume_signal = TARGET_SIGNAL_0;
 
 /* "to_resume" target method.  Resume the process record target.  */
 
@@ -965,14 +1115,188 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      struct record_message_args args;
+      record_resume_ptid = ptid;
 
-      args.regcache = get_current_regcache ();
-      args.signal = signal;
-      record_message (&args);
-      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
-                                signal);
+      if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid))
+        {
+          record_resume_signal = signal;
+        }
+      else
+        {
+          struct record_message_args args;
+
+          args.signal = signal;
+          record_message (&args);
+          record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+                                    record_step, signal);
+        }
+    }
+}
+
+struct record_thread_wait_args {
+  ptid_t real_inferior_ptid;
+  struct thread_info *real_inferior_tp;
+  struct target_waitstatus real_inferior_status;
+
+  struct target_waitstatus *status;
+  int options;
+};
+
+static int
+record_thread_reset_callback (struct thread_info *tp, void *data)
+{
+  tp->record_is_waiting = 0;
+
+  return 0;
+}
+
+static int
+record_thread_check_callback (struct thread_info *tp, void *data)
+{
+  if (tp->record_is_waiting)
+    return 1;
+
+  return 0;
+}
+
+/* Return 1 if it is not a simple step.
+   Return 0 if it is a simple step.  */
+
+static int
+record_thread_wait (ptid_t ptid, struct target_waitstatus *status,
+                    int options, ptid_t *ret_ptid)
+{
+  ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                             ptid, status, options);
+
+  if (ret_ptid)
+    *ret_ptid = wait_ptid;
+
+  if (status->kind == TARGET_WAITKIND_IGNORE)
+    return 0;
+
+  inferior_ptid = wait_ptid;
+
+  /* Is this a SIGTRAP?  */
+  if (status->kind == TARGET_WAITKIND_STOPPED
+      && status->value.sig == TARGET_SIGNAL_TRAP)
+    {
+      CORE_ADDR tmp_pc;
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
+
+      /* Yes -- check if there is a breakpoint.  */
+      registers_changed ();
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
+      tmp_pc = regcache_read_pc (regcache);
+      if (!record_step)
+        tmp_pc -= gdbarch_decr_pc_after_break (gdbarch);
+      if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc))
+        {
+          /* There is a breakpoint.  GDB will want to stop.  */
+          if (record_step)
+            {
+              CORE_ADDR decr_pc_after_break
+                          = gdbarch_decr_pc_after_break (gdbarch);
+              if (decr_pc_after_break)
+                regcache_write_pc (regcache, tmp_pc + decr_pc_after_break);
+            }
+
+          return 1;
+        }
+
+      return 0;
+    }
+
+  return 1;
+}
+
+static int
+record_thread_wait_callback (struct thread_info *tp, void *data)
+{
+  struct record_thread_wait_args *args = data;
+  enum target_signal sig = TARGET_SIGNAL_0;
+  int options = args->options;
+  int ret;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+                        "Process record: record_thread_wait_callback "
+                        "resume %s\n", target_pid_to_str (tp->ptid));
+
+  inferior_ptid = tp->ptid;
+
+  if (!tp->record_is_waiting)
+    {
+      if (record_resume_signal != TARGET_SIGNAL_0
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        {
+          sig = record_resume_signal;
+          record_resume_signal = TARGET_SIGNAL_0;
+        }
+      if (!do_record_message (sig))
+        {
+          args->status->kind = TARGET_WAITKIND_STOPPED;
+          args->status->value.sig = TARGET_SIGNAL_0;
+          return 1;
+        }
+
+      if (record_step == 0)
+        {
+          /* The next insn is a sys_clone.  */
+          if (iterate_over_threads (record_thread_check_callback, NULL))
+            target_stop (minus_one_ptid);
+          non_stop = 0;
+        }
+
+      record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid,
+                                record_step, sig);
+    }
+  else
+    {
+      if (record_debug > 1)
+        fprintf_unfiltered (gdb_stdlog,
+                            "Process record: record_thread_wait_callback "
+                            "not resume %s\n", target_pid_to_str (tp->ptid));
+    }
+
+  if (record_step)
+    options |= TARGET_WNOHANG;
+  ret = record_thread_wait (inferior_ptid, args->status,
+                            options, NULL);
+  if (args->status->kind == TARGET_WAITKIND_IGNORE)
+    {
+      if (!tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 1;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "start waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+    }
+  else
+    {
+      if (tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 0;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "stop waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+
+      if (!args->real_inferior_tp
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        args->real_inferior_tp = tp;
+      if (args->real_inferior_tp == tp)
+        args->real_inferior_status = *args->status;
     }
+
+  return ret;
 }
 
 static int record_get_sig = 0;
@@ -994,7 +1318,13 @@ record_sig_handler (int signo)
 }
 
 static void
-record_wait_cleanups (void *ignore)
+record_wait_signal_cleanups (void *ignore)
+{
+  signal (SIGINT, handle_sigint);
+}
+
+static void
+record_wait_replay_cleanups (void *ignore)
 {
   if (execution_direction == EXEC_REVERSE)
     {
@@ -1005,6 +1335,16 @@ record_wait_cleanups (void *ignore)
     record_list = record_list->prev;
 }
 
+static void
+record_wait_mthread_cleanups (void *ignore)
+{
+  /* Stop the threads that still running.  */
+  if (iterate_over_threads (record_thread_check_callback, NULL))
+    target_stop (minus_one_ptid);
+
+  non_stop = 0;
+}
+
 /* "to_wait" target method for process record target.
 
    In record mode, the target is always run in singlestep mode
@@ -1023,7 +1363,10 @@ record_wait (struct target_ops *ops,
 	     ptid_t ptid, struct target_waitstatus *status,
 	     int options)
 {
+  ptid_t ret_ptid;
   struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct cleanup *signal_cleanups
+                    = make_cleanup (record_wait_signal_cleanups, 0);
 
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -1031,81 +1374,103 @@ record_wait (struct target_ops *ops,
 			"record_resume_step = %d\n",
 			record_resume_step);
 
+  record_get_sig = 0;
+  signal (SIGINT, record_sig_handler);
+
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_step)
-	{
-	  /* This is a single step.  */
-	  return record_beneath_to_wait (record_beneath_to_wait_ops,
-					 ptid, status, options);
-	}
-      else
-	{
-	  /* This is not a single step.  */
-	  ptid_t ret;
-	  CORE_ADDR tmp_pc;
-
-	  while (1)
-	    {
-	      ret = record_beneath_to_wait (record_beneath_to_wait_ops,
-					    ptid, status, options);
+      /* Record mode.  */
+      if (record_thread_number > 1 && !ptid_equal (record_resume_ptid,
+                                                   inferior_ptid))
+        {
+          /* Multi-threads record.  */
+          struct record_thread_wait_args args;
+          struct thread_info *tp;
+          struct cleanup *mthread_cleanups
+                            = make_cleanup (record_wait_mthread_cleanups, 0);
 
-	      /* Is this a SIGTRAP?  */
-	      if (status->kind == TARGET_WAITKIND_STOPPED
-		  && status->value.sig == TARGET_SIGNAL_TRAP)
-		{
-		  struct regcache *regcache;
+          args.real_inferior_ptid = inferior_ptid;
+          args.real_inferior_tp = NULL;
+          args.status = status;
+          args.options = options;
+          non_stop = 1;
+          iterate_over_threads (record_thread_reset_callback, NULL);
 
-		  /* Yes -- check if there is a breakpoint.  */
-		  registers_changed ();
-		  regcache = get_current_regcache ();
-		  tmp_pc = regcache_read_pc (regcache);
-		  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-						  tmp_pc))
-		    {
-		      /* There is a breakpoint.  GDB will want to stop.  */
-		      struct gdbarch *gdbarch = get_regcache_arch (regcache);
-		      CORE_ADDR decr_pc_after_break
-			= gdbarch_decr_pc_after_break (gdbarch);
-		      if (decr_pc_after_break)
-			regcache_write_pc (regcache,
-					   tmp_pc + decr_pc_after_break);
-		    }
-		  else
-		    {
-		      /* There is not a breakpoint, and gdb is not
-		         stepping, therefore gdb will not stop.
-			 Therefore we will not return to gdb.
-		         Record the insn and resume.  */
-		      if (!do_record_message (regcache, TARGET_SIGNAL_0))
-  			{
-                           status->kind = TARGET_WAITKIND_STOPPED;
-                           status->value.sig = TARGET_SIGNAL_0;
-                           break;
-  			}
+          while (1)
+            {
+              tp = iterate_over_threads (record_thread_wait_callback, &args);
+              if (tp)
+                break;
+              if (record_resume_step
+                  && args.real_inferior_tp
+                  && !args.real_inferior_tp->record_is_waiting)
+                {
+                  *status = args.real_inferior_status;
+                  inferior_ptid = args.real_inferior_ptid;
+                  break;
+                }
+            }
 
-		      record_beneath_to_resume (record_beneath_to_resume_ops,
-						ptid, 1,
-						TARGET_SIGNAL_0);
-		      continue;
-		    }
-		}
+          do_cleanups (mthread_cleanups);
 
-	      /* The inferior is broken by a breakpoint or a signal.  */
-	      break;
+          ret_ptid = inferior_ptid;
+        }
+      else
+        {
+          /* Single-thread record.  */
+          if (record_resume_step)
+            {
+	      /* This is a single step.  */
+	      ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                                 ptid, status, options);
 	    }
+          else
+            {
+              /* This is not a single step.  */
+              while (1)
+                {
+                  if (record_thread_wait (ptid, status,
+                                          options, &ret_ptid))
+                    break;
 
-	  return ret;
-	}
+                  if (record_resume_step)
+                    break;
+
+                  /* There is not a breakpoint, and gdb is not
+                     stepping, therefore gdb will not stop.
+                     Therefore we will not return to gdb.
+                     Record the insn and resume.  */
+		  if (!do_record_message (TARGET_SIGNAL_0))
+                    {
+                      ret_ptid = inferior_ptid;
+                      status->kind = TARGET_WAITKIND_STOPPED;
+                      status->value.sig = TARGET_SIGNAL_0;
+                      break;
+                    }
+
+		  record_beneath_to_resume (record_beneath_to_resume_ops,
+		                            record_resume_ptid, record_step,
+		                            TARGET_SIGNAL_0);
+                }
+            }
+        }
     }
   else
     {
-      struct regcache *regcache = get_current_regcache ();
-      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      /* Replay mode.  */
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
       int continue_flag = 1;
       int first_record_end = 1;
-      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+      struct cleanup *old_cleanups =
+        make_cleanup (record_wait_replay_cleanups, 0);
       CORE_ADDR tmp_pc;
+      ptid_t old_inferior_ptid = inferior_ptid;
+
+      if (!ptid_equal (record_prev_ptid, null_ptid))
+        inferior_ptid = record_prev_ptid;
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
 
       status->kind = TARGET_WAITKIND_STOPPED;
 
@@ -1129,8 +1494,6 @@ record_wait (struct target_ops *ops,
 	    }
 	}
 
-      record_get_sig = 0;
-      signal (SIGINT, record_sig_handler);
       /* If GDB is in terminal_inferior mode, it will not get the signal.
          And in GDB replay mode, GDB doesn't need to be in terminal_inferior
          mode, because inferior will not executed.
@@ -1161,7 +1524,7 @@ record_wait (struct target_ops *ops,
 	      break;
 	    }
 
-          record_exec_insn (regcache, gdbarch, record_list);
+          record_exec_insn (&regcache, &gdbarch, record_list);
 
 	  if (record_list->type == record_end)
 	    {
@@ -1184,12 +1547,13 @@ record_wait (struct target_ops *ops,
 		     In EXEC_FORWARD mode, this is the record_end of current
 		     instruction.  */
 		  /* step */
-		  if (record_resume_step)
+		  if (record_resume_step
+                      && ptid_equal (inferior_ptid, old_inferior_ptid))
 		    {
 		      if (record_debug > 1)
 			fprintf_unfiltered (gdb_stdlog,
 					    "Process record: step.\n");
-		      continue_flag = 0;
+                      continue_flag = 0;
 		    }
 
 		  /* check breakpoint */
@@ -1233,22 +1597,23 @@ record_wait (struct target_ops *ops,
 	}
       while (continue_flag);
 
-      signal (SIGINT, handle_sigint);
-
 replay_out:
-      if (record_get_sig)
-	status->value.sig = TARGET_SIGNAL_INT;
-      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+      if (record_list->u.end.sigval != TARGET_SIGNAL_0)
 	/* FIXME: better way to check */
 	status->value.sig = record_list->u.end.sigval;
       else
 	status->value.sig = TARGET_SIGNAL_TRAP;
+      ret_ptid = inferior_ptid;
 
       discard_cleanups (old_cleanups);
     }
 
+  if (record_get_sig)
+    status->value.sig = TARGET_SIGNAL_INT;
+
+  do_cleanups (signal_cleanups);
   do_cleanups (set_cleanups);
-  return inferior_ptid;
+  return ret_ptid;
 }
 
 /* "to_disconnect" method for process record target.  */
@@ -1311,6 +1676,12 @@ record_registers_change (struct regcache
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
+  if (!ptid_equal (record_prev_ptid, inferior_ptid))
+    {
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
+    }
+
   if (regnum < 0)
     {
       int i;
@@ -1433,6 +1804,11 @@ record_xfer_partial (struct target_ops *
       /* Record registers change to list as an instruction.  */
       record_arch_list_head = NULL;
       record_arch_list_tail = NULL;
+      if (!ptid_equal (record_prev_ptid, inferior_ptid))
+        {
+          record_arch_list_add_ptid (&record_prev_ptid);
+          record_prev_ptid = inferior_ptid;
+        }
       if (record_arch_list_add_mem (offset, len))
 	{
 	  record_list_release (record_arch_list_tail);
@@ -2240,7 +2616,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == &record_first)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
@@ -2367,7 +2743,7 @@ cmd_record_save (char *args, int from_tt
         }
 
       /* Execute entry.  */
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->next)
         record_list = record_list->next;
@@ -2382,7 +2758,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == cur_record_list)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
--- a/record.h
+++ b/record.h
@@ -23,6 +23,7 @@
 #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);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Prec multi-thread support
  2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu
@ 2009-11-02 18:14 ` Michael Snyder
  2009-11-09 18:00 ` Tom Tromey
  1 sibling, 0 replies; 5+ messages in thread
From: Michael Snyder @ 2009-11-02 18:14 UTC (permalink / raw)
  To: Hui Zhu; +Cc: gdb-patches ml

Hui Zhu wrote:
> Hi guys,
> 
> This patches just for discussions and comments.  Please help me with it.
> 
> Thanks,
> Hui

Wow, great that you're working on this.

Two ways to help get discussion started:
1) Tell us in plain text what you're doing
(I know you know how hard it is to read someone else's code! <g>)
2) Start writing a test script to illustrate what works and
what doesn't work yet.

Michael


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Prec multi-thread support
  2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu
  2009-11-02 18:14 ` Michael Snyder
@ 2009-11-09 18:00 ` Tom Tromey
  2009-11-12  8:26   ` Hui Zhu
  1 sibling, 1 reply; 5+ messages in thread
From: Tom Tromey @ 2009-11-09 18:00 UTC (permalink / raw)
  To: Hui Zhu; +Cc: gdb-patches ml, Michael Snyder

>>>>> ">" == Hui Zhu <teawater@gmail.com> writes:

>> This patches just for discussions and comments.  Please help me with it.

>> +      struct record_message_args args;
>> +
>> +      args.regcache = get_current_regcache ();
>> +      args.signal = signal;
>> +      record_message (&args);

Directly calling record_message like this is ugly, because it avoids
type-checking by the compiler.

I would recommend restructuring this code in one of two possible ways.

The first approach is to make record_message a simple trampoline that
takes a void*, decodes it, and then calls a properly-typed function.
Then, only use record_message in calls to catch_errors -- direct calls
should call the properly-typed implementation function.

The second approach is to avoid catch_errors altogether and use
TRY_CATCH instead.  This lets you avoid trampolines entirely.  I prefer
this way, but I don't insist on it.

Tom


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Prec multi-thread support
  2009-11-09 18:00 ` Tom Tromey
@ 2009-11-12  8:26   ` Hui Zhu
  2009-11-13  9:27     ` Hui Zhu
  0 siblings, 1 reply; 5+ messages in thread
From: Hui Zhu @ 2009-11-12  8:26 UTC (permalink / raw)
  To: gdb-patches ml; +Cc: Michael Snyder, tromey

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

Hi guys,

This is the new version for multi-thread support.  It make
multi-thread work OK with record save and support thread exit when
record.
I think the code work of prec multi-thread support is done.    I can
begin to do some make it clear and some other things.

Thanks,
Hui

[-- Attachment #2: prec-x86-add-insn.txt --]
[-- Type: text/plain, Size: 734 bytes --]

---
 i386-tdep.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -4847,9 +4847,6 @@ reswitch:
       /* int3 */
       /* XXX */
     case 0xcc:
-      printf_unfiltered (_("Process record doesn't support instruction "
-			   "int3.\n"));
-      ir.addr -= 1;
       goto no_support;
       break;
 
@@ -4958,10 +4955,8 @@ reswitch:
 
       /* rdtsc */
     case 0x0f31:
-      printf_unfiltered (_("Process record doesn't support "
-			   "instruction rdtsc.\n"));
-      ir.addr -= 2;
-      goto no_support;
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM);
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM);
       break;
 
       /* sysenter */

[-- Attachment #3: prec-thread.txt --]
[-- Type: text/plain, Size: 39182 bytes --]

---
 amd64-linux-tdep.c |    4 
 gdbthread.h        |   11 
 i386-linux-tdep.c  |    4 
 i386-tdep.c        |   52 ++--
 i386-tdep.h        |    6 
 linux-record.c     |   13 -
 linux-record.h     |    2 
 record.c           |  675 +++++++++++++++++++++++++++++++++++++++++++++--------
 record.h           |    1 
 thread.c           |    9 
 10 files changed, 641 insertions(+), 136 deletions(-)

--- a/amd64-linux-tdep.c
+++ b/amd64-linux-tdep.c
@@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li
 #define RECORD_ARCH_GET_GS	0x1004
 
 static int
-amd64_linux_syscall_record (struct regcache *regcache)
+amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   ULONGEST syscall_native;
@@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca
     }
   else
     {
-      ret = record_linux_system_call (syscall_gdb, regcache,
+      ret = record_linux_system_call (syscall_gdb, addr, regcache,
                                       &amd64_linux_record_tdep);
       if (ret)
         return ret;
--- a/gdbthread.h
+++ b/gdbthread.h
@@ -29,6 +29,15 @@ struct symtab;
 #include "ui-out.h"
 #include "inferior.h"
 
+/* Frontend view of the thread state.  Possible extensions: stepping,
+   finishing, until(ling),...  */
+enum thread_state
+{
+  THREAD_STOPPED,
+  THREAD_RUNNING,
+  THREAD_EXITED,
+};
+
 struct thread_info
 {
   struct thread_info *next;
@@ -37,6 +46,8 @@ struct thread_info
 				    kernel thread id, etc.  */
   int num;			/* Convenient handle (GDB thread id) */
 
+  int record_is_waiting;
+
   /* Non-zero means the thread is executing.  Note: this is different
      from saying that there is an active target and we are stopped at
      a breakpoint, for instance.  This is a real indicator whether the
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall)
 static struct linux_record_tdep i386_linux_record_tdep;
 
 static int
-i386_linux_intx80_sysenter_record (struct regcache *regcache)
+i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   LONGEST syscall_native;
@@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc
      return 0;
    }
 
-  ret = record_linux_system_call (syscall_gdb, regcache,
+  ret = record_linux_system_call (syscall_gdb, addr, regcache,
 				  &i386_linux_record_tdep);
   if (ret)
     return ret;
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor
 
   if (irp->override >= 0)
     {
-      warning (_("Process record ignores the memory change "
-                 "of instruction at address %s because it "
-                 "can't get the value of the segment register."),
-               paddress (gdbarch, irp->orig_addr));
+      if (record_debug)
+        warning (_("Process record ignores the memory change "
+                   "of instruction at address %s because it "
+                   "can't get the value of the segment register."),
+                 paddress (gdbarch, irp->orig_addr));
       return 0;
     }
 
@@ -4042,11 +4043,12 @@ reswitch:
     case 0xa3:
       if (ir.override >= 0)
         {
-	  warning (_("Process record ignores the memory change "
-                     "of instruction at address %s because "
-                     "it can't get the value of the segment "
-                     "register."),
-                   paddress (gdbarch, ir.orig_addr));
+          if (record_debug)
+	    warning (_("Process record ignores the memory change "
+                       "of instruction at address %s because "
+                       "it can't get the value of the segment "
+                       "register."),
+                     paddress (gdbarch, ir.orig_addr));
 	}
       else
 	{
@@ -4467,11 +4469,12 @@ reswitch:
           if (ir.aflag && (es != ds))
             {
               /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */
-              warning (_("Process record ignores the memory "
-                         "change of instruction at address %s "
-                         "because it can't get the value of the "
-                         "ES segment register."),
-                       paddress (gdbarch, ir.orig_addr));
+              if (record_debug)
+                warning (_("Process record ignores the memory "
+                           "change of instruction at address %s "
+                           "because it can't get the value of the "
+                           "ES segment register."),
+                         paddress (gdbarch, ir.orig_addr));
             }
           else
             {
@@ -4873,7 +4876,7 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -4975,7 +4978,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache,
+                                                            ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5000,7 +5004,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache,
+                                                           ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5136,12 +5141,13 @@ reswitch:
 	      /* sidt */
 	      if (ir.override >= 0)
 		{
-		  warning (_("Process record ignores the memory "
-                             "change of instruction at "
-                             "address %s because it can't get "
-                             "the value of the segment "
-                             "register."),
-                           paddress (gdbarch, ir.orig_addr));
+                  if (record_debug)
+		    warning (_("Process record ignores the memory "
+                               "change of instruction at "
+                               "address %s because it can't get "
+                               "the value of the segment "
+                               "register."),
+                             paddress (gdbarch, ir.orig_addr));
 		}
 	      else
 		{
--- a/i386-tdep.h
+++ b/i386-tdep.h
@@ -115,11 +115,11 @@ struct gdbarch_tdep
      in GDB is not same as I386 instructions.  */
   const int *record_regmap;
   /* Parse intx80 args.  */
-  int (*i386_intx80_record) (struct regcache *regcache);
+  int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse sysenter args.  */
-  int (*i386_sysenter_record) (struct regcache *regcache);
+  int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse syscall args.  */
-  int (*i386_syscall_record) (struct regcache *regcache);
+  int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr);
 };
 
 /* Floating-point registers.  */
--- a/linux-record.c
+++ b/linux-record.c
@@ -21,6 +21,7 @@
 #include "target.h"
 #include "gdbtypes.h"
 #include "regcache.h"
+#include "inferior.h"
 #include "record.h"
 #include "linux-record.h"
 
@@ -222,7 +223,7 @@ record_linux_msghdr (struct regcache *re
    Return -1 if something wrong.  */
 
 int
-record_linux_system_call (enum gdb_syscall syscall, 
+record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr,
 			  struct regcache *regcache,
                           struct linux_record_tdep *tdep)
 {
@@ -242,8 +243,9 @@ record_linux_system_call (enum gdb_sysca
         int q;
         target_terminal_ours ();
         q = yquery (_("The next instruction is syscall exit.  "
-                      "It will make the program exit.  "
-                      "Do you want to stop the program?"));
+                      "It will make the thread %s exit.  "
+                      "Do you want to stop the program?"),
+                    target_pid_to_str (inferior_ptid));
         target_terminal_inferior ();
         if (q)
           return 1;
@@ -1209,10 +1211,13 @@ record_linux_system_call (enum gdb_sysca
 
     case gdb_sys_fsync:
     case gdb_sys_sigreturn:
-    case gdb_sys_clone:
     case gdb_sys_setdomainname:
       break;
 
+    case gdb_sys_clone:
+      record_step = 0;
+      break;
+
     case gdb_sys_newuname:
       regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest);
       if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest,
--- a/linux-record.h
+++ b/linux-record.h
@@ -534,7 +534,7 @@ enum gdb_syscall {
 
 /* Record a linux syscall.  */
 
-extern int record_linux_system_call (enum gdb_syscall num, 
+extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr,
 				     struct regcache *regcache,
 				     struct linux_record_tdep *tdep);
 #endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -30,6 +30,7 @@
 #include "record.h"
 #include "elf-bfd.h"
 #include "gcore.h"
+#include "observer.h"
 
 #include <signal.h>
 
@@ -60,6 +61,8 @@
 
 #define RECORD_FILE_MAGIC	netorder32(0x20091016)
 
+#define RECORD_THREAD	(record_ops.beneath->to_stratum == thread_stratum)
+
 /* These are the core structs of the process record functionality.
 
    A record_entry is a record of the value change of a register
@@ -106,7 +109,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.
@@ -144,6 +148,8 @@ struct record_entry
     struct record_reg_entry reg;
     /* mem */
     struct record_mem_entry mem;
+    /* ptid */
+    ptid_t ptid;
     /* end */
     struct record_end_entry end;
   } u;
@@ -196,10 +202,19 @@ static int record_insn_num = 0;
    than count of insns presently in execution log).  */
 static ULONGEST record_insn_count;
 
+/* In record mode, it's the step of next resume.  */
+int record_step;
+
+/* The current inferior_ptid that is recorded.  */
+static ptid_t record_prev_ptid;
+
 /* The target_ops of process record.  */
 static struct target_ops record_ops;
 static struct target_ops record_core_ops;
 
+static struct observer *record_new_thread_observer = NULL;
+static struct observer *record_thread_exit_observer = NULL;
+
 /* 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,
@@ -285,6 +300,27 @@ record_mem_release (struct record_entry 
   xfree (rec);
 }
 
+/* Alloc a record_ptid record entry.  */
+
+static inline struct record_entry *
+record_ptid_alloc (void)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_ptid;
+
+  return rec;
+}
+
+/* Free a record_ptid record entry.  */
+
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+  xfree (rec);
+}
+
 /* Alloc a record_end record entry.  */
 
 static inline struct record_entry *
@@ -321,6 +357,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;
@@ -328,6 +367,16 @@ record_entry_release (struct record_entr
   return type;
 }
 
+/* Remove one record entry from record_list.  */
+
+static void
+record_entry_remove_from_list (struct record_entry *rec)
+{
+  if (rec->next)
+    rec->next->prev = rec->prev;
+  rec->prev->next = rec->next;
+}
+
 /* Free all record entries in list pointed to by REC.  */
 
 static void
@@ -509,6 +558,24 @@ record_arch_list_add_mem (CORE_ADDR addr
   return 0;
 }
 
+static void
+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 = record_ptid_alloc ();
+
+  rec->u.ptid = *ptid;
+
+  record_arch_list_add (rec);
+}
+
 /* Add a record_end type struct record_entry to record_arch_list.  */
 
 int
@@ -570,7 +637,6 @@ record_arch_list_cleanups (void *ignore)
    record_arch_list, and add it to record_list.  */
 
 struct record_message_args {
-  struct regcache *regcache;
   enum target_signal signal;
 };
 
@@ -579,15 +645,21 @@ record_message (void *args)
 {
   int ret;
   struct record_message_args *myargs = args;
-  struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+  struct regcache *regcache;
+  struct gdbarch *gdbarch;
   struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
+  record_step = 1;
+
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
   /* Check record_insn_num.  */
   record_check_insn_num (1);
 
+  regcache = get_current_regcache ();
+  gdbarch = get_regcache_arch (regcache);
+
   /* If gdb sends a signal value to target_resume,
      save it in the 'end' field of the previous instruction.
 
@@ -610,20 +682,45 @@ record_message (void *args)
      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 */
+  if (ptid_equal (record_prev_ptid, inferior_ptid))
     {
-      gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = myargs->signal;
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          gdb_assert (record_list->type == record_end);
+          record_list->u.end.sigval = myargs->signal;
+        }
+    }
+  else
+    {
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          struct record_entry *rec;
+          for (rec = record_list; rec != record_first.next; rec = rec->prev)
+            {
+              if (rec->type == record_ptid
+                  && ptid_equal (rec->u.ptid, inferior_ptid))
+                {
+                  gdb_assert (rec->prev->type == record_end);
+                  rec->prev->u.end.sigval = myargs->signal;
+                  break;
+                }
+            }
+        }
+
+      /* If the inferior_ptid change (inferior_ptid is not same with
+         record_prev_ptid), record record_prev_ptid to record list.  */
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
     }
 
   if (myargs->signal == TARGET_SIGNAL_0
       || !gdbarch_process_record_signal_p (gdbarch))
     ret = gdbarch_process_record (gdbarch,
-				  myargs->regcache,
-				  regcache_read_pc (myargs->regcache));
+				  regcache,
+				  regcache_read_pc (regcache));
   else
     ret = gdbarch_process_record_signal (gdbarch,
-					 myargs->regcache,
+					 regcache,
 					 myargs->signal);
 
   if (ret > 0)
@@ -646,12 +743,10 @@ record_message (void *args)
 }
 
 static int
-do_record_message (struct regcache *regcache,
-		   enum target_signal signal, int catch)
+do_record_message (enum target_signal signal, int catch)
 {
   struct record_message_args args;
 
-  args.regcache = regcache;
   args.signal = signal;
 
   if (catch)
@@ -682,9 +777,12 @@ record_gdb_operation_disable_set (void)
    entries and memory entries, followed by an 'end' entry.  */
 
 static inline void
-record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
+record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p,
 		  struct record_entry *entry)
 {
+  struct regcache *regcache = *regcache_p;
+  struct gdbarch *gdbarch = *gdbarch_p;
+
   switch (entry->type)
     {
     case record_reg: /* reg */
@@ -748,6 +846,103 @@ record_exec_insn (struct regcache *regca
           }
       }
       break;
+
+    case record_ptid: /* ptid */
+      {
+        ptid_t tmp_ptid;
+
+        if (record_debug > 1)
+          fprintf_unfiltered (gdb_stdlog,
+                              "Process record: record_ptid %s to "
+                              "inferior ptid = %s.\n",
+                              host_address_to_string (entry),
+                              target_pid_to_str (entry->u.ptid));
+
+        if (!ptid_equal (entry->u.ptid, null_ptid))
+          {
+            inferior_ptid = entry->u.ptid;
+            *regcache_p = get_current_regcache ();
+            *gdbarch_p = get_regcache_arch (*regcache_p);
+          }
+        tmp_ptid = entry->u.ptid;
+        entry->u.ptid = record_prev_ptid;
+        record_prev_ptid = tmp_ptid;
+      }
+      break;
+    }
+}
+
+static int record_thread_number;
+
+static int
+record_thread_number_count (struct thread_info *thread, void *unused)
+{
+  if (thread->state_ != THREAD_EXITED)
+    record_thread_number ++;
+
+  return 0;
+}
+
+static void
+record_thread_number_reset (void)
+{
+  record_thread_number = 0;
+
+  if (RECORD_THREAD)
+    iterate_over_threads (record_thread_number_count, NULL);
+}
+
+static void
+record_new_thread_handler (struct thread_info *tp)
+{
+  tp->record_is_waiting = 0;
+
+  record_thread_number_reset ();
+}
+
+static void
+record_thread_exit_handler (struct thread_info *tp, int silent)
+{
+  struct record_entry *rec;
+  ptid_t cur_ptid = record_prev_ptid;
+
+  if (!record_first.next)
+    return;
+
+  tp->record_is_waiting = 0;
+
+  if (tp->state_ != THREAD_EXITED)
+    {
+      gdb_assert (record_thread_number > 0);
+      record_thread_number --;
+    }
+
+  /* Delete all the record_reg and record_ptid for tp->ptid
+     from record list.  */
+  for (rec = record_list; rec != &record_first;)
+    {
+      struct record_entry *tmp = rec;
+      rec = rec->prev;
+
+      switch (tmp->type) {
+        case record_reg:
+          /* If this record_reg is for tp->ptid, delte it.  */
+          if (ptid_equal (cur_ptid, tp->ptid))
+            {
+              record_entry_remove_from_list (tmp);
+              record_entry_release (tmp);
+            }
+          break;
+        case record_ptid:
+          /* If this record_ptid is for tp->ptid, delte it.  */
+          cur_ptid = tmp->u.ptid;
+          if (ptid_equal (cur_ptid, tp->ptid))
+            {
+              record_entry_remove_from_list (tmp);
+              record_entry_release (tmp);
+            }
+          break;
+      }
     }
 }
 
@@ -842,6 +1037,9 @@ record_open_1 (char *name, int from_tty)
     error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
 
   push_target (&record_ops);
+
+  record_new_thread_observer = observer_attach_new_thread (record_new_thread_handler);
+  record_thread_exit_observer = observer_attach_thread_exit (record_thread_exit_handler);
 }
 
 /* "to_open" target method.  Open the process record target.  */
@@ -907,6 +1105,7 @@ record_open (char *name, int from_tty)
   record_insn_count = 0;
   record_list = &record_first;
   record_list->next = NULL;
+  record_prev_ptid = null_ptid;
 
   /* Set the tmp beneath pointers to beneath pointers.  */
   record_beneath_to_resume_ops = tmp_to_resume_ops;
@@ -924,6 +1123,8 @@ record_open (char *name, int from_tty)
     record_core_open_1 (name, from_tty);
   else
     record_open_1 (name, from_tty);
+
+  record_thread_number_reset ();
 }
 
 /* "to_close" target method.  Close the process record target.  */
@@ -936,6 +1137,17 @@ record_close (int quitting)
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
 
+  if (record_new_thread_observer)
+    {
+      observer_detach_new_thread (record_new_thread_observer);
+      record_new_thread_observer = NULL;
+    }
+  if (record_thread_exit_observer)
+    {
+      observer_detach_thread_exit (record_thread_exit_observer);
+      record_thread_exit_observer = NULL;
+    }
+
   record_list_release (record_list);
 
   /* Release record_core_regbuf.  */
@@ -958,6 +1170,8 @@ record_close (int quitting)
 }
 
 static int record_resume_step = 0;
+static ptid_t record_resume_ptid;
+static enum target_signal record_resume_signal = TARGET_SIGNAL_0;
 
 /* "to_resume" target method.  Resume the process record target.  */
 
@@ -969,12 +1183,189 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      do_record_message (get_current_regcache (), signal, 0);
-      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
-                                signal);
+      record_resume_ptid = ptid;
+      if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid))
+        {
+          record_resume_signal = signal;
+        }
+      else
+        {
+          do_record_message (signal, 0);
+          record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+                                    record_step, signal);
+        }
     }
 }
 
+struct record_thread_wait_args {
+  ptid_t real_inferior_ptid;
+  struct thread_info *real_inferior_tp;
+  struct target_waitstatus real_inferior_status;
+
+  struct target_waitstatus *status;
+  int options;
+};
+
+static int
+record_thread_reset_callback (struct thread_info *tp, void *data)
+{
+  tp->record_is_waiting = 0;
+
+  return 0;
+}
+
+static int
+record_thread_check_callback (struct thread_info *tp, void *data)
+{
+  if (tp->state_ != THREAD_EXITED && tp->record_is_waiting)
+    return 1;
+
+  return 0;
+}
+
+/* Return 1 if it is not a simple step.
+   Return 0 if it is a simple step.  */
+
+static int
+record_thread_wait (ptid_t ptid, struct target_waitstatus *status,
+                    int options, ptid_t *ret_ptid)
+{
+  ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                             ptid, status, options);
+
+  if (ret_ptid)
+    *ret_ptid = wait_ptid;
+
+  if (status->kind == TARGET_WAITKIND_IGNORE)
+    return 0;
+
+  inferior_ptid = wait_ptid;
+
+  /* Is this a SIGTRAP?  */
+  if (status->kind == TARGET_WAITKIND_STOPPED
+      && status->value.sig == TARGET_SIGNAL_TRAP)
+    {
+      CORE_ADDR tmp_pc;
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
+
+      /* Yes -- check if there is a breakpoint.  */
+      registers_changed ();
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
+      tmp_pc = regcache_read_pc (regcache);
+      if (!record_step)
+        tmp_pc -= gdbarch_decr_pc_after_break (gdbarch);
+      if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc))
+        {
+          /* There is a breakpoint.  GDB will want to stop.  */
+          if (record_step)
+            {
+              CORE_ADDR decr_pc_after_break
+                          = gdbarch_decr_pc_after_break (gdbarch);
+              if (decr_pc_after_break)
+                regcache_write_pc (regcache, tmp_pc + decr_pc_after_break);
+            }
+
+          return 1;
+        }
+
+      return 0;
+    }
+
+  return 1;
+}
+
+static int
+record_thread_wait_callback (struct thread_info *tp, void *data)
+{
+  struct record_thread_wait_args *args = data;
+  enum target_signal sig = TARGET_SIGNAL_0;
+  int options = args->options;
+  int ret;
+
+  if (tp->state_ == THREAD_EXITED)
+    return 0;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+                        "Process record: record_thread_wait_callback "
+                        "resume %s\n", target_pid_to_str (tp->ptid));
+
+  inferior_ptid = tp->ptid;
+
+  if (!tp->record_is_waiting)
+    {
+      if (record_resume_signal != TARGET_SIGNAL_0
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        {
+          sig = record_resume_signal;
+          record_resume_signal = TARGET_SIGNAL_0;
+        }
+      if (!do_record_message (sig, 1))
+        {
+          args->status->kind = TARGET_WAITKIND_STOPPED;
+          args->status->value.sig = TARGET_SIGNAL_0;
+          return 1;
+        }
+
+      if (record_step == 0)
+        {
+          /* The next insn is a sys_clone.  */
+          if (iterate_over_threads (record_thread_check_callback, NULL))
+            target_stop (minus_one_ptid);
+          non_stop = 0;
+        }
+
+      record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid,
+                                record_step, sig);
+    }
+  else
+    {
+      if (record_debug > 1)
+        fprintf_unfiltered (gdb_stdlog,
+                            "Process record: record_thread_wait_callback "
+                            "not resume %s\n", target_pid_to_str (tp->ptid));
+    }
+
+  if (record_step)
+    options |= TARGET_WNOHANG;
+  ret = record_thread_wait (inferior_ptid, args->status,
+                            options, NULL);
+  if (args->status->kind == TARGET_WAITKIND_IGNORE)
+    {
+      if (!tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 1;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "start waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+    }
+  else
+    {
+      if (tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 0;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "stop waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+
+      if (!args->real_inferior_tp
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        args->real_inferior_tp = tp;
+      if (args->real_inferior_tp == tp)
+        args->real_inferior_status = *args->status;
+    }
+
+  return ret;
+}
+
 static int record_get_sig = 0;
 
 /* SIGINT signal handler, registered by "to_wait" method.  */
@@ -994,7 +1385,13 @@ record_sig_handler (int signo)
 }
 
 static void
-record_wait_cleanups (void *ignore)
+record_wait_signal_cleanups (void *ignore)
+{
+  signal (SIGINT, handle_sigint);
+}
+
+static void
+record_wait_replay_cleanups (void *ignore)
 {
   if (execution_direction == EXEC_REVERSE)
     {
@@ -1005,6 +1402,16 @@ record_wait_cleanups (void *ignore)
     record_list = record_list->prev;
 }
 
+static void
+record_wait_mthread_cleanups (void *ignore)
+{
+  /* Stop the threads that still running.  */
+  if (iterate_over_threads (record_thread_check_callback, NULL))
+    target_stop (minus_one_ptid);
+
+  non_stop = 0;
+}
+
 /* "to_wait" target method for process record target.
 
    In record mode, the target is always run in singlestep mode
@@ -1023,7 +1430,10 @@ record_wait (struct target_ops *ops,
 	     ptid_t ptid, struct target_waitstatus *status,
 	     int options)
 {
+  ptid_t ret_ptid;
   struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct cleanup *signal_cleanups
+                    = make_cleanup (record_wait_signal_cleanups, 0);
 
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -1031,81 +1441,103 @@ record_wait (struct target_ops *ops,
 			"record_resume_step = %d\n",
 			record_resume_step);
 
+  record_get_sig = 0;
+  signal (SIGINT, record_sig_handler);
+
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_step)
-	{
-	  /* This is a single step.  */
-	  return record_beneath_to_wait (record_beneath_to_wait_ops,
-					 ptid, status, options);
-	}
-      else
-	{
-	  /* This is not a single step.  */
-	  ptid_t ret;
-	  CORE_ADDR tmp_pc;
-
-	  while (1)
-	    {
-	      ret = record_beneath_to_wait (record_beneath_to_wait_ops,
-					    ptid, status, options);
+      /* Record mode.  */
+      if (record_thread_number > 1 && !ptid_equal (record_resume_ptid,
+                                                   inferior_ptid))
+        {
+          /* Multi-threads record.  */
+          struct record_thread_wait_args args;
+          struct thread_info *tp;
+          struct cleanup *mthread_cleanups
+                            = make_cleanup (record_wait_mthread_cleanups, 0);
 
-	      /* Is this a SIGTRAP?  */
-	      if (status->kind == TARGET_WAITKIND_STOPPED
-		  && status->value.sig == TARGET_SIGNAL_TRAP)
-		{
-		  struct regcache *regcache;
+          args.real_inferior_ptid = inferior_ptid;
+          args.real_inferior_tp = NULL;
+          args.status = status;
+          args.options = options;
+          non_stop = 1;
+          iterate_over_threads (record_thread_reset_callback, NULL);
 
-		  /* Yes -- check if there is a breakpoint.  */
-		  registers_changed ();
-		  regcache = get_current_regcache ();
-		  tmp_pc = regcache_read_pc (regcache);
-		  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-						  tmp_pc))
-		    {
-		      /* There is a breakpoint.  GDB will want to stop.  */
-		      struct gdbarch *gdbarch = get_regcache_arch (regcache);
-		      CORE_ADDR decr_pc_after_break
-			= gdbarch_decr_pc_after_break (gdbarch);
-		      if (decr_pc_after_break)
-			regcache_write_pc (regcache,
-					   tmp_pc + decr_pc_after_break);
-		    }
-		  else
-		    {
-		      /* There is not a breakpoint, and gdb is not
-		         stepping, therefore gdb will not stop.
-			 Therefore we will not return to gdb.
-		         Record the insn and resume.  */
-		      if (!do_record_message (regcache, TARGET_SIGNAL_0, 1))
-  			{
-                           status->kind = TARGET_WAITKIND_STOPPED;
-                           status->value.sig = TARGET_SIGNAL_0;
-                           break;
-  			}
+          while (1)
+            {
+              tp = iterate_over_threads (record_thread_wait_callback, &args);
+              if (tp)
+                break;
+              if (record_resume_step
+                  && args.real_inferior_tp
+                  && !args.real_inferior_tp->record_is_waiting)
+                {
+                  *status = args.real_inferior_status;
+                  inferior_ptid = args.real_inferior_ptid;
+                  break;
+                }
+            }
 
-		      record_beneath_to_resume (record_beneath_to_resume_ops,
-						ptid, 1,
-						TARGET_SIGNAL_0);
-		      continue;
-		    }
-		}
+          do_cleanups (mthread_cleanups);
 
-	      /* The inferior is broken by a breakpoint or a signal.  */
-	      break;
+          ret_ptid = inferior_ptid;
+        }
+      else
+        {
+          /* Single-thread record.  */
+          if (record_resume_step)
+            {
+	      /* This is a single step.  */
+	      ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                                 ptid, status, options);
 	    }
+          else
+            {
+              /* This is not a single step.  */
+              while (1)
+                {
+                  if (record_thread_wait (ptid, status,
+                                          options, &ret_ptid))
+                    break;
 
-	  return ret;
-	}
+                  if (record_resume_step)
+                    break;
+
+                  /* There is not a breakpoint, and gdb is not
+                     stepping, therefore gdb will not stop.
+                     Therefore we will not return to gdb.
+                     Record the insn and resume.  */
+		  if (!do_record_message (TARGET_SIGNAL_0, 1))
+                    {
+                      ret_ptid = inferior_ptid;
+                      status->kind = TARGET_WAITKIND_STOPPED;
+                      status->value.sig = TARGET_SIGNAL_0;
+                      break;
+                    }
+
+		  record_beneath_to_resume (record_beneath_to_resume_ops,
+		                            record_resume_ptid, record_step,
+		                            TARGET_SIGNAL_0);
+                }
+            }
+        }
     }
   else
     {
-      struct regcache *regcache = get_current_regcache ();
-      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      /* Replay mode.  */
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
       int continue_flag = 1;
       int first_record_end = 1;
-      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+      struct cleanup *old_cleanups =
+        make_cleanup (record_wait_replay_cleanups, 0);
       CORE_ADDR tmp_pc;
+      ptid_t old_inferior_ptid = inferior_ptid;
+
+      if (!ptid_equal (record_prev_ptid, null_ptid))
+        inferior_ptid = record_prev_ptid;
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
 
       status->kind = TARGET_WAITKIND_STOPPED;
 
@@ -1129,8 +1561,6 @@ record_wait (struct target_ops *ops,
 	    }
 	}
 
-      record_get_sig = 0;
-      signal (SIGINT, record_sig_handler);
       /* If GDB is in terminal_inferior mode, it will not get the signal.
          And in GDB replay mode, GDB doesn't need to be in terminal_inferior
          mode, because inferior will not executed.
@@ -1161,7 +1591,7 @@ record_wait (struct target_ops *ops,
 	      break;
 	    }
 
-          record_exec_insn (regcache, gdbarch, record_list);
+          record_exec_insn (&regcache, &gdbarch, record_list);
 
 	  if (record_list->type == record_end)
 	    {
@@ -1184,12 +1614,13 @@ record_wait (struct target_ops *ops,
 		     In EXEC_FORWARD mode, this is the record_end of current
 		     instruction.  */
 		  /* step */
-		  if (record_resume_step)
+		  if (record_resume_step
+                      && ptid_equal (inferior_ptid, old_inferior_ptid))
 		    {
 		      if (record_debug > 1)
 			fprintf_unfiltered (gdb_stdlog,
 					    "Process record: step.\n");
-		      continue_flag = 0;
+                      continue_flag = 0;
 		    }
 
 		  /* check breakpoint */
@@ -1233,22 +1664,23 @@ record_wait (struct target_ops *ops,
 	}
       while (continue_flag);
 
-      signal (SIGINT, handle_sigint);
-
 replay_out:
-      if (record_get_sig)
-	status->value.sig = TARGET_SIGNAL_INT;
-      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+      if (record_list->u.end.sigval != TARGET_SIGNAL_0)
 	/* FIXME: better way to check */
 	status->value.sig = record_list->u.end.sigval;
       else
 	status->value.sig = TARGET_SIGNAL_TRAP;
+      ret_ptid = inferior_ptid;
 
       discard_cleanups (old_cleanups);
     }
 
+  if (record_get_sig)
+    status->value.sig = TARGET_SIGNAL_INT;
+
+  do_cleanups (signal_cleanups);
   do_cleanups (set_cleanups);
-  return inferior_ptid;
+  return ret_ptid;
 }
 
 /* "to_disconnect" method for process record target.  */
@@ -1311,6 +1743,12 @@ record_registers_change (struct regcache
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
+  if (!ptid_equal (record_prev_ptid, inferior_ptid))
+    {
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
+    }
+
   if (regnum < 0)
     {
       int i;
@@ -1433,6 +1871,11 @@ record_xfer_partial (struct target_ops *
       /* Record registers change to list as an instruction.  */
       record_arch_list_head = NULL;
       record_arch_list_tail = NULL;
+      if (!ptid_equal (record_prev_ptid, inferior_ptid))
+        {
+          record_arch_list_add_ptid (&record_prev_ptid);
+          record_prev_ptid = inferior_ptid;
+        }
       if (record_arch_list_add_mem (offset, len))
 	{
 	  record_list_release (record_arch_list_tail);
@@ -2088,6 +2531,32 @@ record_restore (void)
 				rec->u.mem.len);
           break;
 
+        case record_ptid: /* ptid */
+          rec = record_ptid_alloc ();
+
+	  /* Get Process id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.pid = (int) netorder64 (addr);
+
+	  /* Get Lightweight process id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.lwp = (long) netorder64 (addr);
+
+	  /* Get Thread id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.tid = (long) netorder64 (addr);
+
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog, "\
+  Reading ptid %s (1 + 8 + 8 + 8 bytes), offset == %s\n",
+				target_pid_to_str (record_list->u.ptid),
+				paddress (get_current_arch (),
+					  bfd_offset));
+          break;
+
         case record_end: /* end */
           rec = record_end_alloc ();
           record_insn_num ++;
@@ -2240,7 +2709,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == &record_first)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
@@ -2261,6 +2730,9 @@ cmd_record_save (char *args, int from_tt
       case record_mem:
 	save_size += 1 + 4 + 8 + record_list->u.mem.len;
 	break;
+      case record_ptid:
+	save_size += 1 + 8 + 8 + 8;
+	break;
       }
 
   /* Make the new bfd section.  */
@@ -2347,6 +2819,25 @@ cmd_record_save (char *args, int from_tt
 			     record_list->u.mem.len, &bfd_offset);
               break;
 
+            case record_ptid: /* ptid */
+	      if (record_debug)
+		fprintf_unfiltered (gdb_stdlog, "\
+  Writing ptid %s (1 plus 8 plus 8 plus 8 bytes)\n",
+				    target_pid_to_str (record_list->u.ptid));
+
+	      /* Write Process id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.pid);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+
+	      /* Write Lightweight process id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.lwp);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+
+	      /* Write Thread id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.tid);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+              break;
+
               case record_end:
 		if (record_debug)
 		  fprintf_unfiltered (gdb_stdlog, "\
@@ -2367,7 +2858,7 @@ cmd_record_save (char *args, int from_tt
         }
 
       /* Execute entry.  */
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->next)
         record_list = record_list->next;
@@ -2382,7 +2873,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == cur_record_list)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
--- a/record.h
+++ b/record.h
@@ -23,6 +23,7 @@
 #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);
--- a/thread.c
+++ b/thread.c
@@ -63,15 +63,6 @@ static void thread_apply_command (char *
 static void restore_current_thread (ptid_t);
 static void prune_threads (void);
 
-/* Frontend view of the thread state.  Possible extensions: stepping,
-   finishing, until(ling),...  */
-enum thread_state
-{
-  THREAD_STOPPED,
-  THREAD_RUNNING,
-  THREAD_EXITED,
-};
-
 struct thread_info*
 inferior_thread (void)
 {

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Prec multi-thread support
  2009-11-12  8:26   ` Hui Zhu
@ 2009-11-13  9:27     ` Hui Zhu
  0 siblings, 0 replies; 5+ messages in thread
From: Hui Zhu @ 2009-11-13  9:27 UTC (permalink / raw)
  To: gdb-patches ml; +Cc: Michael Snyder, tromey

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

Hi guys,

The new patch fix some small bugs.

Thanks,
Hui

On Thu, Nov 12, 2009 at 16:25, Hui Zhu <teawater@gmail.com> wrote:
> Hi guys,
>
> This is the new version for multi-thread support.  It make
> multi-thread work OK with record save and support thread exit when
> record.
> I think the code work of prec multi-thread support is done.    I can
> begin to do some make it clear and some other things.
>
> Thanks,
> Hui
>

[-- Attachment #2: prec-thread.txt --]
[-- Type: text/plain, Size: 39263 bytes --]

---
 amd64-linux-tdep.c |    4 
 gdbthread.h        |   11 
 i386-linux-tdep.c  |    4 
 i386-tdep.c        |   52 ++--
 i386-tdep.h        |    6 
 linux-record.c     |   13 -
 linux-record.h     |    2 
 record.c           |  680 +++++++++++++++++++++++++++++++++++++++++++++--------
 record.h           |    1 
 thread.c           |    9 
 10 files changed, 647 insertions(+), 135 deletions(-)

--- a/amd64-linux-tdep.c
+++ b/amd64-linux-tdep.c
@@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li
 #define RECORD_ARCH_GET_GS	0x1004
 
 static int
-amd64_linux_syscall_record (struct regcache *regcache)
+amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   ULONGEST syscall_native;
@@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca
     }
   else
     {
-      ret = record_linux_system_call (syscall_gdb, regcache,
+      ret = record_linux_system_call (syscall_gdb, addr, regcache,
                                       &amd64_linux_record_tdep);
       if (ret)
         return ret;
--- a/gdbthread.h
+++ b/gdbthread.h
@@ -29,6 +29,15 @@ struct symtab;
 #include "ui-out.h"
 #include "inferior.h"
 
+/* Frontend view of the thread state.  Possible extensions: stepping,
+   finishing, until(ling),...  */
+enum thread_state
+{
+  THREAD_STOPPED,
+  THREAD_RUNNING,
+  THREAD_EXITED,
+};
+
 struct thread_info
 {
   struct thread_info *next;
@@ -37,6 +46,8 @@ struct thread_info
 				    kernel thread id, etc.  */
   int num;			/* Convenient handle (GDB thread id) */
 
+  int record_is_waiting;
+
   /* Non-zero means the thread is executing.  Note: this is different
      from saying that there is an active target and we are stopped at
      a breakpoint, for instance.  This is a real indicator whether the
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall)
 static struct linux_record_tdep i386_linux_record_tdep;
 
 static int
-i386_linux_intx80_sysenter_record (struct regcache *regcache)
+i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr)
 {
   int ret;
   LONGEST syscall_native;
@@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc
      return 0;
    }
 
-  ret = record_linux_system_call (syscall_gdb, regcache,
+  ret = record_linux_system_call (syscall_gdb, addr, regcache,
 				  &i386_linux_record_tdep);
   if (ret)
     return ret;
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor
 
   if (irp->override >= 0)
     {
-      warning (_("Process record ignores the memory change "
-                 "of instruction at address %s because it "
-                 "can't get the value of the segment register."),
-               paddress (gdbarch, irp->orig_addr));
+      if (record_debug)
+        warning (_("Process record ignores the memory change "
+                   "of instruction at address %s because it "
+                   "can't get the value of the segment register."),
+                 paddress (gdbarch, irp->orig_addr));
       return 0;
     }
 
@@ -4042,11 +4043,12 @@ reswitch:
     case 0xa3:
       if (ir.override >= 0)
         {
-	  warning (_("Process record ignores the memory change "
-                     "of instruction at address %s because "
-                     "it can't get the value of the segment "
-                     "register."),
-                   paddress (gdbarch, ir.orig_addr));
+          if (record_debug)
+	    warning (_("Process record ignores the memory change "
+                       "of instruction at address %s because "
+                       "it can't get the value of the segment "
+                       "register."),
+                     paddress (gdbarch, ir.orig_addr));
 	}
       else
 	{
@@ -4467,11 +4469,12 @@ reswitch:
           if (ir.aflag && (es != ds))
             {
               /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */
-              warning (_("Process record ignores the memory "
-                         "change of instruction at address %s "
-                         "because it can't get the value of the "
-                         "ES segment register."),
-                       paddress (gdbarch, ir.orig_addr));
+              if (record_debug)
+                warning (_("Process record ignores the memory "
+                           "change of instruction at address %s "
+                           "because it can't get the value of the "
+                           "ES segment register."),
+                         paddress (gdbarch, ir.orig_addr));
             }
           else
             {
@@ -4873,7 +4876,7 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -4975,7 +4978,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache,
+                                                            ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5000,7 +5004,8 @@ reswitch:
 	    ir.addr -= 2;
 	    goto no_support;
 	  }
-	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache);
+	ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache,
+                                                           ir.addr);
 	if (ret)
 	  return ret;
       }
@@ -5136,12 +5141,13 @@ reswitch:
 	      /* sidt */
 	      if (ir.override >= 0)
 		{
-		  warning (_("Process record ignores the memory "
-                             "change of instruction at "
-                             "address %s because it can't get "
-                             "the value of the segment "
-                             "register."),
-                           paddress (gdbarch, ir.orig_addr));
+                  if (record_debug)
+		    warning (_("Process record ignores the memory "
+                               "change of instruction at "
+                               "address %s because it can't get "
+                               "the value of the segment "
+                               "register."),
+                             paddress (gdbarch, ir.orig_addr));
 		}
 	      else
 		{
--- a/i386-tdep.h
+++ b/i386-tdep.h
@@ -115,11 +115,11 @@ struct gdbarch_tdep
      in GDB is not same as I386 instructions.  */
   const int *record_regmap;
   /* Parse intx80 args.  */
-  int (*i386_intx80_record) (struct regcache *regcache);
+  int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse sysenter args.  */
-  int (*i386_sysenter_record) (struct regcache *regcache);
+  int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr);
   /* Parse syscall args.  */
-  int (*i386_syscall_record) (struct regcache *regcache);
+  int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr);
 };
 
 /* Floating-point registers.  */
--- a/linux-record.c
+++ b/linux-record.c
@@ -21,6 +21,7 @@
 #include "target.h"
 #include "gdbtypes.h"
 #include "regcache.h"
+#include "inferior.h"
 #include "record.h"
 #include "linux-record.h"
 
@@ -222,7 +223,7 @@ record_linux_msghdr (struct regcache *re
    Return -1 if something wrong.  */
 
 int
-record_linux_system_call (enum gdb_syscall syscall, 
+record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr,
 			  struct regcache *regcache,
                           struct linux_record_tdep *tdep)
 {
@@ -242,8 +243,9 @@ record_linux_system_call (enum gdb_sysca
         int q;
         target_terminal_ours ();
         q = yquery (_("The next instruction is syscall exit.  "
-                      "It will make the program exit.  "
-                      "Do you want to stop the program?"));
+                      "It will make the thread %s exit.  "
+                      "Do you want to stop the program?"),
+                    target_pid_to_str (inferior_ptid));
         target_terminal_inferior ();
         if (q)
           return 1;
@@ -1209,10 +1211,13 @@ record_linux_system_call (enum gdb_sysca
 
     case gdb_sys_fsync:
     case gdb_sys_sigreturn:
-    case gdb_sys_clone:
     case gdb_sys_setdomainname:
       break;
 
+    case gdb_sys_clone:
+      record_step = 0;
+      break;
+
     case gdb_sys_newuname:
       regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest);
       if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest,
--- a/linux-record.h
+++ b/linux-record.h
@@ -534,7 +534,7 @@ enum gdb_syscall {
 
 /* Record a linux syscall.  */
 
-extern int record_linux_system_call (enum gdb_syscall num, 
+extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr,
 				     struct regcache *regcache,
 				     struct linux_record_tdep *tdep);
 #endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -30,6 +30,7 @@
 #include "record.h"
 #include "elf-bfd.h"
 #include "gcore.h"
+#include "observer.h"
 
 #include <signal.h>
 
@@ -60,6 +61,8 @@
 
 #define RECORD_FILE_MAGIC	netorder32(0x20091016)
 
+#define RECORD_THREAD	(record_ops.beneath->to_stratum == thread_stratum)
+
 /* These are the core structs of the process record functionality.
 
    A record_entry is a record of the value change of a register
@@ -106,7 +109,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.
@@ -144,6 +148,8 @@ struct record_entry
     struct record_reg_entry reg;
     /* mem */
     struct record_mem_entry mem;
+    /* ptid */
+    ptid_t ptid;
     /* end */
     struct record_end_entry end;
   } u;
@@ -196,10 +202,19 @@ static int record_insn_num = 0;
    than count of insns presently in execution log).  */
 static ULONGEST record_insn_count;
 
+/* In record mode, it's the step of next resume.  */
+int record_step;
+
+/* The current inferior_ptid that is recorded.  */
+static ptid_t record_prev_ptid;
+
 /* The target_ops of process record.  */
 static struct target_ops record_ops;
 static struct target_ops record_core_ops;
 
+static struct observer *record_new_thread_observer = NULL;
+static struct observer *record_thread_exit_observer = NULL;
+
 /* 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,
@@ -285,6 +300,27 @@ record_mem_release (struct record_entry 
   xfree (rec);
 }
 
+/* Alloc a record_ptid record entry.  */
+
+static inline struct record_entry *
+record_ptid_alloc (void)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_ptid;
+
+  return rec;
+}
+
+/* Free a record_ptid record entry.  */
+
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+  xfree (rec);
+}
+
 /* Alloc a record_end record entry.  */
 
 static inline struct record_entry *
@@ -321,6 +357,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;
@@ -328,6 +367,16 @@ record_entry_release (struct record_entr
   return type;
 }
 
+/* Remove one record entry from record_list.  */
+
+static void
+record_entry_remove_from_list (struct record_entry *rec)
+{
+  if (rec->next)
+    rec->next->prev = rec->prev;
+  rec->prev->next = rec->next;
+}
+
 /* Free all record entries in list pointed to by REC.  */
 
 static void
@@ -509,6 +558,24 @@ record_arch_list_add_mem (CORE_ADDR addr
   return 0;
 }
 
+static void
+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 = record_ptid_alloc ();
+
+  rec->u.ptid = *ptid;
+
+  record_arch_list_add (rec);
+}
+
 /* Add a record_end type struct record_entry to record_arch_list.  */
 
 int
@@ -570,7 +637,6 @@ record_arch_list_cleanups (void *ignore)
    record_arch_list, and add it to record_list.  */
 
 struct record_message_args {
-  struct regcache *regcache;
   enum target_signal signal;
 };
 
@@ -579,15 +645,21 @@ record_message (void *args)
 {
   int ret;
   struct record_message_args *myargs = args;
-  struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+  struct regcache *regcache;
+  struct gdbarch *gdbarch;
   struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
+  record_step = 1;
+
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
   /* Check record_insn_num.  */
   record_check_insn_num (1);
 
+  regcache = get_current_regcache ();
+  gdbarch = get_regcache_arch (regcache);
+
   /* If gdb sends a signal value to target_resume,
      save it in the 'end' field of the previous instruction.
 
@@ -610,20 +682,45 @@ record_message (void *args)
      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 */
+  if (ptid_equal (record_prev_ptid, inferior_ptid))
     {
-      gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = myargs->signal;
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          gdb_assert (record_list->type == record_end);
+          record_list->u.end.sigval = myargs->signal;
+        }
+    }
+  else
+    {
+      if (myargs->signal != TARGET_SIGNAL_0)
+        {
+          struct record_entry *rec;
+          for (rec = record_list; rec != record_first.next; rec = rec->prev)
+            {
+              if (rec->type == record_ptid
+                  && ptid_equal (rec->u.ptid, inferior_ptid))
+                {
+                  gdb_assert (rec->prev->type == record_end);
+                  rec->prev->u.end.sigval = myargs->signal;
+                  break;
+                }
+            }
+        }
+
+      /* If the inferior_ptid change (inferior_ptid is not same with
+         record_prev_ptid), record record_prev_ptid to record list.  */
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
     }
 
   if (myargs->signal == TARGET_SIGNAL_0
       || !gdbarch_process_record_signal_p (gdbarch))
     ret = gdbarch_process_record (gdbarch,
-				  myargs->regcache,
-				  regcache_read_pc (myargs->regcache));
+				  regcache,
+				  regcache_read_pc (regcache));
   else
     ret = gdbarch_process_record_signal (gdbarch,
-					 myargs->regcache,
+					 regcache,
 					 myargs->signal);
 
   if (ret > 0)
@@ -646,12 +743,10 @@ record_message (void *args)
 }
 
 static int
-do_record_message (struct regcache *regcache,
-		   enum target_signal signal, int catch)
+do_record_message (enum target_signal signal, int catch)
 {
   struct record_message_args args;
 
-  args.regcache = regcache;
   args.signal = signal;
 
   if (catch)
@@ -682,9 +777,12 @@ record_gdb_operation_disable_set (void)
    entries and memory entries, followed by an 'end' entry.  */
 
 static inline void
-record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
+record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p,
 		  struct record_entry *entry)
 {
+  struct regcache *regcache = *regcache_p;
+  struct gdbarch *gdbarch = *gdbarch_p;
+
   switch (entry->type)
     {
     case record_reg: /* reg */
@@ -748,6 +846,103 @@ record_exec_insn (struct regcache *regca
           }
       }
       break;
+
+    case record_ptid: /* ptid */
+      {
+        ptid_t tmp_ptid;
+
+        if (record_debug > 1)
+          fprintf_unfiltered (gdb_stdlog,
+                              "Process record: record_ptid %s to "
+                              "inferior ptid = %s.\n",
+                              host_address_to_string (entry),
+                              target_pid_to_str (entry->u.ptid));
+
+        if (!ptid_equal (entry->u.ptid, null_ptid))
+          {
+            inferior_ptid = entry->u.ptid;
+            *regcache_p = get_current_regcache ();
+            *gdbarch_p = get_regcache_arch (*regcache_p);
+          }
+        tmp_ptid = entry->u.ptid;
+        entry->u.ptid = record_prev_ptid;
+        record_prev_ptid = tmp_ptid;
+      }
+      break;
+    }
+}
+
+static int record_thread_number;
+
+static int
+record_thread_number_count (struct thread_info *thread, void *unused)
+{
+  if (thread->state_ != THREAD_EXITED)
+    record_thread_number ++;
+
+  return 0;
+}
+
+static void
+record_thread_number_reset (void)
+{
+  record_thread_number = 0;
+
+  if (RECORD_THREAD)
+    iterate_over_threads (record_thread_number_count, NULL);
+}
+
+static void
+record_new_thread_handler (struct thread_info *tp)
+{
+  tp->record_is_waiting = 0;
+
+  record_thread_number_reset ();
+}
+
+static void
+record_thread_exit_handler (struct thread_info *tp, int silent)
+{
+  struct record_entry *rec;
+  ptid_t cur_ptid = record_prev_ptid;
+
+  if (!record_first.next)
+    return;
+
+  tp->record_is_waiting = 0;
+
+  if (tp->state_ != THREAD_EXITED)
+    {
+      gdb_assert (record_thread_number > 0);
+      record_thread_number --;
+    }
+
+  /* Delete all the record_reg and record_ptid for tp->ptid
+     from record list.  */
+  for (rec = record_list; rec != &record_first;)
+    {
+      struct record_entry *tmp = rec;
+      rec = rec->prev;
+
+      switch (tmp->type) {
+        case record_reg:
+          /* If this record_reg is for tp->ptid, delte it.  */
+          if (ptid_equal (cur_ptid, tp->ptid))
+            {
+              record_entry_remove_from_list (tmp);
+              record_entry_release (tmp);
+            }
+          break;
+        case record_ptid:
+          /* If this record_ptid is for tp->ptid, delte it.  */
+          cur_ptid = tmp->u.ptid;
+          if (ptid_equal (cur_ptid, tp->ptid))
+            {
+              record_entry_remove_from_list (tmp);
+              record_entry_release (tmp);
+            }
+          break;
+      }
     }
 }
 
@@ -842,6 +1037,9 @@ record_open_1 (char *name, int from_tty)
     error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
 
   push_target (&record_ops);
+
+  record_new_thread_observer = observer_attach_new_thread (record_new_thread_handler);
+  record_thread_exit_observer = observer_attach_thread_exit (record_thread_exit_handler);
 }
 
 /* "to_open" target method.  Open the process record target.  */
@@ -907,6 +1105,7 @@ record_open (char *name, int from_tty)
   record_insn_count = 0;
   record_list = &record_first;
   record_list->next = NULL;
+  record_prev_ptid = null_ptid;
 
   /* Set the tmp beneath pointers to beneath pointers.  */
   record_beneath_to_resume_ops = tmp_to_resume_ops;
@@ -924,6 +1123,8 @@ record_open (char *name, int from_tty)
     record_core_open_1 (name, from_tty);
   else
     record_open_1 (name, from_tty);
+
+  record_thread_number_reset ();
 }
 
 /* "to_close" target method.  Close the process record target.  */
@@ -936,6 +1137,17 @@ record_close (int quitting)
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
 
+  if (record_new_thread_observer)
+    {
+      observer_detach_new_thread (record_new_thread_observer);
+      record_new_thread_observer = NULL;
+    }
+  if (record_thread_exit_observer)
+    {
+      observer_detach_thread_exit (record_thread_exit_observer);
+      record_thread_exit_observer = NULL;
+    }
+
   record_list_release (record_list);
 
   /* Release record_core_regbuf.  */
@@ -958,6 +1170,8 @@ record_close (int quitting)
 }
 
 static int record_resume_step = 0;
+static ptid_t record_resume_ptid;
+static enum target_signal record_resume_signal = TARGET_SIGNAL_0;
 
 /* "to_resume" target method.  Resume the process record target.  */
 
@@ -969,10 +1183,186 @@ record_resume (struct target_ops *ops, p
 
   if (!RECORD_IS_REPLAY)
     {
-      do_record_message (get_current_regcache (), signal, 0);
-      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
-                                signal);
+      record_resume_ptid = ptid;
+      if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid))
+        {
+          record_resume_signal = signal;
+        }
+      else
+        {
+          do_record_message (signal, 0);
+          record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+                                    record_step, signal);
+        }
+    }
+}
+
+struct record_thread_wait_args {
+  ptid_t real_inferior_ptid;
+  struct thread_info *real_inferior_tp;
+  struct target_waitstatus real_inferior_status;
+
+  struct target_waitstatus *status;
+  int options;
+};
+
+static int
+record_thread_reset_callback (struct thread_info *tp, void *data)
+{
+  tp->record_is_waiting = 0;
+
+  return 0;
+}
+
+static int
+record_thread_stop_callback (struct thread_info *tp, void *data)
+{
+  if (tp->state_ != THREAD_EXITED && tp->record_is_waiting)
+    target_stop (tp->ptid);
+
+  return 0;
+}
+
+/* Return 1 if it is not a simple step.
+   Return 0 if it is a simple step.  */
+
+static int
+record_thread_wait (ptid_t ptid, struct target_waitstatus *status,
+                    int options, ptid_t *ret_ptid)
+{
+  ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                             ptid, status, options);
+
+  if (ret_ptid)
+    *ret_ptid = wait_ptid;
+
+  if (status->kind == TARGET_WAITKIND_IGNORE)
+    return 0;
+
+  inferior_ptid = wait_ptid;
+
+  /* Is this a SIGTRAP?  */
+  if (status->kind == TARGET_WAITKIND_STOPPED
+      && status->value.sig == TARGET_SIGNAL_TRAP)
+    {
+      CORE_ADDR tmp_pc;
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
+
+      /* Yes -- check if there is a breakpoint.  */
+      registers_changed ();
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
+      tmp_pc = regcache_read_pc (regcache);
+      if (!record_step)
+        tmp_pc -= gdbarch_decr_pc_after_break (gdbarch);
+      if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc))
+        {
+          /* There is a breakpoint.  GDB will want to stop.  */
+          if (record_step)
+            {
+              CORE_ADDR decr_pc_after_break
+                          = gdbarch_decr_pc_after_break (gdbarch);
+              if (decr_pc_after_break)
+                regcache_write_pc (regcache, tmp_pc + decr_pc_after_break);
+            }
+
+          return 1;
+        }
+
+      return 0;
+    }
+
+  return 1;
+}
+
+static int
+record_thread_wait_callback (struct thread_info *tp, void *data)
+{
+  struct record_thread_wait_args *args = data;
+  enum target_signal sig = TARGET_SIGNAL_0;
+  int options = args->options;
+  int ret;
+
+  if (tp->state_ == THREAD_EXITED)
+    return 0;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+                        "Process record: record_thread_wait_callback "
+                        "resume %s\n", target_pid_to_str (tp->ptid));
+
+  inferior_ptid = tp->ptid;
+
+  if (!tp->record_is_waiting)
+    {
+      if (record_resume_signal != TARGET_SIGNAL_0
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        {
+          sig = record_resume_signal;
+          record_resume_signal = TARGET_SIGNAL_0;
+        }
+      if (!do_record_message (sig, 1))
+        {
+          args->status->kind = TARGET_WAITKIND_STOPPED;
+          args->status->value.sig = TARGET_SIGNAL_0;
+          return 1;
+        }
+
+      if (record_step == 0)
+        {
+          /* The next insn is a sys_clone.  */
+          iterate_over_threads (record_thread_stop_callback, NULL);
+          non_stop = 0;
+        }
+
+      record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid,
+                                record_step, sig);
     }
+  else
+    {
+      if (record_debug > 1)
+        fprintf_unfiltered (gdb_stdlog,
+                            "Process record: record_thread_wait_callback "
+                            "not resume %s\n", target_pid_to_str (tp->ptid));
+    }
+
+  if (record_step)
+    options |= TARGET_WNOHANG;
+  ret = record_thread_wait (inferior_ptid, args->status,
+                            options, NULL);
+  if (args->status->kind == TARGET_WAITKIND_IGNORE)
+    {
+      if (!tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 1;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "start waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+    }
+  else
+    {
+      if (tp->record_is_waiting)
+        {
+          tp->record_is_waiting = 0;
+          if (record_debug > 1)
+            fprintf_unfiltered (gdb_stdlog,
+                                "Process record: record_thread_wait_callback "
+                                "stop waiting %s.\n",
+			        target_pid_to_str (tp->ptid));
+        }
+
+      if (!args->real_inferior_tp
+          && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+        args->real_inferior_tp = tp;
+      if (args->real_inferior_tp == tp)
+        args->real_inferior_status = *args->status;
+    }
+
+  return ret;
 }
 
 static int record_get_sig = 0;
@@ -994,7 +1384,13 @@ record_sig_handler (int signo)
 }
 
 static void
-record_wait_cleanups (void *ignore)
+record_wait_signal_cleanups (void *ignore)
+{
+  signal (SIGINT, handle_sigint);
+}
+
+static void
+record_wait_replay_cleanups (void *ignore)
 {
   if (execution_direction == EXEC_REVERSE)
     {
@@ -1005,6 +1401,17 @@ record_wait_cleanups (void *ignore)
     record_list = record_list->prev;
 }
 
+static void
+record_wait_mthread_cleanups (void *ignore)
+{
+  non_stop = 1;
+
+  /* Stop the threads that still running.  */
+  iterate_over_threads (record_thread_stop_callback, NULL);
+
+  non_stop = 0;
+}
+
 /* "to_wait" target method for process record target.
 
    In record mode, the target is always run in singlestep mode
@@ -1023,7 +1430,10 @@ record_wait (struct target_ops *ops,
 	     ptid_t ptid, struct target_waitstatus *status,
 	     int options)
 {
+  ptid_t ret_ptid;
   struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct cleanup *signal_cleanups
+                    = make_cleanup (record_wait_signal_cleanups, 0);
 
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -1031,81 +1441,110 @@ record_wait (struct target_ops *ops,
 			"record_resume_step = %d\n",
 			record_resume_step);
 
+  record_get_sig = 0;
+  signal (SIGINT, record_sig_handler);
+
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_step)
-	{
-	  /* This is a single step.  */
-	  return record_beneath_to_wait (record_beneath_to_wait_ops,
-					 ptid, status, options);
-	}
-      else
-	{
-	  /* This is not a single step.  */
-	  ptid_t ret;
-	  CORE_ADDR tmp_pc;
+      /* Record mode.  */
+      if (record_thread_number > 1 && !ptid_equal (record_resume_ptid,
+                                                   inferior_ptid))
+        {
+          /* Multi-threads record.  */
+          struct record_thread_wait_args args;
+          struct thread_info *tp;
+          struct cleanup *mthread_cleanups
+                            = make_cleanup (record_wait_mthread_cleanups, 0);
 
-	  while (1)
-	    {
-	      ret = record_beneath_to_wait (record_beneath_to_wait_ops,
-					    ptid, status, options);
+          args.real_inferior_ptid = inferior_ptid;
+          args.real_inferior_tp = NULL;
+          args.status = status;
+          args.options = options;
+          non_stop = 1;
+          iterate_over_threads (record_thread_reset_callback, NULL);
 
-	      /* Is this a SIGTRAP?  */
-	      if (status->kind == TARGET_WAITKIND_STOPPED
-		  && status->value.sig == TARGET_SIGNAL_TRAP)
-		{
-		  struct regcache *regcache;
+          while (1)
+            {
+              tp = iterate_over_threads (record_thread_wait_callback, &args);
+              if (tp)
+                break;
+              if (record_resume_step
+                  && args.real_inferior_tp
+                  && !args.real_inferior_tp->record_is_waiting)
+                {
+                  *status = args.real_inferior_status;
+                  inferior_ptid = args.real_inferior_ptid;
+                  break;
+                }
+            }
 
-		  /* Yes -- check if there is a breakpoint.  */
-		  registers_changed ();
-		  regcache = get_current_regcache ();
-		  tmp_pc = regcache_read_pc (regcache);
-		  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-						  tmp_pc))
-		    {
-		      /* There is a breakpoint.  GDB will want to stop.  */
-		      struct gdbarch *gdbarch = get_regcache_arch (regcache);
-		      CORE_ADDR decr_pc_after_break
-			= gdbarch_decr_pc_after_break (gdbarch);
-		      if (decr_pc_after_break)
-			regcache_write_pc (regcache,
-					   tmp_pc + decr_pc_after_break);
-		    }
-		  else
-		    {
-		      /* There is not a breakpoint, and gdb is not
-		         stepping, therefore gdb will not stop.
-			 Therefore we will not return to gdb.
-		         Record the insn and resume.  */
-		      if (!do_record_message (regcache, TARGET_SIGNAL_0, 1))
-  			{
-                           status->kind = TARGET_WAITKIND_STOPPED;
-                           status->value.sig = TARGET_SIGNAL_0;
-                           break;
-  			}
+          do_cleanups (mthread_cleanups);
 
-		      record_beneath_to_resume (record_beneath_to_resume_ops,
-						ptid, 1,
-						TARGET_SIGNAL_0);
-		      continue;
-		    }
-		}
+          if (tp && tp->state_ != THREAD_EXITED)
+            {
+              inferior_ptid = tp->ptid;
+            }
+          ret_ptid = inferior_ptid;
+        }
+      else
+        {
+          /* Single-thread record.  */
+          if (record_thread_number > 1)
+            ptid = record_resume_ptid;
 
-	      /* The inferior is broken by a breakpoint or a signal.  */
-	      break;
+          if (record_resume_step)
+            {
+	      /* This is a single step.  */
+	      ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+                                                 ptid, status, options);
 	    }
+          else
+            {
+              /* This is not a single step.  */
+              while (1)
+                {
+                  if (record_thread_wait (ptid, status,
+                                          options, &ret_ptid))
+                    break;
 
-	  return ret;
-	}
+                  if (record_resume_step)
+                    break;
+
+                  /* There is not a breakpoint, and gdb is not
+                     stepping, therefore gdb will not stop.
+                     Therefore we will not return to gdb.
+                     Record the insn and resume.  */
+		  if (!do_record_message (TARGET_SIGNAL_0, 1))
+                    {
+                      ret_ptid = inferior_ptid;
+                      status->kind = TARGET_WAITKIND_STOPPED;
+                      status->value.sig = TARGET_SIGNAL_0;
+                      break;
+                    }
+
+		  record_beneath_to_resume (record_beneath_to_resume_ops,
+		                            record_resume_ptid, record_step,
+		                            TARGET_SIGNAL_0);
+                }
+            }
+        }
     }
   else
     {
-      struct regcache *regcache = get_current_regcache ();
-      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      /* Replay mode.  */
+      struct regcache *regcache;
+      struct gdbarch *gdbarch;
       int continue_flag = 1;
       int first_record_end = 1;
-      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+      struct cleanup *old_cleanups =
+        make_cleanup (record_wait_replay_cleanups, 0);
       CORE_ADDR tmp_pc;
+      ptid_t old_inferior_ptid = inferior_ptid;
+
+      if (!ptid_equal (record_prev_ptid, null_ptid))
+        inferior_ptid = record_prev_ptid;
+      regcache = get_current_regcache ();
+      gdbarch = get_regcache_arch (regcache);
 
       status->kind = TARGET_WAITKIND_STOPPED;
 
@@ -1129,8 +1568,6 @@ record_wait (struct target_ops *ops,
 	    }
 	}
 
-      record_get_sig = 0;
-      signal (SIGINT, record_sig_handler);
       /* If GDB is in terminal_inferior mode, it will not get the signal.
          And in GDB replay mode, GDB doesn't need to be in terminal_inferior
          mode, because inferior will not executed.
@@ -1161,7 +1598,7 @@ record_wait (struct target_ops *ops,
 	      break;
 	    }
 
-          record_exec_insn (regcache, gdbarch, record_list);
+          record_exec_insn (&regcache, &gdbarch, record_list);
 
 	  if (record_list->type == record_end)
 	    {
@@ -1184,12 +1621,13 @@ record_wait (struct target_ops *ops,
 		     In EXEC_FORWARD mode, this is the record_end of current
 		     instruction.  */
 		  /* step */
-		  if (record_resume_step)
+		  if (record_resume_step
+                      && ptid_equal (inferior_ptid, old_inferior_ptid))
 		    {
 		      if (record_debug > 1)
 			fprintf_unfiltered (gdb_stdlog,
 					    "Process record: step.\n");
-		      continue_flag = 0;
+                      continue_flag = 0;
 		    }
 
 		  /* check breakpoint */
@@ -1233,22 +1671,23 @@ record_wait (struct target_ops *ops,
 	}
       while (continue_flag);
 
-      signal (SIGINT, handle_sigint);
-
 replay_out:
-      if (record_get_sig)
-	status->value.sig = TARGET_SIGNAL_INT;
-      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+      if (record_list->u.end.sigval != TARGET_SIGNAL_0)
 	/* FIXME: better way to check */
 	status->value.sig = record_list->u.end.sigval;
       else
 	status->value.sig = TARGET_SIGNAL_TRAP;
+      ret_ptid = inferior_ptid;
 
       discard_cleanups (old_cleanups);
     }
 
+  if (record_get_sig)
+    status->value.sig = TARGET_SIGNAL_INT;
+
+  do_cleanups (signal_cleanups);
   do_cleanups (set_cleanups);
-  return inferior_ptid;
+  return ret_ptid;
 }
 
 /* "to_disconnect" method for process record target.  */
@@ -1311,6 +1750,12 @@ record_registers_change (struct regcache
   record_arch_list_head = NULL;
   record_arch_list_tail = NULL;
 
+  if (!ptid_equal (record_prev_ptid, inferior_ptid))
+    {
+      record_arch_list_add_ptid (&record_prev_ptid);
+      record_prev_ptid = inferior_ptid;
+    }
+
   if (regnum < 0)
     {
       int i;
@@ -1433,6 +1878,11 @@ record_xfer_partial (struct target_ops *
       /* Record registers change to list as an instruction.  */
       record_arch_list_head = NULL;
       record_arch_list_tail = NULL;
+      if (!ptid_equal (record_prev_ptid, inferior_ptid))
+        {
+          record_arch_list_add_ptid (&record_prev_ptid);
+          record_prev_ptid = inferior_ptid;
+        }
       if (record_arch_list_add_mem (offset, len))
 	{
 	  record_list_release (record_arch_list_tail);
@@ -2088,6 +2538,32 @@ record_restore (void)
 				rec->u.mem.len);
           break;
 
+        case record_ptid: /* ptid */
+          rec = record_ptid_alloc ();
+
+	  /* Get Process id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.pid = (int) netorder64 (addr);
+
+	  /* Get Lightweight process id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.lwp = (long) netorder64 (addr);
+
+	  /* Get Thread id.  */
+	  bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  rec->u.ptid.tid = (long) netorder64 (addr);
+
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog, "\
+  Reading ptid %s (1 + 8 + 8 + 8 bytes), offset == %s\n",
+				target_pid_to_str (record_list->u.ptid),
+				paddress (get_current_arch (),
+					  bfd_offset));
+          break;
+
         case record_end: /* end */
           rec = record_end_alloc ();
           record_insn_num ++;
@@ -2240,7 +2716,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == &record_first)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
@@ -2261,6 +2737,9 @@ cmd_record_save (char *args, int from_tt
       case record_mem:
 	save_size += 1 + 4 + 8 + record_list->u.mem.len;
 	break;
+      case record_ptid:
+	save_size += 1 + 8 + 8 + 8;
+	break;
       }
 
   /* Make the new bfd section.  */
@@ -2347,6 +2826,25 @@ cmd_record_save (char *args, int from_tt
 			     record_list->u.mem.len, &bfd_offset);
               break;
 
+            case record_ptid: /* ptid */
+	      if (record_debug)
+		fprintf_unfiltered (gdb_stdlog, "\
+  Writing ptid %s (1 plus 8 plus 8 plus 8 bytes)\n",
+				    target_pid_to_str (record_list->u.ptid));
+
+	      /* Write Process id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.pid);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+
+	      /* Write Lightweight process id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.lwp);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+
+	      /* Write Thread id.  */
+	      addr = netorder64 ((uint64_t) record_list->u.ptid.tid);
+	      bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset);
+              break;
+
               case record_end:
 		if (record_debug)
 		  fprintf_unfiltered (gdb_stdlog, "\
@@ -2367,7 +2865,7 @@ cmd_record_save (char *args, int from_tt
         }
 
       /* Execute entry.  */
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->next)
         record_list = record_list->next;
@@ -2382,7 +2880,7 @@ cmd_record_save (char *args, int from_tt
       if (record_list == cur_record_list)
         break;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+      record_exec_insn (&regcache, &gdbarch, record_list);
 
       if (record_list->prev)
         record_list = record_list->prev;
--- a/record.h
+++ b/record.h
@@ -23,6 +23,7 @@
 #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);
--- a/thread.c
+++ b/thread.c
@@ -63,15 +63,6 @@ static void thread_apply_command (char *
 static void restore_current_thread (ptid_t);
 static void prune_threads (void);
 
-/* Frontend view of the thread state.  Possible extensions: stepping,
-   finishing, until(ling),...  */
-enum thread_state
-{
-  THREAD_STOPPED,
-  THREAD_RUNNING,
-  THREAD_EXITED,
-};
-
 struct thread_info*
 inferior_thread (void)
 {

[-- Attachment #3: prec-x86-add-insn.txt --]
[-- Type: text/plain, Size: 734 bytes --]

---
 i386-tdep.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -4847,9 +4847,6 @@ reswitch:
       /* int3 */
       /* XXX */
     case 0xcc:
-      printf_unfiltered (_("Process record doesn't support instruction "
-			   "int3.\n"));
-      ir.addr -= 1;
       goto no_support;
       break;
 
@@ -4958,10 +4955,8 @@ reswitch:
 
       /* rdtsc */
     case 0x0f31:
-      printf_unfiltered (_("Process record doesn't support "
-			   "instruction rdtsc.\n"));
-      ir.addr -= 2;
-      goto no_support;
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM);
+      I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM);
       break;
 
       /* sysenter */

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2009-11-13  9:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu
2009-11-02 18:14 ` Michael Snyder
2009-11-09 18:00 ` Tom Tromey
2009-11-12  8:26   ` Hui Zhu
2009-11-13  9:27     ` Hui Zhu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox