gdb/
authorPedro Alves <palves@redhat.com>
Sun, 24 May 2009 18:00:08 +0000 (18:00 +0000)
committerPedro Alves <palves@redhat.com>
Sun, 24 May 2009 18:00:08 +0000 (18:00 +0000)
* gdbthread.h (struct thread_info): New `pending_follow' field.
* thread.c (new_thread): New function.
(add_thread_silent): Use it.
* breakpoint.c (internal_breakpoint_number): New global, moved
from inside...
(create_internal_breakpoint): ... this.
(clone_momentary_breakpoint): New.
* breakpoint.h (clone_momentary_breakpoint): Declare.
* infrun.c (nullify_last_target_wait_ptid): Move declaration
higher.
(pending_follow): Delete.
(follow_fork): Handle pending follow fork event here.  Moved the
preserving of thread stepping state here.
(resume): Don't handle pending follow fork events here.  Only
install the inferior's terminal modes if we're about to resume it.
(proceed): Handle possible pending follow fork events here.
(init_wait_for_inferior): No need to clear pending_follow anymore,
it's gone.
(handle_inferior_event): Adjust to per-thread `pending_follow'.
Call `follow_fork' to handle following the fork.  If the
follow-fork is cancelled, stop stepping.
* linux-nat.c (linux_child_follow_fork): Adjust to per-thread
`pending_follow' events.  Remove code that handled preserving the
thread stepping state.
* inf-ptrace.c (inf_ptrace_follow_fork): Ditto.
* inf-ttrace.c (inf_ttrace_follow_fork): Ditto.

gdb/testsuite/
* gdb.threads/fork-thread-pending.c: New.
* gdb.threads/fork-thread-pending.exp: New.

12 files changed:
gdb/ChangeLog
gdb/breakpoint.c
gdb/breakpoint.h
gdb/gdbthread.h
gdb/inf-ptrace.c
gdb/inf-ttrace.c
gdb/infrun.c
gdb/linux-nat.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.threads/fork-thread-pending.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/fork-thread-pending.exp [new file with mode: 0644]
gdb/thread.c

index 70b5602bad0958cc106ef381a59a040573ff164a..d392345df309c2dcfc4bb9abd430503905d7d03d 100644 (file)
@@ -1,3 +1,32 @@
+2009-05-24  Pedro Alves  <pedro@codesourcery.com>
+
+       * gdbthread.h (struct thread_info): New `pending_follow' field.
+       * thread.c (new_thread): New function.
+       (add_thread_silent): Use it.
+       * breakpoint.c (internal_breakpoint_number): New global, moved
+       from inside...
+       (create_internal_breakpoint): ... this.
+       (clone_momentary_breakpoint): New.
+       * breakpoint.h (clone_momentary_breakpoint): Declare.
+       * infrun.c (nullify_last_target_wait_ptid): Move declaration
+       higher.
+       (pending_follow): Delete.
+       (follow_fork): Handle pending follow fork event here.  Moved the
+       preserving of thread stepping state here.
+       (resume): Don't handle pending follow fork events here.  Only
+       install the inferior's terminal modes if we're about to resume it.
+       (proceed): Handle possible pending follow fork events here.
+       (init_wait_for_inferior): No need to clear pending_follow anymore,
+       it's gone.
+       (handle_inferior_event): Adjust to per-thread `pending_follow'.
+       Call `follow_fork' to handle following the fork.  If the
+       follow-fork is cancelled, stop stepping.
+       * linux-nat.c (linux_child_follow_fork): Adjust to per-thread
+       `pending_follow' events.  Remove code that handled preserving the
+       thread stepping state.
+       * inf-ptrace.c (inf_ptrace_follow_fork): Ditto.
+       * inf-ttrace.c (inf_ttrace_follow_fork): Ditto.
+
 2009-05-24  Pierre Muller  <muller@ics.u-strasbg.fr>
 
        * symfile.c (add_shared_symbol_files_command): Remove
index b5362e2c8ea861d0ada6929860ec0ea6ed7ccd47..1235946999bf0f7c5377995b46e2e2791d2ada76 100644 (file)
@@ -1456,10 +1456,11 @@ reattach_breakpoints (int pid)
   return 0;
 }
 
+static int internal_breakpoint_number = -1;
+
 static struct breakpoint *
 create_internal_breakpoint (CORE_ADDR address, enum bptype type)
 {
-  static int internal_breakpoint_number = -1;
   struct symtab_and_line sal;
   struct breakpoint *b;
 
@@ -5007,6 +5008,43 @@ set_momentary_breakpoint (struct symtab_and_line sal, struct frame_id frame_id,
   return b;
 }
 
+/* Make a deep copy of momentary breakpoint ORIG.  Returns NULL if
+   ORIG is NULL.  */
+
+struct breakpoint *
+clone_momentary_breakpoint (struct breakpoint *orig)
+{
+  struct breakpoint *copy;
+
+  /* If there's nothing to clone, then return nothing.  */
+  if (orig == NULL)
+    return NULL;
+
+  copy = set_raw_breakpoint_without_location (orig->type);
+  copy->loc = allocate_bp_location (copy);
+  set_breakpoint_location_function (copy->loc);
+
+  copy->loc->requested_address = orig->loc->requested_address;
+  copy->loc->address = orig->loc->address;
+  copy->loc->section = orig->loc->section;
+
+  if (orig->source_file == NULL)
+    copy->source_file = NULL;
+  else
+    copy->source_file = xstrdup (orig->source_file);
+
+  copy->line_number = orig->line_number;
+  copy->frame_id = orig->frame_id;
+  copy->thread = orig->thread;
+
+  copy->enable_state = bp_enabled;
+  copy->disposition = disp_donttouch;
+  copy->number = internal_breakpoint_number--;
+
+  update_global_location_list_nothrow (0);
+  return copy;
+}
+
 struct breakpoint *
 set_momentary_breakpoint_at_pc (CORE_ADDR pc, enum bptype type)
 {
index 43c2f3f3fd91dffb37a9c614c8c6aa7b99e2a3ed..4e672b5fd2c7350164d49934979c891fe16afb36 100644 (file)
@@ -696,6 +696,8 @@ extern struct breakpoint *set_momentary_breakpoint
 extern struct breakpoint *set_momentary_breakpoint_at_pc
   (CORE_ADDR pc, enum bptype type);
 
+extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);
+
 extern void set_ignore_count (int, int, int);
 
 extern void set_default_breakpoint (int, CORE_ADDR, struct symtab *, int);
index 1833a9e729df37c90adf2075a91b18204d06225a..ccdaeb0ed403d53c736e4427b4a2b484e1336f64 100644 (file)
@@ -165,6 +165,11 @@ struct thread_info
      next time inferior stops if it stops due to stepping.  */
   int step_multi;
 
+  /* This is used to remember when a fork or vfork event was caught by
+     a catchpoint, and thus the event is to be followed at the next
+     resume of the thread, and not immediately.  */
+  struct target_waitstatus pending_follow;
+
   /* Last signal that the inferior received (why it stopped).  */
   enum target_signal stop_signal;
 
index 7076da7073b2232002fd8888f66b1e980625b529..89a37a6db9bf8d5d4c77547452f994111fa56a5b 100644 (file)
@@ -46,20 +46,8 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
 {
   pid_t pid, fpid;
   ptrace_state_t pe;
-  struct thread_info *last_tp = NULL;
 
-  /* FIXME: kettenis/20050720: This stuff should really be passed as
-     an argument by our caller.  */
-  {
-    ptid_t ptid;
-    struct target_waitstatus status;
-
-    get_last_target_status (&ptid, &status);
-    gdb_assert (status.kind == TARGET_WAITKIND_FORKED);
-
-    pid = ptid_get_pid (ptid);
-    last_tp = find_thread_pid (ptid);
-  }
+  pid = ptid_get_pid (inferior_ptid);
 
   if (ptrace (PT_GET_PROCESS_STATE, pid,
               (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
@@ -70,18 +58,9 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
 
   if (follow_child)
     {
-      /* Copy user stepping state to the new inferior thread.  */
-      struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
-      CORE_ADDR step_range_start = last_tp->step_range_start;
-      CORE_ADDR step_range_end = last_tp->step_range_end;
-      struct frame_id step_frame_id = last_tp->step_frame_id;
       struct inferior *parent_inf, *child_inf;
       struct thread_info *tp;
 
-      /* Otherwise, deleting the parent would get rid of this
-        breakpoint.  */
-      last_tp->step_resume_breakpoint = NULL;
-
       parent_inf = find_inferior_pid (pid);
 
       /* Add the child.  */
@@ -102,26 +81,15 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
       /* Delete the parent.  */
       detach_inferior (pid);
 
-      tp = add_thread_silent (inferior_ptid);
-
-      tp->step_resume_breakpoint = step_resume_breakpoint;
-      tp->step_range_start = step_range_start;
-      tp->step_range_end = step_range_end;
-      tp->step_frame_id = step_frame_id;
-
-      /* Reset breakpoints in the child as appropriate.  */
-      follow_inferior_reset_breakpoints ();
+      add_thread_silent (inferior_ptid);
     }
   else
     {
-      inferior_ptid = pid_to_ptid (pid);
-
       /* Breakpoints have already been detached from the child by
         infrun.c.  */
 
       if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
        perror_with_name (("ptrace"));
-      detach_inferior (pid);
     }
 
   return 0;
index a4c4b99aa6527342630caef19f944f1b7c9135f9..ffec586ed923215baf7953a6eb79acca017fa22d 100644 (file)
@@ -412,25 +412,13 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
   pid_t pid, fpid;
   lwpid_t lwpid, flwpid;
   ttstate_t tts;
-  struct thread_info *last_tp = NULL;
-  struct breakpoint *step_resume_breakpoint = NULL;
-  CORE_ADDR step_range_start = 0, step_range_end = 0;
-  struct frame_id step_frame_id = null_frame_id;
-
-  /* FIXME: kettenis/20050720: This stuff should really be passed as
-     an argument by our caller.  */
-  {
-    ptid_t ptid;
-    struct target_waitstatus status;
-
-    get_last_target_status (&ptid, &status);
-    gdb_assert (status.kind == TARGET_WAITKIND_FORKED
-               || status.kind == TARGET_WAITKIND_VFORKED);
-
-    pid = ptid_get_pid (ptid);
-    lwpid = ptid_get_lwp (ptid);
-    last_tp = find_thread_pid (ptid);
-  }
+  struct thread_info *tp = inferior_thread ();
+
+  gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
+             || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED);
+
+  pid = ptid_get_pid (inferior_ptid);
+  lwpid = ptid_get_lwp (inferior_ptid);
 
   /* Get all important details that core GDB doesn't (and shouldn't)
      know about.  */
@@ -462,16 +450,6 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
 
       parent_inf = find_inferior_pid (pid);
 
-      /* Copy user stepping state to the new inferior thread.  */
-      step_resume_breakpoint = last_tp->step_resume_breakpoint;
-      step_range_start = last_tp->step_range_start;
-      step_range_end = last_tp->step_range_end;
-      step_frame_id = last_tp->step_frame_id;
-
-      /* Otherwise, deleting the parent would get rid of this
-        breakpoint.  */
-      last_tp->step_resume_breakpoint = NULL;
-
       inferior_ptid = ptid_build (fpid, flwpid, 0);
       inf = add_inferior (fpid);
       inf->attach_flag = parent_inf->attach_flag;
@@ -553,14 +531,6 @@ Detaching after fork from child process %ld.\n"), (long)fpid);
        xmalloc (sizeof (struct inf_ttrace_private_thread_info));
       memset (ti->private, 0,
              sizeof (struct inf_ttrace_private_thread_info));
-
-      ti->step_resume_breakpoint = step_resume_breakpoint;
-      ti->step_range_start = step_range_start;
-      ti->step_range_end = step_range_end;
-      ti->step_frame_id = step_frame_id;
-
-      /* Reset breakpoints in the child as appropriate.  */
-      follow_inferior_reset_breakpoints ();
     }
 
   return 0;
index 489f9d160e8b062cb564d836a16c351312483a7a..f9b34ddf906a733b6a829f1163866db9687a64d5 100644 (file)
@@ -83,6 +83,8 @@ static int prepare_to_proceed (int);
 
 void _initialize_infrun (void);
 
+void nullify_last_target_wait_ptid (void);
+
 /* When set, stop the 'step' command if we enter a function which has
    no line number information.  The normal behavior is that we step
    over such function.  */
@@ -255,21 +257,6 @@ void init_thread_stepping_state (struct thread_info *tss);
 
 void init_infwait_state (void);
 
-/* This is used to remember when a fork or vfork event was caught by a
-   catchpoint, and thus the event is to be followed at the next resume
-   of the inferior, and not immediately.  */
-static struct
-{
-  enum target_waitkind kind;
-  struct
-  {
-    ptid_t parent_pid;
-    ptid_t child_pid;
-  }
-  fork_event;
-}
-pending_follow;
-
 static const char follow_fork_mode_child[] = "child";
 static const char follow_fork_mode_parent[] = "parent";
 
@@ -290,12 +277,157 @@ Debugger response to a program call of fork or vfork is \"%s\".\n"),
 }
 \f
 
+/* Tell the target to follow the fork we're stopped at.  Returns true
+   if the inferior should be resumed; false, if the target for some
+   reason decided it's best not to resume.  */
+
 static int
 follow_fork (void)
 {
   int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+  int should_resume = 1;
+  struct thread_info *tp;
+
+  /* Copy user stepping state to the new inferior thread.  FIXME: the
+     followed fork child thread should have a copy of most of the
+     parent thread structure's run control related fields, not just
+     these.  */
+  struct breakpoint *step_resume_breakpoint;
+  CORE_ADDR step_range_start;
+  CORE_ADDR step_range_end;
+  struct frame_id step_frame_id;
+
+  if (!non_stop)
+    {
+      ptid_t wait_ptid;
+      struct target_waitstatus wait_status;
+
+      /* Get the last target status returned by target_wait().  */
+      get_last_target_status (&wait_ptid, &wait_status);
+
+      /* If not stopped at a fork event, then there's nothing else to
+        do.  */
+      if (wait_status.kind != TARGET_WAITKIND_FORKED
+         && wait_status.kind != TARGET_WAITKIND_VFORKED)
+       return 1;
+
+      /* Check if we switched over from WAIT_PTID, since the event was
+        reported.  */
+      if (!ptid_equal (wait_ptid, minus_one_ptid)
+         && !ptid_equal (inferior_ptid, wait_ptid))
+       {
+         /* We did.  Switch back to WAIT_PTID thread, to tell the
+            target to follow it (in either direction).  We'll
+            afterwards refuse to resume, and inform the user what
+            happened.  */
+         switch_to_thread (wait_ptid);
+         should_resume = 0;
+       }
+    }
+
+  tp = inferior_thread ();
+
+  /* If there were any forks/vforks that were caught and are now to be
+     followed, then do so now.  */
+  switch (tp->pending_follow.kind)
+    {
+    case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
+      {
+       ptid_t parent, child;
+
+       /* If the user did a next/step, etc, over a fork call,
+          preserve the stepping state in the fork child.  */
+       if (follow_child && should_resume)
+         {
+           step_resume_breakpoint
+             = clone_momentary_breakpoint (tp->step_resume_breakpoint);
+           step_range_start = tp->step_range_start;
+           step_range_end = tp->step_range_end;
+           step_frame_id = tp->step_frame_id;
+
+           /* For now, delete the parent's sr breakpoint, otherwise,
+              parent/child sr breakpoints are considered duplicates,
+              and the child version will not be installed.  Remove
+              this when the breakpoints module becomes aware of
+              inferiors and address spaces.  */
+           delete_step_resume_breakpoint (tp);
+           tp->step_range_start = 0;
+           tp->step_range_end = 0;
+           tp->step_frame_id = null_frame_id;
+         }
+
+       parent = inferior_ptid;
+       child = tp->pending_follow.value.related_pid;
+
+       /* Tell the target to do whatever is necessary to follow
+          either parent or child.  */
+       if (target_follow_fork (follow_child))
+         {
+           /* Target refused to follow, or there's some other reason
+              we shouldn't resume.  */
+           should_resume = 0;
+         }
+       else
+         {
+           /* This pending follow fork event is now handled, one way
+              or another.  The previous selected thread may be gone
+              from the lists by now, but if it is still around, need
+              to clear the pending follow request.  */
+           tp = find_thread_pid (parent);
+           if (tp)
+             tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+
+           /* This makes sure we don't try to apply the "Switched
+              over from WAIT_PID" logic above.  */
+           nullify_last_target_wait_ptid ();
+
+           /* If we followed the child, switch to it... */
+           if (follow_child)
+             {
+               switch_to_thread (child);
+
+               /* ... and preserve the stepping state, in case the
+                  user was stepping over the fork call.  */
+               if (should_resume)
+                 {
+                   tp = inferior_thread ();
+                   tp->step_resume_breakpoint = step_resume_breakpoint;
+                   tp->step_range_start = step_range_start;
+                   tp->step_range_end = step_range_end;
+                   tp->step_frame_id = step_frame_id;
+                 }
+               else
+                 {
+                   /* If we get here, it was because we're trying to
+                      resume from a fork catchpoint, but, the user
+                      has switched threads away from the thread that
+                      forked.  In that case, the resume command
+                      issued is most likely not applicable to the
+                      child, so just warn, and refuse to resume.  */
+                   warning (_("\
+Not resuming: switched threads before following fork child.\n"));
+                 }
+
+               /* Reset breakpoints in the child as appropriate.  */
+               follow_inferior_reset_breakpoints ();
+             }
+           else
+             switch_to_thread (parent);
+         }
+      }
+      break;
+    case TARGET_WAITKIND_SPURIOUS:
+      /* Nothing to follow.  */
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+                     "Unexpected pending_follow.kind %d\n",
+                     tp->pending_follow.kind);
+      break;
+    }
 
-  return target_follow_fork (follow_child);
+  return should_resume;
 }
 
 void
@@ -987,8 +1119,6 @@ resume (int step, enum target_signal sig)
 {
   int should_resume = 1;
   struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
-
-  /* Note that these must be reset if we follow a fork below.  */
   struct regcache *regcache = get_current_regcache ();
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   struct thread_info *tp = inferior_thread ();
@@ -1058,31 +1188,6 @@ a command like `return' or `jump' to continue execution."));
   if (step)
     step = maybe_software_singlestep (gdbarch, pc);
 
-  /* If there were any forks/vforks/execs that were caught and are
-     now to be followed, then do so.  */
-  switch (pending_follow.kind)
-    {
-    case TARGET_WAITKIND_FORKED:
-    case TARGET_WAITKIND_VFORKED:
-      pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
-      if (follow_fork ())
-       should_resume = 0;
-
-      /* Following a child fork will change our notion of current
-        thread.  */
-      tp = inferior_thread ();
-      regcache = get_current_regcache ();
-      gdbarch = get_regcache_arch (regcache);
-      pc = regcache_read_pc (regcache);
-      break;
-
-    default:
-      break;
-    }
-
-  /* Install inferior's terminal modes.  */
-  target_terminal_inferior ();
-
   if (should_resume)
     {
       ptid_t resume_ptid;
@@ -1164,6 +1269,9 @@ a command like `return' or `jump' to continue execution."));
           displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
         }
 
+      /* Install inferior's terminal modes.  */
+      target_terminal_inferior ();
+
       /* Avoid confusing the next resume, if the next stop/resume
         happens to apply to another thread.  */
       tp->stop_signal = TARGET_SIGNAL_0;
@@ -1305,12 +1413,26 @@ prepare_to_proceed (int step)
 void
 proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
 {
-  struct regcache *regcache = get_current_regcache ();
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct regcache *regcache;
+  struct gdbarch *gdbarch;
   struct thread_info *tp;
-  CORE_ADDR pc = regcache_read_pc (regcache);
+  CORE_ADDR pc;
   int oneproc = 0;
 
+  /* If we're stopped at a fork/vfork, follow the branch set by the
+     "set follow-fork-mode" command; otherwise, we'll just proceed
+     resuming the current thread.  */
+  if (!follow_fork ())
+    {
+      /* The target for some reason decided not to resume.  */
+      normal_stop ();
+      return;
+    }
+
+  regcache = get_current_regcache ();
+  gdbarch = get_regcache_arch (regcache);
+  pc = regcache_read_pc (regcache);
+
   if (step > 0)
     step_start_function = find_pc_function (pc);
   if (step < 0)
@@ -1517,9 +1639,6 @@ init_wait_for_inferior (void)
 
   breakpoint_init_inferior (inf_starting);
 
-  /* The first resume is not following a fork/vfork/exec. */
-  pending_follow.kind = TARGET_WAITKIND_SPURIOUS;      /* I.e., none. */
-
   clear_proceed_status ();
 
   stepping_past_singlestep_breakpoint = 0;
@@ -1698,8 +1817,6 @@ infrun_thread_stop_requested (ptid_t ptid)
   iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
 }
 
-void nullify_last_target_wait_ptid (void);
-
 static void
 infrun_thread_thread_exit (struct thread_info *tp, int silent)
 {
@@ -2407,10 +2524,6 @@ handle_inferior_event (struct execution_control_state *ecs)
     case TARGET_WAITKIND_VFORKED:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n");
-      pending_follow.kind = ecs->ws.kind;
-
-      pending_follow.fork_event.parent_pid = ecs->ptid;
-      pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
 
       if (!ptid_equal (ecs->ptid, inferior_ptid))
        {
@@ -2439,6 +2552,11 @@ handle_inferior_event (struct execution_control_state *ecs)
          detach_breakpoints (child_pid);
        }
 
+      /* In case the event is caught by a catchpoint, remember that
+        the event is to be followed at the next resume of the thread,
+        and not immediately.  */
+      ecs->event_thread->pending_follow = ecs->ws;
+
       stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
 
       ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
@@ -2448,8 +2566,19 @@ handle_inferior_event (struct execution_control_state *ecs)
       /* If no catchpoint triggered for this, then keep going.  */
       if (ecs->random_signal)
        {
+         int should_resume;
+
          ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
-         keep_going (ecs);
+
+         should_resume = follow_fork ();
+
+         ecs->event_thread = inferior_thread ();
+         ecs->ptid = inferior_ptid;
+
+         if (should_resume)
+           keep_going (ecs);
+         else
+           stop_stepping (ecs);
          return;
        }
       ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
index 90574424a32c42c686ccb38201563cd329946ac6..f93ea9fe25aec508cf823b7a5d8ccd29fd4a1eee 100644 (file)
@@ -575,19 +575,17 @@ static int
 linux_child_follow_fork (struct target_ops *ops, int follow_child)
 {
   sigset_t prev_mask;
-  ptid_t last_ptid;
-  struct target_waitstatus last_status;
   int has_vforked;
   int parent_pid, child_pid;
 
   block_child_signals (&prev_mask);
 
-  get_last_target_status (&last_ptid, &last_status);
-  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
-  parent_pid = ptid_get_lwp (last_ptid);
+  has_vforked = (inferior_thread ()->pending_follow.kind
+                == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (inferior_ptid);
   if (parent_pid == 0)
-    parent_pid = ptid_get_pid (last_ptid);
-  child_pid = PIDGET (last_status.value.related_pid);
+    parent_pid = ptid_get_pid (inferior_ptid);
+  child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
 
   if (! follow_child)
     {
@@ -625,7 +623,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
          /* Add process to GDB's tables.  */
          child_inf = add_inferior (child_pid);
 
-         parent_inf = find_inferior_pid (GET_PID (last_ptid));
+         parent_inf = current_inferior ();
          child_inf->attach_flag = parent_inf->attach_flag;
          copy_terminal_info (child_inf, parent_inf);
 
@@ -692,21 +690,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
     }
   else
     {
-      struct thread_info *last_tp = find_thread_pid (last_ptid);
       struct thread_info *tp;
-      char child_pid_spelling[40];
       struct inferior *parent_inf, *child_inf;
 
-      /* Copy user stepping state to the new inferior thread.  */
-      struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
-      CORE_ADDR step_range_start = last_tp->step_range_start;
-      CORE_ADDR step_range_end = last_tp->step_range_end;
-      struct frame_id step_frame_id = last_tp->step_frame_id;
-
-      /* Otherwise, deleting the parent would get rid of this
-        breakpoint.  */
-      last_tp->step_resume_breakpoint = NULL;
-
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
 
@@ -723,7 +709,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 
       child_inf = add_inferior (child_pid);
 
-      parent_inf = find_inferior_pid (GET_PID (last_ptid));
+      parent_inf = current_inferior ();
       child_inf->attach_flag = parent_inf->attach_flag;
       copy_terminal_info (child_inf, parent_inf);
 
@@ -772,15 +758,6 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 
       linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
-
-      tp = inferior_thread ();
-      tp->step_resume_breakpoint = step_resume_breakpoint;
-      tp->step_range_start = step_range_start;
-      tp->step_range_end = step_range_end;
-      tp->step_frame_id = step_frame_id;
-
-      /* Reset breakpoints in the child as appropriate.  */
-      follow_inferior_reset_breakpoints ();
     }
 
   restore_child_signals_mask (&prev_mask);
index c65edbe318d25d7b626a8d5848c8f056f09bbd1c..5fabccc5648ce99a9ead24532f75287c9010e8e6 100644 (file)
@@ -1,3 +1,8 @@
+2009-05-24  Pedro Alves  <pedro@codesourcery.com>
+
+       * gdb.threads/fork-thread-pending.c: New.
+       * gdb.threads/fork-thread-pending.exp: New.
+
 2009-05-21  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.dwarf2/dw2-strp.exp (p a_string2, ptype a_string2): New.
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.c b/gdb/testsuite/gdb.threads/fork-thread-pending.c
new file mode 100644 (file)
index 0000000..34ea52d
--- /dev/null
@@ -0,0 +1,109 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008, 2009 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define NUMTHREADS 10
+
+volatile int done = 0;
+
+static void *
+start (void *arg)
+{
+  while (!done)
+    usleep (100);
+  assert (0);
+  return arg;
+}
+
+void *
+thread_function (void *arg)
+{
+  int x = * (int *) arg;
+
+  printf ("Thread <%d> executing\n", x);
+
+  while (!done)
+    usleep (100);
+
+  return NULL;
+}
+
+void *
+thread_forker (void *arg)
+{
+  int x = * (int *) arg;
+  pid_t pid;
+  int rv;
+  int i;
+  pthread_t thread;
+
+  printf ("Thread forker <%d> executing\n", x);
+
+  switch ((pid = fork ()))
+    {
+    case -1:
+      assert (0);
+    default:
+      wait (&rv);
+      done = 1;
+      break;
+    case 0:
+      i = pthread_create (&thread, NULL, start, NULL);
+      assert (i == 0);
+      i = pthread_join (thread, NULL);
+      assert (i == 0);
+
+      assert (0);
+    }
+
+  return NULL;
+}
+
+int
+main (void)
+{
+  pthread_t threads[NUMTHREADS];
+  int args[NUMTHREADS];
+  int i, j;
+
+  /* Create a few threads that do mostly nothing, and then one that
+     forks.  */
+  for (j = 0; j < NUMTHREADS - 1; ++j)
+    {
+      args[j] = j;
+      pthread_create (&threads[j], NULL, thread_function, &args[j]);
+    }
+
+  args[j] = j;
+  pthread_create (&threads[j], NULL, thread_forker, &args[j]);
+
+  for (j = 0; j < NUMTHREADS; ++j)
+    {
+      pthread_join (threads[j], NULL);
+    }
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
new file mode 100644 (file)
index 0000000..6bc866b
--- /dev/null
@@ -0,0 +1,128 @@
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# There's no support for `set follow-fork-mode' in the remote
+# protocol.
+if { [is_remote target] } {
+    return 0
+}
+
+# Only GNU/Linux is known to support `set follow-fork-mode child'.
+#
+if { ! [istarget "*-*-linux*"] } {
+    return 0
+}
+
+set testfile fork-thread-pending
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_load ${binfile}
+if ![runto_main] then {
+   fail "Can't run to main"
+   return 0
+}
+
+gdb_test "set follow-fork-mode child" "" "1, set follow-fork-mode child"
+gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "1, insert fork catchpoint"
+gdb_breakpoint "start" "" "1, set breakpoint at start"
+
+gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
+
+gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
+
+gdb_test "thread 2" "" "1, switched away from event thread"
+
+gdb_test "continue" "Not resuming.*" "1, refused to resume"
+
+set test "1, followed to the child, found one thread"
+gdb_test_multiple "info threads" "metest" {
+    -re " Thread .* Thread .*$gdb_prompt $" {
+       fail "$test"
+    }
+    -re " Thread .*$gdb_prompt $" {
+       pass "$test"
+    }
+    -re "$gdb_prompt $" {
+       fail "$test (unknown output)"
+    }
+    timeout {
+       fail "$test (timeout)"
+    }
+}
+
+gdb_test "continue" "Breakpoint 3, start.*" "1, get to the spawned thread in fork child"
+
+set test "1, followed to the child, found two threads"
+gdb_test_multiple "info threads" "$test" {
+    -re " Thread .* Thread .* Thread .*$gdb_prompt $" {
+       fail "$test"
+    }
+    -re " Thread .* Thread .*$gdb_prompt $" {
+       pass "$test"
+    }
+    -re "$gdb_prompt $" {
+       fail "$test (unknown output)"
+    }
+    timeout {
+       fail "$test (timeout)"
+    }
+}
+
+# Start over, but this time, don't switch away from the fork event thread.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_load ${binfile}
+if ![runto_main] then {
+   fail "Can't run to main"
+   return 0
+}
+
+gdb_test "set follow-fork-mode child" "" "2, set follow-fork-mode child"
+gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "2, insert fork catchpoint"
+gdb_breakpoint "start"
+
+gdb_test "continue" "Catchpoint.*" "2, get to the fork event"
+
+gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "2, multiple threads found"
+
+gdb_test "continue" "Breakpoint 3, start.*" "2, get to the spawned thread in fork child"
+
+set test "2, followed to the child, found two threads"
+gdb_test_multiple "info threads" "$test" {
+    -re " Thread .* Thread .* Thread .*$gdb_prompt $" {
+       fail "$test"
+    }
+    -re " Thread .* Thread .*$gdb_prompt $" {
+       pass "$test"
+    }
+    -re "$gdb_prompt $" {
+       fail "$test (unknown output)"
+    }
+    timeout {
+       fail "$test (timeout)"
+    }
+}
index 95f265a7fcbb9799e41f6ad7db7e1fa267768365..7b2c6bb0764e9347cbbb9dbf8c769c33cd0f68e1 100644 (file)
@@ -141,6 +141,28 @@ init_thread_list (void)
   thread_list = NULL;
 }
 
+/* Allocate a new thread with target id PTID and add it to the thread
+   list.  */
+
+static struct thread_info *
+new_thread (ptid_t ptid)
+{
+  struct thread_info *tp;
+
+  tp = xcalloc (1, sizeof (*tp));
+
+  tp->ptid = ptid;
+  tp->num = ++highest_thread_num;
+  tp->next = thread_list;
+  thread_list = tp;
+
+  /* Nothing to follow yet.  */
+  tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+  tp->state_ = THREAD_STOPPED;
+
+  return tp;
+}
+
 struct thread_info *
 add_thread_silent (ptid_t ptid)
 {
@@ -162,12 +184,7 @@ add_thread_silent (ptid_t ptid)
 
       if (ptid_equal (inferior_ptid, ptid))
        {
-         tp = xmalloc (sizeof (*tp));
-         memset (tp, 0, sizeof (*tp));
-         tp->ptid = minus_one_ptid;
-         tp->num = ++highest_thread_num;
-         tp->next = thread_list;
-         thread_list = tp;
+         tp = new_thread (ptid);
 
          /* Make switch_to_thread not read from the thread.  */
          tp->state_ = THREAD_EXITED;
@@ -191,13 +208,7 @@ add_thread_silent (ptid_t ptid)
        delete_thread (ptid);
     }
 
-  tp = (struct thread_info *) xmalloc (sizeof (*tp));
-  memset (tp, 0, sizeof (*tp));
-  tp->ptid = ptid;
-  tp->num = ++highest_thread_num;
-  tp->next = thread_list;
-  thread_list = tp;
-
+  tp = new_thread (ptid);
   observer_notify_new_thread (tp);
 
   return tp;