Re: [RFA/RFC] Prec multi-thread support [4/4] record target

Hui Zhu
2009-11-26T07:36:46Z

Sorry for I forget it. To use this patch need patch
http://sourceware.org/ml/gdb-patches/2009-11/msg00520.html first.

prec-fix-error-handler.txt is still in discussion. But looks we are moving on.
If we don't finish prec-fix-error-handler.txt in one or two days, I
will post a new prec-thread-target.txt for cvs head.

BTW, even if new prec-fix-error-handler.txt check in, I still need
post a new prec-thread-target.txt too. :P

Thanks,
Hui

On Thu, Nov 26, 2009 at 03:35, Michael Snyder <msnyder@vmware.com> wrote:
> Hi Hui,
>
> This one (4 of 4) does not seem to apply cleanly against cvs head
> (record.c is cvs revision 1.40).
>
> Since the rejected diffs seem to have to do with do_record_message,
> I'm guessing that you may have experienced some "bleed" between this
> patch and another one that you're working on.
>
> Can you sort it out?  This patch should only include thread
> support, not fixes for the do_record_message issue.
>
> Thanks,
> Michael
>
>
> Hui Zhu wrote:
>>
>> To record the multithread inferior, record let each thread exec one by
>> one. For example:
>> Inferior have 2 threads t1 and t2.
>> 1. set t1 to inferior_ptid.
>> 2. record_message t1.
>> 3. resume t1.
>> 4. wait t1.
>> 5. Check if t1 get breakpoint or someting.  If so, return.
>> 5. set t2 to inferior_ptid.
>> 6. record_message t2.
>> 7. resume t2.
>> 8. wait t2.
>> 9. Check if t2 get breakpoint or someting.  If so, return.
>> 10. Check if step, if so return.  If not, goto 1.
>>
>> But there is a problem that thread will hang by a lock sometime.  If
>> t1 is waiting a lock and t2 have it.  GDB and inferior will dead lock
>> in there.
>> So record call wait with TARGET_WNOHANG. After we get
>> TARGET_WAITKIND_IGNORE (thread is keep running), we will set
>> tp->record_is_waiting to 1 that flag thread is running.
>> And record set non_stop to 1, because if we want linux-nat handle 2
>> thread running.  This is the only way.  If not set, GDB will get a lot
>> of internal_error.
>> Then the inferior exec will be:
>> 1. set t1 to inferior_ptid.
>> 2. If t1->record_is_waiting is 0, record_message t1.
>> 3. If t1->record_is_waiting is 0, resume t1.
>> 4. wait t1.
>> 5. If get TARGET_WAITKIND_IGNORE, set t1->record_is_waiting to 1.
>> Check if t1 get breakpoint or someting.  If so, return.
>> 5. set t2 to inferior_ptid.
>> 6. If t2->record_is_waiting is 0, record_message t2.
>> 7. If t2->record_is_waiting is 0, resume t2.
>> 8. wait t2.
>> 9. If get TARGET_WAITKIND_IGNORE, set t2->record_is_waiting to 1.
>> Check if t2 get breakpoint or someting.  If so, return.
>> 10. Check if step, if so return.  If not, goto 1.
>>
>> Before record_wait return, it will call iterate_over_threads
>> (record_thread_stop_callback, NULL) to stop each thread that
>> record_is_waiting is 1.
>>
>>
>> And if this is a single thread or just one thread exec, record will
>> exec it like before.
>> record_new_thread_observer and record_thread_exit_observer make record
>> know how much thread in inferior.
>>
>>
>> 2009-11-25  Hui Zhu  <teawater@gmail.com>
>>
>>        * record.c (observer.h): New include.
>>        (RECORD_THREAD): New macro.
>>        (record_type): Add record_ptid.
>>        (record_entry): Add ptid.
>>        (record_prev_ptid,
>>        record_new_thread_observer,
>>        record_thread_exit_observer): New variable.
>>        (record_ptid_alloc, record_ptid_release): New function.
>>        (record_entry_release): Add record_ptid.
>>        (record_entry_remove_from_list,
>>        record_arch_list_add_ptid): New function.
>>        (record_message_args): Remove regcache.
>>        (record_message): Add code for multithread.
>>        (do_record_message): Remove regcache.
>>        (record_exec_insn): Remove regcache, gdbarch.  Add
>>        regcache_p, gdbarch_p, aspace_p.
>>        Add suport for record_ptid.
>>        (record_thread_number): New variable.
>>        (record_thread_number_count,
>>        record_thread_number_reset,
>>        record_new_thread_handler,
>>        record_thread_exit_handler): New function.
>>        (record_open_1): Add record_new_thread_observer
>>        and record_thread_exit_observer.
>>        (record_open): Add record_prev_ptid
>>        and record_thread_number_reset.
>>        (record_close): Add record_new_thread_observer
>>        and record_thread_exit_observer.
>>        (record_resume_ptid, record_resume_signal): New variable.
>>        (record_resume): Add code for multithread.
>>        (record_thread_wait_args): New struct.
>>        (record_thread_reset_callback,
>>        record_thread_stop_callback,
>>        record_thread_wait,
>>        record_thread_wait_callback,
>>        record_wait_signal_cleanups) New function.
>>        (record_wait_cleanups): Change to record_wait_replay_cleanups.
>>        (record_wait_mthread_cleanups): New function.
>>        (record_wait): Add code for multithread.
>>        (record_registers_change): Ditto.
>>        (record_xfer_partial): Ditto.
>>        (record_restore): Add code restore record_ptid entry.
>>        (cmd_record_save): Add code save record_ptid entry.
>>        (record_goto_insn): Update argument to call record_exec_insn.
>>        record.h (record_step): New extern.
>>
>> ---
>>  record.c |  734
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
>>  record.h |    1
>>  2 files changed, 631 insertions(+), 104 deletions(-)
>>
>> --- 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  (current_target.beneath->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,
>> @@ -288,6 +303,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 *
>> @@ -324,6 +360,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;
>> @@ -331,6 +370,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
>> @@ -512,6 +561,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
>> @@ -573,7 +640,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;
>>  };
>>
>> @@ -582,15 +648,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.
>>
>> @@ -613,20 +685,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)
>> @@ -649,12 +746,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)
>> @@ -688,9 +783,14 @@ static int record_hw_watchpoint = 0;
>>    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 address_space **aspace_p,
>>                  struct record_entry *entry)
>>  {
>> +  struct regcache *regcache = *regcache_p;
>> +  struct gdbarch *gdbarch = *gdbarch_p;
>> +
>>   switch (entry->type)
>>     {
>>     case record_reg: /* reg */
>> @@ -769,6 +869,110 @@ 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);
>> +            *aspace_p = get_regcache_aspace (*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 (record_prev_ptid, tp->ptid))
>> +            {
>> +              record_prev_ptid = cur_ptid;
>> +              record_entry_remove_from_list (tmp);
>> +              record_entry_release (tmp);
>> +            }
>> +          else if (ptid_equal (cur_ptid, tp->ptid))
>> +            {
>> +              record_entry_remove_from_list (tmp);
>> +              record_entry_release (tmp);
>> +            }
>> +          break;
>> +      }
>>     }
>>  }
>>
>> @@ -865,6 +1069,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.  */
>> @@ -934,6 +1141,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;
>> @@ -953,6 +1161,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.  */
>> @@ -965,6 +1175,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.  */
>> @@ -987,6 +1208,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.  */
>>
>> @@ -998,10 +1221,201 @@ 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;
>> +      struct address_space *aspace;
>> +      CORE_ADDR decr_pc_after_break;
>> +
>> +      /* Yes -- this is likely our single-step finishing,
>> +         but check if there's any reason the core would be
>> +         interested in the event.  */
>> +
>> +      registers_changed ();
>> +      regcache = get_current_regcache ();
>> +      gdbarch = get_regcache_arch (regcache);
>> +      tmp_pc = regcache_read_pc (regcache);
>> +      aspace = get_regcache_aspace (regcache);
>> +      decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
>> +
>> +      /* To check if this is breakpoint need the right pc, if this is not
>> +         a single step.  So we need adjust tmp_pc to make it right.  */
>> +      if (!record_step)
>> +        tmp_pc -= decr_pc_after_break;
>> +
>> +      if (target_stopped_by_watchpoint ())
>> +        {
>> +          /* Always interested in watchpoints.  */
>> +          return 1;
>> +        }
>> +      else if (breakpoint_inserted_here_p (aspace, tmp_pc))
>> +        {
>> +          /* There is a breakpoint here.  Let the core handle it.  */
>> +          if (record_step
>> +              && software_breakpoint_inserted_here_p (aspace, tmp_pc))
>> +            {
>> +              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;
>> @@ -1023,7 +1437,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)
>>     {
>> @@ -1034,6 +1454,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
>> @@ -1052,7 +1483,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,
>> @@ -1060,93 +1494,112 @@ 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;
>> -                 struct address_space *aspace;
>> +          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 -- this is likely our single-step finishing,
>> -                    but check if there's any reason the core would be
>> -                    interested in the event.  */
>> +          do_cleanups (mthread_cleanups);
>>
>> -                 registers_changed ();
>> -                 regcache = get_current_regcache ();
>> -                 tmp_pc = regcache_read_pc (regcache);
>> -                 aspace = get_regcache_aspace (regcache);
>> +          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;
>>
>> -                 if (target_stopped_by_watchpoint ())
>> -                   {
>> -                     /* Always interested in watchpoints.  */
>> -                   }
>> -                 else if (breakpoint_inserted_here_p (aspace, tmp_pc))
>> -                   {
>> -                     /* There is a breakpoint here.  Let the core
>> -                        handle it.  */
>> -                     if (software_breakpoint_inserted_here_p (aspace,
>> tmp_pc))
>> -                       {
>> -                         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
>> -                   {
>> -                     /* This must be a single-step trap.  Record the
>> -                        insn and issue another step.  */
>> -                     if (!do_record_message (regcache, TARGET_SIGNAL_0,
>> 1))
>> -                       {
>> -                           status->kind = TARGET_WAITKIND_STOPPED;
>> -                           status->value.sig = TARGET_SIGNAL_0;
>> -                           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;
>>
>> -                     record_beneath_to_resume
>> (record_beneath_to_resume_ops,
>> -                                               ptid, 1,
>> -                                               TARGET_SIGNAL_0);
>> -                     continue;
>> -                   }
>> -               }
>> +                  if (record_resume_step)
>> +                    break;
>>
>> -             /* The inferior is broken by a breakpoint or a signal.  */
>> -             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;
>> +                    }
>>
>> -         return ret;
>> -       }
>> +                 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);
>> -      struct address_space *aspace = get_regcache_aspace (regcache);
>> +      /* Replay mode.  */
>> +      struct regcache *regcache;
>> +      struct gdbarch *gdbarch;
>> +      struct address_space *aspace;
>>       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);
>> +      aspace = get_regcache_aspace (regcache);
>>
>>       record_hw_watchpoint = 0;
>>       status->kind = TARGET_WAITKIND_STOPPED;
>> @@ -1173,8 +1626,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.
>> @@ -1205,7 +1656,7 @@ record_wait (struct target_ops *ops,
>>              break;
>>            }
>>
>> -          record_exec_insn (regcache, gdbarch, record_list);
>> +          record_exec_insn (&regcache, &gdbarch, &aspace, record_list);
>>
>>          if (record_list->type == record_end)
>>            {
>> @@ -1228,19 +1679,20 @@ 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 */
>>                  tmp_pc = regcache_read_pc (regcache);
>>                  if (breakpoint_inserted_here_p (aspace, tmp_pc))
>>                    {
>> -                     int decr_pc_after_break
>> +                     CORE_ADDR decr_pc_after_break
>>                        = gdbarch_decr_pc_after_break (gdbarch);
>>
>>                      if (record_debug)
>> @@ -1288,22 +1740,23 @@ Process record: hit hw watchpoint.\n");
>>        }
>>       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;
>>  }
>>
>>  static int
>> @@ -1384,6 +1837,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;
>> @@ -1506,6 +1965,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);
>> @@ -2061,6 +2525,17 @@ info_record_command (char *args, int fro
>>        4 bytes: memory length (network byte order).
>>        8 bytes: memory address (network byte order).
>>        n bytes: memory value (n == memory length).
>> +     record_mem:
>> +       1 byte:  record type (record_mem, see enum record_type).
>> +       4 bytes: memory length (network byte order).
>> +       8 bytes: memory address (network byte order).
>> +       n bytes: memory value (n == memory length).
>> +     record_ptid:
>> +       1 byte:  record type (record_ptid, see enum record_type).
>> +       8 bytes: process id (network byte order).
>> +                It must same with CORELOW_PID (1).
>> +       8 bytes: lightweight process id (network byte order).
>> +       8 bytes: thread id (network byte order).
>>
>>  */
>>
>> @@ -2222,6 +2697,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 ++;
>> @@ -2327,6 +2828,7 @@ cmd_record_save (char *args, int from_tt
>>   uint32_t magic;
>>   struct regcache *regcache;
>>   struct gdbarch *gdbarch;
>> +  struct address_space *aspace;
>>   struct cleanup *old_cleanups;
>>   struct cleanup *set_cleanups;
>>   bfd *obfd;
>> @@ -2363,6 +2865,7 @@ cmd_record_save (char *args, int from_tt
>>   /* Get the values of regcache and gdbarch.  */
>>   regcache = get_current_regcache ();
>>   gdbarch = get_regcache_arch (regcache);
>> +  aspace = get_regcache_aspace (regcache);
>>
>>   /* Disable the GDB operation record.  */
>>   set_cleanups = record_gdb_operation_disable_set ();
>> @@ -2374,7 +2877,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, &aspace, record_list);
>>
>>       if (record_list->prev)
>>         record_list = record_list->prev;
>> @@ -2395,6 +2898,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.  */
>> @@ -2481,6 +2987,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) 1);
>> +             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, "\
>> @@ -2501,7 +3026,7 @@ cmd_record_save (char *args, int from_tt
>>         }
>>
>>       /* Execute entry.  */
>> -      record_exec_insn (regcache, gdbarch, record_list);
>> +      record_exec_insn (&regcache, &gdbarch, &aspace, record_list);
>>
>>       if (record_list->next)
>>         record_list = record_list->next;
>> @@ -2516,7 +3041,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, &aspace, record_list);
>>
>>       if (record_list->prev)
>>         record_list = record_list->prev;
>> @@ -2542,6 +3067,7 @@ record_goto_insn (struct record_entry *e
>>   struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
>>   struct regcache *regcache = get_current_regcache ();
>>   struct gdbarch *gdbarch = get_regcache_arch (regcache);
>> +  struct address_space *aspace = get_regcache_aspace (regcache);
>>
>>   /* Assume everything is valid: we will hit the entry,
>>      and we will not hit the end of the recording.  */
>> @@ -2551,7 +3077,7 @@ record_goto_insn (struct record_entry *e
>>
>>   do
>>     {
>> -      record_exec_insn (regcache, gdbarch, record_list);
>> +      record_exec_insn (&regcache, &gdbarch, &aspace, record_list);
>>       if (dir == EXEC_REVERSE)
>>        record_list = record_list->prev;
>>       else
>> --- 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);
>
>