gdb/
authorPedro Alves <palves@redhat.com>
Thu, 28 Jun 2012 16:34:04 +0000 (16:34 +0000)
committerPedro Alves <palves@redhat.com>
Thu, 28 Jun 2012 16:34:04 +0000 (16:34 +0000)
2012-06-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
    Pedro Alves  <palves@redhat.com>

* gdbthread.h (ALL_THREADS): New macro.
(thread_list): Declare.
* infrun.c (handle_inferior_event) <spurious signal>: Don't keep
going, but instead fall through to the stepping handling.
* linux-nat.c (resume_lwp): New parameter 'signo'.  Resume with
the passed in signal.  Adjust debug output.
(resume_callback): Rename to ...
(linux_nat_resume_callback): ... this.  Pass the thread's last
stop signal, if in "pass" state.
(linux_nat_resume): Adjust to rename.
(stop_wait_callback): New assertion.  Don't respawn signals;
instead let the LWP remain with SIGNALLED set.
(linux_nat_wait_1): Remove flushing of pending SIGSTOPs.
* remote.c (append_pending_thread_resumptions): New.
(remote_vcont_resume): Call it.
* target.h (target_resume): Extend comment.

gdb/testsuite/
2012-06-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
    Pedro Alves  <palves@redhat.com>

* gdb.threads/siginfo-threads.exp: New file.
* gdb.threads/siginfo-threads.c: New file.
* gdb.threads/sigstep-threads.exp: New file.
* gdb.threads/sigstep-threads.c: New file.

12 files changed:
gdb/ChangeLog
gdb/gdbthread.h
gdb/infrun.c
gdb/linux-nat.c
gdb/remote.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.threads/siginfo-threads.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/siginfo-threads.exp [new file with mode: 0644]
gdb/testsuite/gdb.threads/sigstep-threads.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/sigstep-threads.exp [new file with mode: 0644]
gdb/thread.c

index d291ae6..1780862 100644 (file)
@@ -1,3 +1,23 @@
+2012-06-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdbthread.h (ALL_THREADS): New macro.
+       (thread_list): Declare.
+       * infrun.c (handle_inferior_event) <spurious signal>: Don't keep
+       going, but instead fall through to the stepping handling.
+       * linux-nat.c (resume_lwp): New parameter 'signo'.  Resume with
+       the passed in signal.  Adjust debug output.
+       (resume_callback): Rename to ...
+       (linux_nat_resume_callback): ... this.  Pass the thread's last
+       stop signal, if in "pass" state.
+       (linux_nat_resume): Adjust to rename.
+       (stop_wait_callback): New assertion.  Don't respawn signals;
+       instead let the LWP remain with SIGNALLED set.
+       (linux_nat_wait_1): Remove flushing of pending SIGSTOPs.
+       * remote.c (append_pending_thread_resumptions): New.
+       (remote_vcont_resume): Call it.
+       * target.h (target_resume): Extend comment.
+
 2012-06-28  Iain Sandoe  <iain@codesourcery.com>
 
        * auxv.c (fprint_target_auxv): Handle extended cache data tags.
index 7cd66b6..0250555 100644 (file)
@@ -301,6 +301,11 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 typedef int (*thread_callback_func) (struct thread_info *, void *);
 extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
 
+/* Traverse all threads.  */
+
+#define ALL_THREADS(T)                         \
+  for (T = thread_list; T; T = T->next)
+
 extern int thread_count (void);
 
 /* Switch from one thread to another.  */
@@ -391,4 +396,6 @@ extern struct thread_info* inferior_thread (void);
 
 extern void update_thread_list (void);
 
+extern struct thread_info *thread_list;
+
 #endif /* GDBTHREAD_H */
index 53db335..2f66ddc 100644 (file)
@@ -4363,227 +4363,227 @@ process_event_stop_test:
         (leaving the inferior at the step-resume-breakpoint without
         actually executing it).  Either way continue until the
         breakpoint is really hit.  */
-      keep_going (ecs);
-      return;
     }
+  else
+    {
+      /* Handle cases caused by hitting a breakpoint.  */
 
-  /* Handle cases caused by hitting a breakpoint.  */
-  {
-    CORE_ADDR jmp_buf_pc;
-    struct bpstat_what what;
-
-    what = bpstat_what (ecs->event_thread->control.stop_bpstat);
-
-    if (what.call_dummy)
-      {
-       stop_stack_dummy = what.call_dummy;
-      }
-
-    /* If we hit an internal event that triggers symbol changes, the
-       current frame will be invalidated within bpstat_what (e.g., if
-       we hit an internal solib event).  Re-fetch it.  */
-    frame = get_current_frame ();
-    gdbarch = get_frame_arch (frame);
-
-    switch (what.main_action)
-      {
-      case BPSTAT_WHAT_SET_LONGJMP_RESUME:
-       /* If we hit the breakpoint at longjmp while stepping, we
-          install a momentary breakpoint at the target of the
-          jmp_buf.  */
-
-       if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog,
-                             "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n");
+      CORE_ADDR jmp_buf_pc;
+      struct bpstat_what what;
 
-       ecs->event_thread->stepping_over_breakpoint = 1;
+      what = bpstat_what (ecs->event_thread->control.stop_bpstat);
 
-       if (what.is_longjmp)
-         {
-           struct value *arg_value;
-
-           /* If we set the longjmp breakpoint via a SystemTap probe,
-              then use it to extract the arguments.  The destination
-              PC is the third argument to the probe.  */
-           arg_value = probe_safe_evaluate_at_pc (frame, 2);
-           if (arg_value)
-             jmp_buf_pc = value_as_address (arg_value);
-           else if (!gdbarch_get_longjmp_target_p (gdbarch)
-                    || !gdbarch_get_longjmp_target (gdbarch,
-                                                    frame, &jmp_buf_pc))
-             {
-               if (debug_infrun)
-                 fprintf_unfiltered (gdb_stdlog,
-                                     "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME "
-                                     "(!gdbarch_get_longjmp_target)\n");
-               keep_going (ecs);
-               return;
-             }
-
-           /* Insert a breakpoint at resume address.  */
-           insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
-         }
-       else
-         check_exception_resume (ecs, frame);
-       keep_going (ecs);
-       return;
-
-      case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
+      if (what.call_dummy)
        {
-         struct frame_info *init_frame;
-
-         /* There are several cases to consider.
-
-            1. The initiating frame no longer exists.  In this case
-            we must stop, because the exception or longjmp has gone
-            too far.
-
-            2. The initiating frame exists, and is the same as the
-            current frame.  We stop, because the exception or longjmp
-            has been caught.
+         stop_stack_dummy = what.call_dummy;
+       }
 
-            3. The initiating frame exists and is different from the
-            current frame.  This means the exception or longjmp has
-            been caught beneath the initiating frame, so keep
-            going.
+      /* If we hit an internal event that triggers symbol changes, the
+        current frame will be invalidated within bpstat_what (e.g.,
+        if we hit an internal solib event).  Re-fetch it.  */
+      frame = get_current_frame ();
+      gdbarch = get_frame_arch (frame);
 
-            4. longjmp breakpoint has been placed just to protect
-            against stale dummy frames and user is not interested in
-            stopping around longjmps.  */
+      switch (what.main_action)
+       {
+       case BPSTAT_WHAT_SET_LONGJMP_RESUME:
+         /* If we hit the breakpoint at longjmp while stepping, we
+            install a momentary breakpoint at the target of the
+            jmp_buf.  */
 
          if (debug_infrun)
            fprintf_unfiltered (gdb_stdlog,
-                               "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
+                               "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n");
 
-         gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
-                     != NULL);
-         delete_exception_resume_breakpoint (ecs->event_thread);
+         ecs->event_thread->stepping_over_breakpoint = 1;
 
          if (what.is_longjmp)
            {
-             check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num);
-
-             if (!frame_id_p (ecs->event_thread->initiating_frame))
+             struct value *arg_value;
+
+             /* If we set the longjmp breakpoint via a SystemTap
+                probe, then use it to extract the arguments.  The
+                destination PC is the third argument to the
+                probe.  */
+             arg_value = probe_safe_evaluate_at_pc (frame, 2);
+             if (arg_value)
+               jmp_buf_pc = value_as_address (arg_value);
+             else if (!gdbarch_get_longjmp_target_p (gdbarch)
+                      || !gdbarch_get_longjmp_target (gdbarch,
+                                                      frame, &jmp_buf_pc))
                {
-                 /* Case 4.  */
+                 if (debug_infrun)
+                   fprintf_unfiltered (gdb_stdlog,
+                                       "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME "
+                                       "(!gdbarch_get_longjmp_target)\n");
                  keep_going (ecs);
                  return;
                }
+
+             /* Insert a breakpoint at resume address.  */
+             insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
            }
+         else
+           check_exception_resume (ecs, frame);
+         keep_going (ecs);
+         return;
 
-         init_frame = frame_find_by_id (ecs->event_thread->initiating_frame);
+       case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
+         {
+           struct frame_info *init_frame;
 
-         if (init_frame)
-           {
-             struct frame_id current_id
-               = get_frame_id (get_current_frame ());
-             if (frame_id_eq (current_id,
-                              ecs->event_thread->initiating_frame))
-               {
-                 /* Case 2.  Fall through.  */
-               }
-             else
-               {
-                 /* Case 3.  */
-                 keep_going (ecs);
-                 return;
-               }
-           }
+           /* There are several cases to consider.
 
-         /* For Cases 1 and 2, remove the step-resume breakpoint,
-            if it exists.  */
-         delete_step_resume_breakpoint (ecs->event_thread);
+              1. The initiating frame no longer exists.  In this case
+              we must stop, because the exception or longjmp has gone
+              too far.
 
-         ecs->event_thread->control.stop_step = 1;
-         print_end_stepping_range_reason ();
-         stop_stepping (ecs);
-       }
-       return;
-
-      case BPSTAT_WHAT_SINGLE:
-        if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n");
-       ecs->event_thread->stepping_over_breakpoint = 1;
-       /* Still need to check other stuff, at least the case
-          where we are stepping and step out of the right range.  */
-       break;
+              2. The initiating frame exists, and is the same as the
+              current frame.  We stop, because the exception or
+              longjmp has been caught.
 
-      case BPSTAT_WHAT_STEP_RESUME:
-        if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+              3. The initiating frame exists and is different from
+              the current frame.  This means the exception or longjmp
+              has been caught beneath the initiating frame, so keep
+              going.
 
-       delete_step_resume_breakpoint (ecs->event_thread);
-       if (ecs->event_thread->control.proceed_to_finish
-           && execution_direction == EXEC_REVERSE)
-         {
-           struct thread_info *tp = ecs->event_thread;
-
-           /* We are finishing a function in reverse, and just hit
-              the step-resume breakpoint at the start address of the
-              function, and we're almost there -- just need to back
-              up by one more single-step, which should take us back
-              to the function call.  */
-           tp->control.step_range_start = tp->control.step_range_end = 1;
-           keep_going (ecs);
-           return;
-         }
-       fill_in_stop_func (gdbarch, ecs);
-       if (stop_pc == ecs->stop_func_start
-           && execution_direction == EXEC_REVERSE)
-         {
-           /* We are stepping over a function call in reverse, and
-              just hit the step-resume breakpoint at the start
-              address of the function.  Go back to single-stepping,
-              which should take us back to the function call.  */
-           ecs->event_thread->stepping_over_breakpoint = 1;
-           keep_going (ecs);
-           return;
-         }
-       break;
+              4. longjmp breakpoint has been placed just to protect
+              against stale dummy frames and user is not interested
+              in stopping around longjmps.  */
 
-      case BPSTAT_WHAT_STOP_NOISY:
-        if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n");
-       stop_print_frame = 1;
+           if (debug_infrun)
+             fprintf_unfiltered (gdb_stdlog,
+                                 "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
 
-       /* We are about to nuke the step_resume_breakpointt via the
-          cleanup chain, so no need to worry about it here.  */
+           gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
+                       != NULL);
+           delete_exception_resume_breakpoint (ecs->event_thread);
 
-       stop_stepping (ecs);
-       return;
+           if (what.is_longjmp)
+             {
+               check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num);
 
-      case BPSTAT_WHAT_STOP_SILENT:
-        if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_SILENT\n");
-       stop_print_frame = 0;
+               if (!frame_id_p (ecs->event_thread->initiating_frame))
+                 {
+                   /* Case 4.  */
+                   keep_going (ecs);
+                   return;
+                 }
+             }
 
-       /* We are about to nuke the step_resume_breakpoin via the
-          cleanup chain, so no need to worry about it here.  */
+           init_frame = frame_find_by_id (ecs->event_thread->initiating_frame);
 
-       stop_stepping (ecs);
-       return;
+           if (init_frame)
+             {
+               struct frame_id current_id
+                 = get_frame_id (get_current_frame ());
+               if (frame_id_eq (current_id,
+                                ecs->event_thread->initiating_frame))
+                 {
+                   /* Case 2.  Fall through.  */
+                 }
+               else
+                 {
+                   /* Case 3.  */
+                   keep_going (ecs);
+                   return;
+                 }
+             }
 
-      case BPSTAT_WHAT_HP_STEP_RESUME:
-        if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n");
+           /* For Cases 1 and 2, remove the step-resume breakpoint,
+              if it exists.  */
+           delete_step_resume_breakpoint (ecs->event_thread);
 
-       delete_step_resume_breakpoint (ecs->event_thread);
-       if (ecs->event_thread->step_after_step_resume_breakpoint)
-         {
-           /* Back when the step-resume breakpoint was inserted, we
-              were trying to single-step off a breakpoint.  Go back
-              to doing that.  */
-           ecs->event_thread->step_after_step_resume_breakpoint = 0;
-           ecs->event_thread->stepping_over_breakpoint = 1;
-           keep_going (ecs);
-           return;
+           ecs->event_thread->control.stop_step = 1;
+           print_end_stepping_range_reason ();
+           stop_stepping (ecs);
          }
-       break;
+         return;
 
-      case BPSTAT_WHAT_KEEP_CHECKING:
-       break;
-      }
-  }
+       case BPSTAT_WHAT_SINGLE:
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n");
+         ecs->event_thread->stepping_over_breakpoint = 1;
+         /* Still need to check other stuff, at least the case where
+            we are stepping and step out of the right range.  */
+         break;
+
+       case BPSTAT_WHAT_STEP_RESUME:
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+
+         delete_step_resume_breakpoint (ecs->event_thread);
+         if (ecs->event_thread->control.proceed_to_finish
+             && execution_direction == EXEC_REVERSE)
+           {
+             struct thread_info *tp = ecs->event_thread;
+
+             /* We are finishing a function in reverse, and just hit
+                the step-resume breakpoint at the start address of
+                the function, and we're almost there -- just need to
+                back up by one more single-step, which should take us
+                back to the function call.  */
+             tp->control.step_range_start = tp->control.step_range_end = 1;
+             keep_going (ecs);
+             return;
+           }
+         fill_in_stop_func (gdbarch, ecs);
+         if (stop_pc == ecs->stop_func_start
+             && execution_direction == EXEC_REVERSE)
+           {
+             /* We are stepping over a function call in reverse, and
+                just hit the step-resume breakpoint at the start
+                address of the function.  Go back to single-stepping,
+                which should take us back to the function call.  */
+             ecs->event_thread->stepping_over_breakpoint = 1;
+             keep_going (ecs);
+             return;
+           }
+         break;
+
+       case BPSTAT_WHAT_STOP_NOISY:
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n");
+         stop_print_frame = 1;
+
+         /* We are about to nuke the step_resume_breakpointt via the
+            cleanup chain, so no need to worry about it here.  */
+
+         stop_stepping (ecs);
+         return;
+
+       case BPSTAT_WHAT_STOP_SILENT:
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_SILENT\n");
+         stop_print_frame = 0;
+
+         /* We are about to nuke the step_resume_breakpoin via the
+            cleanup chain, so no need to worry about it here.  */
+
+         stop_stepping (ecs);
+         return;
+
+       case BPSTAT_WHAT_HP_STEP_RESUME:
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n");
+
+         delete_step_resume_breakpoint (ecs->event_thread);
+         if (ecs->event_thread->step_after_step_resume_breakpoint)
+           {
+             /* Back when the step-resume breakpoint was inserted, we
+                were trying to single-step off a breakpoint.  Go back
+                to doing that.  */
+             ecs->event_thread->step_after_step_resume_breakpoint = 0;
+             ecs->event_thread->stepping_over_breakpoint = 1;
+             keep_going (ecs);
+             return;
+           }
+         break;
+
+       case BPSTAT_WHAT_KEEP_CHECKING:
+         break;
+       }
+    }
 
   /* We come here if we hit a breakpoint but should not
      stop for it.  Possibly we also were stepping
index 33b13fa..b82c248 100644 (file)
@@ -1901,7 +1901,7 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
 /* Resume LP.  */
 
 static void
-resume_lwp (struct lwp_info *lp, int step)
+resume_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
 {
   if (lp->stopped)
     {
@@ -1919,14 +1919,18 @@ resume_lwp (struct lwp_info *lp, int step)
        {
          if (debug_linux_nat)
            fprintf_unfiltered (gdb_stdlog,
-                               "RC:  PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
-                               target_pid_to_str (lp->ptid));
+                               "RC: Resuming sibling %s, %s, %s\n",
+                               target_pid_to_str (lp->ptid),
+                               (signo != GDB_SIGNAL_0
+                                ? strsignal (gdb_signal_to_host (signo))
+                                : "0"),
+                               step ? "step" : "resume");
 
          if (linux_nat_prepare_to_resume != NULL)
            linux_nat_prepare_to_resume (lp);
          linux_ops->to_resume (linux_ops,
                                pid_to_ptid (GET_LWP (lp->ptid)),
-                               step, GDB_SIGNAL_0);
+                               step, signo);
          lp->stopped = 0;
          lp->step = step;
          memset (&lp->siginfo, 0, sizeof (lp->siginfo));
@@ -1949,10 +1953,27 @@ resume_lwp (struct lwp_info *lp, int step)
     }
 }
 
+/* Resume LWP, with the last stop signal, if it is in pass state.  */
+
 static int
-resume_callback (struct lwp_info *lp, void *data)
+linux_nat_resume_callback (struct lwp_info *lp, void *data)
 {
-  resume_lwp (lp, 0);
+  enum gdb_signal signo = GDB_SIGNAL_0;
+
+  if (lp->stopped)
+    {
+      struct thread_info *thread;
+
+      thread = find_thread_ptid (lp->ptid);
+      if (thread != NULL)
+       {
+         if (signal_pass_state (thread->suspend.stop_signal))
+           signo = thread->suspend.stop_signal;
+         thread->suspend.stop_signal = GDB_SIGNAL_0;
+       }
+    }
+
+  resume_lwp (lp, 0, signo);
   return 0;
 }
 
@@ -2059,11 +2080,11 @@ linux_nat_resume (struct target_ops *ops,
     }
 
   /* Mark LWP as not stopped to prevent it from being continued by
-     resume_callback.  */
+     linux_nat_resume_callback.  */
   lp->stopped = 0;
 
   if (resume_many)
-    iterate_over_lwps (ptid, resume_callback, NULL);
+    iterate_over_lwps (ptid, linux_nat_resume_callback, NULL);
 
   /* Convert to something the lower layer understands.  */
   ptid = pid_to_ptid (GET_LWP (lp->ptid));
@@ -2881,110 +2902,39 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 
       if (WSTOPSIG (status) != SIGSTOP)
        {
-         if (linux_nat_status_is_event (status))
-           {
-             /* If a LWP other than the LWP that we're reporting an
-                event for has hit a GDB breakpoint (as opposed to
-                some random trap signal), then just arrange for it to
-                hit it again later.  We don't keep the SIGTRAP status
-                and don't forward the SIGTRAP signal to the LWP.  We
-                will handle the current event, eventually we will
-                resume all LWPs, and this one will get its breakpoint
-                trap again.
-
-                If we do not do this, then we run the risk that the
-                user will delete or disable the breakpoint, but the
-                thread will have already tripped on it.  */
-
-             /* Save the trap's siginfo in case we need it later.  */
-             save_siginfo (lp);
-
-             save_sigtrap (lp);
-
-             /* Now resume this LWP and get the SIGSTOP event.  */
-             errno = 0;
-             ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
-             if (debug_linux_nat)
-               {
-                 fprintf_unfiltered (gdb_stdlog,
-                                     "PTRACE_CONT %s, 0, 0 (%s)\n",
-                                     target_pid_to_str (lp->ptid),
-                                     errno ? safe_strerror (errno) : "OK");
-
-                 fprintf_unfiltered (gdb_stdlog,
-                                     "SWC: Candidate SIGTRAP event in %s\n",
-                                     target_pid_to_str (lp->ptid));
-               }
-             /* Hold this event/waitstatus while we check to see if
-                there are any more (we still want to get that SIGSTOP).  */
-             stop_wait_callback (lp, NULL);
-
-             /* Hold the SIGTRAP for handling by linux_nat_wait.  If
-                there's another event, throw it back into the
-                queue.  */
-             if (lp->status)
-               {
-                 if (debug_linux_nat)
-                   fprintf_unfiltered (gdb_stdlog,
-                                       "SWC: kill %s, %s\n",
-                                       target_pid_to_str (lp->ptid),
-                                       status_to_str ((int) status));
-                 kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
-               }
+         /* The thread was stopped with a signal other than SIGSTOP.  */
 
-             /* Save the sigtrap event.  */
-             lp->status = status;
-             return 0;
-           }
-         else
-           {
-             /* The thread was stopped with a signal other than
-                SIGSTOP, and didn't accidentally trip a breakpoint.  */
+         /* Save the trap's siginfo in case we need it later.  */
+         save_siginfo (lp);
 
-             if (debug_linux_nat)
-               {
-                 fprintf_unfiltered (gdb_stdlog,
-                                     "SWC: Pending event %s in %s\n",
-                                     status_to_str ((int) status),
-                                     target_pid_to_str (lp->ptid));
-               }
-             /* Now resume this LWP and get the SIGSTOP event.  */
-             errno = 0;
-             ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
-             if (debug_linux_nat)
-               fprintf_unfiltered (gdb_stdlog,
-                                   "SWC: PTRACE_CONT %s, 0, 0 (%s)\n",
-                                   target_pid_to_str (lp->ptid),
-                                   errno ? safe_strerror (errno) : "OK");
-
-             /* Hold this event/waitstatus while we check to see if
-                there are any more (we still want to get that SIGSTOP).  */
-             stop_wait_callback (lp, NULL);
-
-             /* If the lp->status field is still empty, use it to
-                hold this event.  If not, then this event must be
-                returned to the event queue of the LWP.  */
-             if (lp->status)
-               {
-                 if (debug_linux_nat)
-                   {
-                     fprintf_unfiltered (gdb_stdlog,
-                                         "SWC: kill %s, %s\n",
-                                         target_pid_to_str (lp->ptid),
-                                         status_to_str ((int) status));
-                   }
-                 kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
-               }
-             else
-               lp->status = status;
-             return 0;
-           }
+         save_sigtrap (lp);
+
+         if (debug_linux_nat)
+           fprintf_unfiltered (gdb_stdlog,
+                               "SWC: Pending event %s in %s\n",
+                               status_to_str ((int) status),
+                               target_pid_to_str (lp->ptid));
+
+         /* Save the sigtrap event.  */
+         lp->status = status;
+         gdb_assert (!lp->stopped);
+         gdb_assert (lp->signalled);
+         lp->stopped = 1;
        }
       else
        {
          /* We caught the SIGSTOP that we intended to catch, so
             there's no SIGSTOP pending.  */
+
+         if (debug_linux_nat)
+           fprintf_unfiltered (gdb_stdlog,
+                               "SWC: Delayed SIGSTOP caught for %s.\n",
+                               target_pid_to_str (lp->ptid));
+
          lp->stopped = 1;
+
+         /* Reset SIGNALLED only after the stop_wait_callback call
+            above as it does gdb_assert on SIGNALLED.  */
          lp->signalled = 0;
        }
     }
@@ -3238,7 +3188,7 @@ stop_and_resume_callback (struct lwp_info *lp, void *data)
                fprintf_unfiltered (gdb_stdlog,
                                    "SARC: re-resuming LWP %ld\n",
                                    GET_LWP (lp->ptid));
-             resume_lwp (lp, lp->step);
+             resume_lwp (lp, lp->step, GDB_SIGNAL_0);
            }
          else
            {
@@ -3612,54 +3562,6 @@ retry:
        lp = NULL;
     }
 
-  if (lp && lp->signalled && lp->last_resume_kind != resume_stop)
-    {
-      /* A pending SIGSTOP may interfere with the normal stream of
-         events.  In a typical case where interference is a problem,
-         we have a SIGSTOP signal pending for LWP A while
-         single-stepping it, encounter an event in LWP B, and take the
-         pending SIGSTOP while trying to stop LWP A.  After processing
-         the event in LWP B, LWP A is continued, and we'll never see
-         the SIGTRAP associated with the last time we were
-         single-stepping LWP A.  */
-
-      /* Resume the thread.  It should halt immediately returning the
-         pending SIGSTOP.  */
-      registers_changed ();
-      if (linux_nat_prepare_to_resume != NULL)
-       linux_nat_prepare_to_resume (lp);
-      linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
-                           lp->step, GDB_SIGNAL_0);
-      if (debug_linux_nat)
-       fprintf_unfiltered (gdb_stdlog,
-                           "LLW: %s %s, 0, 0 (expect SIGSTOP)\n",
-                           lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
-                           target_pid_to_str (lp->ptid));
-      lp->stopped = 0;
-      gdb_assert (lp->resumed);
-
-      /* Catch the pending SIGSTOP.  */
-      status = lp->status;
-      lp->status = 0;
-
-      stop_wait_callback (lp, NULL);
-
-      /* If the lp->status field isn't empty, we caught another signal
-        while flushing the SIGSTOP.  Return it back to the event
-        queue of the LWP, as we already have an event to handle.  */
-      if (lp->status)
-       {
-         if (debug_linux_nat)
-           fprintf_unfiltered (gdb_stdlog,
-                               "LLW: kill %s, %s\n",
-                               target_pid_to_str (lp->ptid),
-                               status_to_str (lp->status));
-         kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
-       }
-
-      lp->status = status;
-    }
-
   if (!target_can_async_p ())
     {
       /* Causes SIGINT to be passed on to the attached process.  */
index 38ecd08..01e3f30 100644 (file)
@@ -4644,6 +4644,28 @@ append_resumption (char *p, char *endp,
   return p;
 }
 
+/* Append a vCont continue-with-signal action for threads that have a
+   non-zero stop signal.  */
+
+static char *
+append_pending_thread_resumptions (char *p, char *endp, ptid_t ptid)
+{
+  struct thread_info *thread;
+
+  ALL_THREADS (thread)
+    if (ptid_match (thread->ptid, ptid)
+       && !ptid_equal (inferior_ptid, thread->ptid)
+       && thread->suspend.stop_signal != GDB_SIGNAL_0
+       && signal_pass_state (thread->suspend.stop_signal))
+      {
+       p = append_resumption (p, endp, thread->ptid,
+                              0, thread->suspend.stop_signal);
+       thread->suspend.stop_signal = GDB_SIGNAL_0;
+      }
+
+  return p;
+}
+
 /* Resume the remote inferior by using a "vCont" packet.  The thread
    to be resumed is PTID; STEP and SIGGNAL indicate whether the
    resumed thread should be single-stepped and/or signalled.  If PTID
@@ -4696,6 +4718,10 @@ remote_vcont_resume (ptid_t ptid, int step, enum gdb_signal siggnal)
          p = append_resumption (p, endp, inferior_ptid, step, siggnal);
        }
 
+      /* Also pass down any pending signaled resumption for other
+        threads not the current.  */
+      p = append_pending_thread_resumptions (p, endp, ptid);
+
       /* And continue others without a signal.  */
       append_resumption (p, endp, ptid, /*step=*/ 0, GDB_SIGNAL_0);
     }
index 233d355..e5bdf4d 100644 (file)
@@ -920,10 +920,16 @@ extern void target_detach (char *, int);
 
 extern void target_disconnect (char *, int);
 
-/* Resume execution of the target process PTID.  STEP says whether to
-   single-step or to run free; SIGGNAL is the signal to be given to
-   the target, or GDB_SIGNAL_0 for no signal.  The caller may not
-   pass GDB_SIGNAL_DEFAULT.  */
+/* Resume execution of the target process PTID (or a group of
+   threads).  STEP says whether to single-step or to run free; SIGGNAL
+   is the signal to be given to the target, or GDB_SIGNAL_0 for no
+   signal.  The caller may not pass GDB_SIGNAL_DEFAULT.  A specific
+   PTID means `step/resume only this process id'.  A wildcard PTID
+   (all threads, or all threads of process) means `step/resume
+   INFERIOR_PTID, and let other threads (for which the wildcard PTID
+   matches) resume with their 'thread->suspend.stop_signal' signal
+   (usually GDB_SIGNAL_0) if it is in "pass" state, or with no signal
+   if in "no pass" state.  */
 
 extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);
 
index 36a3534..0e857df 100644 (file)
@@ -1,3 +1,11 @@
+2012-06-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdb.threads/siginfo-threads.exp: New file.
+       * gdb.threads/siginfo-threads.c: New file.
+       * gdb.threads/sigstep-threads.exp: New file.
+       * gdb.threads/sigstep-threads.c: New file.
+
 2012-06-28  Tom Tromey  <tromey@redhat.com>
 
        * gdb.go/package.exp: Partially revert earlier patch; use
diff --git a/gdb/testsuite/gdb.threads/siginfo-threads.c b/gdb/testsuite/gdb.threads/siginfo-threads.c
new file mode 100644 (file)
index 0000000..5859088
--- /dev/null
@@ -0,0 +1,457 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010-2012 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/>.  */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+
+#define gettid() syscall (__NR_gettid)
+#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig)
+
+/* Terminate always in the main task.  It can lock up with SIGSTOPped
+   GDB otherwise.  */
+#define TIMEOUT (gettid () == getpid() ? 10 : 15)
+
+static pid_t thread1_tid;
+static pthread_cond_t thread1_tid_cond
+  = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread1_tid_mutex
+  = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static int thread1_sigusr1_hit;
+static int thread1_sigusr2_hit;
+
+static pid_t thread2_tid;
+static pthread_cond_t thread2_tid_cond
+  = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread2_tid_mutex
+  = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static int thread2_sigusr1_hit;
+static int thread2_sigusr2_hit;
+
+static pthread_mutex_t terminate_mutex
+  = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+/* Do not use alarm as it would create a ptrace event which would hang
+   us up if we are being traced by GDB, which we stopped
+   ourselves.  */
+
+static void
+timed_mutex_lock (pthread_mutex_t *mutex)
+{
+  int i;
+  struct timespec start, now;
+
+  i = clock_gettime (CLOCK_MONOTONIC, &start);
+  assert (i == 0);
+
+  do
+    {
+      i = pthread_mutex_trylock (mutex);
+      if (i == 0)
+       return;
+      assert (i == EBUSY);
+
+      i = clock_gettime (CLOCK_MONOTONIC, &now);
+      assert (i == 0);
+      assert (now.tv_sec >= start.tv_sec);
+    }
+  while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+  fprintf (stderr, "Timed out waiting for internal lock!\n");
+  exit (EXIT_FAILURE);
+}
+
+static void
+handler (int signo, siginfo_t *siginfo, void *exception)
+{
+  int *varp;
+
+  assert (siginfo->si_signo == signo);
+  assert (siginfo->si_code == SI_TKILL);
+  assert (siginfo->si_pid == getpid ());
+
+  if (gettid () == thread1_tid)
+    {
+      if (signo == SIGUSR1)
+       varp = &thread1_sigusr1_hit;
+      else if (signo == SIGUSR2)
+       varp = &thread1_sigusr2_hit;
+      else
+       assert (0);
+    }
+  else if (gettid () == thread2_tid)
+    {
+      if (signo == SIGUSR1)
+       varp = &thread2_sigusr1_hit;
+      else if (signo == SIGUSR2)
+       varp = &thread2_sigusr2_hit;
+      else
+       assert (0);
+    }
+  else
+    assert (0);
+
+  if (*varp)
+    {
+      fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo,
+              (unsigned long) gettid ());
+      exit (EXIT_FAILURE);
+    }
+  *varp = 1;
+}
+
+static void *
+thread1_func (void *unused)
+{
+  int i;
+
+  timed_mutex_lock (&thread1_tid_mutex);
+
+  /* THREAD1_TID_MUTEX must be already locked to avoid a race.  */
+  thread1_tid = gettid ();
+
+  i = pthread_cond_signal (&thread1_tid_cond);
+  assert (i == 0);
+  i = pthread_mutex_unlock (&thread1_tid_mutex);
+  assert (i == 0);
+
+  /* Be sure the "t (tracing stop)" test can proceed for both
+     threads.  */
+  timed_mutex_lock (&terminate_mutex);
+  i = pthread_mutex_unlock (&terminate_mutex);
+  assert (i == 0);
+
+  if (!thread1_sigusr1_hit)
+    {
+      fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n");
+      exit (EXIT_FAILURE);
+    }
+  if (!thread1_sigusr2_hit)
+    {
+      fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n");
+      exit (EXIT_FAILURE);
+    }
+
+  return NULL;
+}
+
+static void *
+thread2_func (void *unused)
+{
+  int i;
+
+  timed_mutex_lock (&thread2_tid_mutex);
+
+  /* THREAD2_TID_MUTEX must be already locked to avoid a race.  */
+  thread2_tid = gettid ();
+
+  i = pthread_cond_signal (&thread2_tid_cond);
+  assert (i == 0);
+  i = pthread_mutex_unlock (&thread2_tid_mutex);
+  assert (i == 0);
+
+  /* Be sure the "t (tracing stop)" test can proceed for both
+     threads.  */
+  timed_mutex_lock (&terminate_mutex);
+  i = pthread_mutex_unlock (&terminate_mutex);
+  assert (i == 0);
+
+  if (!thread2_sigusr1_hit)
+    {
+      fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n");
+      exit (EXIT_FAILURE);
+    }
+  if (!thread2_sigusr2_hit)
+    {
+      fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n");
+      exit (EXIT_FAILURE);
+    }
+
+  return NULL;
+}
+
+static const char *
+proc_string (const char *filename, const char *line)
+{
+  FILE *f;
+  static char buf[LINE_MAX];
+  size_t line_len = strlen (line);
+
+  f = fopen (filename, "r");
+  if (f == NULL)
+    {
+      fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
+              strerror (errno));
+      exit (EXIT_FAILURE);
+    }
+  while (errno = 0, fgets (buf, sizeof (buf), f))
+    {
+      char *s;
+
+      s = strchr (buf, '\n');
+      assert (s != NULL);
+      *s = 0;
+
+      if (strncmp (buf, line, line_len) != 0)
+       continue;
+
+      if (fclose (f))
+       {
+         fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
+                  strerror (errno));
+         exit (EXIT_FAILURE);
+       }
+
+      return &buf[line_len];
+    }
+  if (errno != 0)
+    {
+      fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
+      exit (EXIT_FAILURE);
+    }
+  fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
+  exit (EXIT_FAILURE);
+}
+
+static unsigned long
+proc_ulong (const char *filename, const char *line)
+{
+  const char *s = proc_string (filename, line);
+  long retval;
+  char *end;
+
+  errno = 0;
+  retval = strtol (s, &end, 10);
+  if (retval < 0 || retval >= LONG_MAX || (end && *end))
+    {
+      fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
+              strerror (errno));
+      exit (EXIT_FAILURE);
+    }
+  return retval;
+}
+
+static void
+state_wait (pid_t process, const char *wanted)
+{
+  char *filename;
+  int i;
+  struct timespec start, now;
+  const char *state;
+
+  i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
+  assert (i > 0);
+
+  i = clock_gettime (CLOCK_MONOTONIC, &start);
+  assert (i == 0);
+
+  do
+    {
+      state = proc_string (filename, "State:\t");
+
+      /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0
+        has changed "T (tracing stop)" to "t (tracing stop)".  Make the GDB
+        testcase backward compatible with older Linux kernels.  */
+      if (strcmp (state, "T (tracing stop)") == 0)
+       state = "t (tracing stop)";
+
+      if (strcmp (state, wanted) == 0)
+       {
+         free (filename);
+         return;
+       }
+
+      if (sched_yield ())
+       {
+         perror ("sched_yield()");
+         exit (EXIT_FAILURE);
+       }
+
+      i = clock_gettime (CLOCK_MONOTONIC, &now);
+      assert (i == 0);
+      assert (now.tv_sec >= start.tv_sec);
+    }
+  while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+  fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
+          (unsigned long) process, wanted, state);
+  exit (EXIT_FAILURE);
+}
+
+static volatile pid_t tracer = 0;
+static pthread_t thread1, thread2;
+
+static void
+cleanup (void)
+{
+  printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
+
+  if (tracer)
+    {
+      int i;
+      int tracer_save = tracer;
+
+      tracer = 0;
+
+      i = kill (tracer_save, SIGCONT);
+      assert (i == 0);
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  int i;
+  int standalone = 0;
+  struct sigaction act;
+
+  if (argc == 2 && strcmp (argv[1], "-s") == 0)
+    standalone = 1;
+  else
+    assert (argc == 1);
+
+  setbuf (stdout, NULL);
+
+  timed_mutex_lock (&thread1_tid_mutex);
+  timed_mutex_lock (&thread2_tid_mutex);
+
+  timed_mutex_lock (&terminate_mutex);
+
+  errno = 0;
+  memset (&act, 0, sizeof (act));
+  act.sa_sigaction = handler;
+  act.sa_flags = SA_RESTART | SA_SIGINFO;
+  i = sigemptyset (&act.sa_mask);
+  assert_perror (errno);
+  assert (i == 0);
+  i = sigaction (SIGUSR1, &act, NULL);
+  assert_perror (errno);
+  assert (i == 0);
+  i = sigaction (SIGUSR2, &act, NULL);
+  assert_perror (errno);
+  assert (i == 0);
+
+  i = pthread_create (&thread1, NULL, thread1_func, NULL);
+  assert (i == 0);
+
+  i = pthread_create (&thread2, NULL, thread2_func, NULL);
+  assert (i == 0);
+
+  if (!standalone)
+    {
+      tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
+      if (tracer == 0)
+       {
+         fprintf (stderr, "The testcase must be run by GDB!\n");
+         exit (EXIT_FAILURE);
+       }
+      if (tracer != getppid ())
+       {
+         fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
+         exit (EXIT_FAILURE);
+       }
+    }
+
+  /* SIGCONT our debugger in the case of our crash as we would deadlock
+     otherwise.  */
+
+  atexit (cleanup);
+
+  printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
+
+  if (tracer)
+    {
+      i = kill (tracer, SIGSTOP);
+      assert (i == 0);
+      state_wait (tracer, "T (stopped)");
+    }
+
+  /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex)
+     and so they could not trigger the signals before GDB is unstopped
+     later.  Threads get resumed by the pthread_cond_wait below.  Use
+     `while' loops for protection against spurious pthread_cond_wait
+     wakeups.  */
+
+  printf ("Waiting till the threads initialize their TIDs.\n");
+
+  while (thread1_tid == 0)
+    {
+      i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
+      assert (i == 0);
+    }
+
+  while (thread2_tid == 0)
+    {
+      i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
+      assert (i == 0);
+    }
+
+  printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
+         (unsigned long) thread1_tid, (unsigned long) thread2_tid,
+         (unsigned long) getpid ());
+
+  errno = 0;
+  i = tgkill (getpid (), thread1_tid, SIGUSR1);
+  assert_perror (errno);
+  assert (i == 0);
+  i = tgkill (getpid (), thread1_tid, SIGUSR2);
+  assert_perror (errno);
+  assert (i == 0);
+  i = tgkill (getpid (), thread2_tid, SIGUSR1);
+  assert_perror (errno);
+  assert (i == 0);
+  i = tgkill (getpid (), thread2_tid, SIGUSR2);
+  assert_perror (errno);
+  assert (i == 0);
+
+  printf ("Waiting till the threads are trapped by the signals.\n");
+
+  if (tracer)
+    {
+      /* s390x-unknown-linux-gnu will fail with "R (running)".  */
+
+      state_wait (thread1_tid, "t (tracing stop)");
+
+      state_wait (thread2_tid, "t (tracing stop)");
+    }
+
+  cleanup ();
+
+  printf ("Joining the threads.\n");
+
+  i = pthread_mutex_unlock (&terminate_mutex);
+  assert (i == 0);
+
+  i = pthread_join (thread1, NULL);
+  assert (i == 0);
+
+  i = pthread_join (thread2, NULL);
+  assert (i == 0);
+
+  printf ("Exiting.\n");       /* break-at-exit */
+
+  return EXIT_SUCCESS;
+}
diff --git a/gdb/testsuite/gdb.threads/siginfo-threads.exp b/gdb/testsuite/gdb.threads/siginfo-threads.exp
new file mode 100644 (file)
index 0000000..f3f9c51
--- /dev/null
@@ -0,0 +1,99 @@
+# Copyright 2010-2012 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/>.
+
+standard_testfile
+set executable ${testfile}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" \
+        ${binfile} \
+        executable \
+        [list debug additional_flags=-lrt]] != "" } {
+    return -1
+}
+
+clean_restart $testfile
+
+if ![runto_main] {
+    return -1
+}
+
+# `nostop noprint pass' could in some cases report false PASS due to
+# the (preempt 'handle') code path in linux-nat.c.
+
+gdb_test "handle SIGUSR1 stop print pass" \
+    "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR1\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
+gdb_test "handle SIGUSR2 stop print pass" \
+    "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR2\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
+
+gdb_breakpoint [gdb_get_line_number "break-at-exit"]
+
+set test "get pid"
+gdb_test_multiple "p getpid ()" $test {
+    -re " = (\[0-9\]+)\r\n$gdb_prompt $" {
+       set pid $expect_out(1,string)
+       pass $test
+    }
+}
+
+for {set sigcount 0} {$sigcount < 4} {incr sigcount} {
+    set test "catch signal $sigcount"
+    set sigusr ""
+    gdb_test_multiple "continue" $test {
+       -re "Program received signal SIGUSR(\[12\]), User defined signal \[12\]\\.\r\n.*\r\n$gdb_prompt $" {
+           set sigusr $expect_out(1,string)
+           pass $test
+       }
+    }
+    if {$sigusr == ""} {
+       return -1
+    }
+
+    set test "signal $sigcount si_signo"
+    if {$sigusr == 1} {
+       set signo 10
+    } else {
+       set signo 12
+    }
+    gdb_test_multiple {p $_siginfo.si_signo} $test {
+       -re " = $signo\r\n$gdb_prompt $" {
+           pass $test
+       }
+       -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
+           unsupported $test
+       }
+    }
+
+    set test "signal $sigcount si_code is SI_TKILL"
+    gdb_test_multiple {p $_siginfo.si_code} $test {
+       -re " = -6\r\n$gdb_prompt $" {
+           pass $test
+       }
+       -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
+           unsupported $test
+       }
+    }
+
+    set test "signal $sigcount si_pid"
+    gdb_test_multiple {p $_siginfo._sifields._kill.si_pid} $test {
+       -re " = $pid\r\n$gdb_prompt $" {
+           pass $test
+       }
+       -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
+           unsupported $test
+       }
+    }
+}
+
+gdb_continue_to_breakpoint break-at-exit ".*break-at-exit.*"
diff --git a/gdb/testsuite/gdb.threads/sigstep-threads.c b/gdb/testsuite/gdb.threads/sigstep-threads.c
new file mode 100644 (file)
index 0000000..ca77fe4
--- /dev/null
@@ -0,0 +1,54 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010-2012 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 <signal.h>
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, (tgid), (tid), (sig))
+#define gettid() syscall (__NR_gettid)
+
+static volatile int var;
+
+static void
+handler (int signo)    /* step-0 */
+{                      /* step-0 */
+  var++;               /* step-1 */
+  tgkill (getpid (), gettid (), SIGUSR1);      /* step-2 */
+}
+
+static void *
+start (void *arg)
+{
+  tgkill (getpid (), gettid (), SIGUSR1);
+  assert (0);
+  return NULL;
+}
+
+int
+main (void)
+{
+  pthread_t thread;
+
+  signal (SIGUSR1, handler);
+
+  pthread_create (&thread, NULL, start, NULL);
+  start (NULL);        /* main-start */
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/sigstep-threads.exp b/gdb/testsuite/gdb.threads/sigstep-threads.exp
new file mode 100644 (file)
index 0000000..484ca37
--- /dev/null
@@ -0,0 +1,73 @@
+# Copyright 2010-2012 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/>.
+
+standard_testfile
+set executable ${testfile}
+
+if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested ${testfile}.exp
+    return -1
+}
+
+clean_restart $executable
+
+if ![runto_main] {
+    return -1;
+}
+
+# `noprint' would not test the full logic of GDB.
+gdb_test "handle SIGUSR1 nostop print pass" \
+    "\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
+
+gdb_test_no_output "set scheduler-locking off"
+
+gdb_breakpoint [gdb_get_line_number "step-1"]
+gdb_test_no_output {set $step1=$bpnum}
+gdb_continue_to_breakpoint "step-1" ".* step-1 .*"
+gdb_test_no_output {disable $step1}
+
+# 1 as we are now stopped at the `step-1' label.
+set step_at 1
+for {set i 0} {$i < 100} {incr i} {
+    set test "step $i"
+    # Presume this step failed - as in the case of a timeout.
+    set failed 1
+    gdb_test_multiple "step" $test {
+       -re "\r\nProgram received signal SIGUSR1, User defined signal 1.\r\n" {
+           exp_continue -continue_timer
+       }
+       -re "step-(\[012\]).*\r\n$gdb_prompt $" {
+           set now $expect_out(1,string)
+           if {$step_at == 2 && $now == 1} {
+               set failed 0
+           } elseif {$step_at == 1 && $now == 2} {
+               set failed 0
+               # Continue over the re-signalling back to the handle entry.
+               gdb_test_no_output {enable $step1} ""
+               gdb_test "continue" " step-1 .*" ""
+               set now 1
+               gdb_test_no_output {disable $step1} ""
+           } else  {
+               fail $test
+           }
+           set step_at $now
+       }
+    }
+    if $failed {
+       return
+    }
+}
+# We can never reliably say the racy problematic case has been tested.
+pass "step"
index d361dd8..ff5220d 100644 (file)
@@ -54,7 +54,7 @@ void _initialize_thread (void);
 
 /* Prototypes for local functions.  */
 
-static struct thread_info *thread_list = NULL;
+struct thread_info *thread_list = NULL;
 static int highest_thread_num;
 
 static void thread_command (char *tidstr, int from_tty);