This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC 5/6] Associate siginfo_t with any signal


Hi,

this patch brings the primary fix of this patchset.  siginfo_t gets associated
with every signal (both target_signal_t and int host_signal).

<signal.h> included from "gdb/signals.h" is fatal on ppc64-rhel55 host with
--enable-targets=all as there are various conflicts like:

In file included from mips-linux-tdep.c:39:
mips-linux-tdep.h:21:1: error: "ELF_NGREG" redefined
In file included from /usr/include/asm/sigcontext.h:12,
                 from /usr/include/bits/sigcontext.h:28,
                 from /usr/include/signal.h:333,
                 from ./../include/gdb/signals.h:24,
                 from defs.h:68,
                 from mips-linux-tdep.c:21:
/usr/include/asm/elf.h:91:1: error: this is the location of the previous definition
make[2]: *** [mips-linux-tdep.o] Error 1

I believe *-tdep.h file must not define such a general symbol as "ELF_NGREG".
I can fix these *-tdep.* files if this way gets approved.

It has some issues on ia64 and s390x also has gdb.base/siginfo-addr.exp
regression.  I can fix up them later.

I would fill-in the gdb/ ChangeLog if the patch'es idea does not get rejected.
I understand the order is not right.  Writing the ChangeLog will be a very
costly task that could get dropped in several seconds of a first review.


Thanks,
Jan


include/gdb/
2010-07-26  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* signals.h: Include <signal.h>.
	(target_signal_t) <siginfo>: New field.
	(TARGET_SIGNAL_SIGINFO): New accessor.

gdb/
2010-07-26  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* configure.ac (HAVE_RT_TGSIGQUEUEINFO_SYSCALL): New check.
	* config.in: Regenerated.
	* configure: Regenerated.
	* gnu-nat.c: Use target_signal_t.siginfo and lwp_info.siginfo.
	* i386-linux-nat.c: Likewise.
	* inf-ptrace.c: Likewise.
	* inf-ttrace.c: Likewise.
	* linux-nat.c: Likewise.
	* linux-nat.h: Likewise.
	* procfs.c: Likewise.
	* rs6000-nat.c: Likewise.
	* spu-linux-nat.c: Likewise.
	* target.c: Likewise.
	* target.h: Likewise.

gdb/testsuite/
2010-07-26  Jan Kratochvil  <jan.kratochvil@redhat.com>

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

--- a/gdb/config.in
+++ b/gdb/config.in
@@ -461,6 +461,9 @@
 /* Define to 1 if you have the `realpath' function. */
 #undef HAVE_REALPATH
 
+/* Define if you support the rt_tgsigqueueinfo syscall. */
+#undef HAVE_RT_TGSIGQUEUEINFO_SYSCALL
+
 /* Define to 1 if you have the `sbrk' function. */
 #undef HAVE_SBRK
 
--- a/gdb/configure
+++ b/gdb/configure
@@ -14567,6 +14567,41 @@ $as_echo "#define HAVE_TKILL_SYSCALL 1" >>confdefs.h
 
 fi
 
+if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo" >&5
+$as_echo_n "checking whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo... " >&6; }
+if test "${gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/syscall.h>
+int
+main ()
+{
+int i = __NR_rt_tgsigqueueinfo;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=yes
+else
+  gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=no
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" >&5
+$as_echo "$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" >&6; }
+fi
+if test "x$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" = "xyes" && test "x$ac_cv_func_syscall" = "xyes"; then
+
+$as_echo "#define HAVE_RT_TGSIGQUEUEINFO_SYSCALL 1" >>confdefs.h
+
+fi
+
 ac_fn_c_check_decl "$LINENO" "ADDR_NO_RANDOMIZE" "ac_cv_have_decl_ADDR_NO_RANDOMIZE" "#include <sys/personality.h>
 "
 if test "x$ac_cv_have_decl_ADDR_NO_RANDOMIZE" = x""yes; then :
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1637,6 +1637,23 @@ if test "x$gdb_cv_sys_syscall_h_has_tkill" = "xyes" && test "x$ac_cv_func_syscal
   AC_DEFINE(HAVE_TKILL_SYSCALL, 1, [Define if you support the tkill syscall.])
 fi
 
+dnl See if we have a sys/syscall header file that has __NR_rt_tgsigqueueinfo.
+if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then
+   AC_CACHE_CHECK([whether <sys/syscall.h> has __NR_rt_tgsigqueueinfo],
+                  gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo,
+     AC_TRY_COMPILE(
+       [#include <sys/syscall.h>],
+       [int i = __NR_rt_tgsigqueueinfo;],
+       gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=yes,
+       gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo=no
+     )
+   )
+fi
+dnl See if we can issue rt_tgsigqueueinfo syscall.
+if test "x$gdb_cv_sys_syscall_h_has_rt_tgsigqueueinfo" = "xyes" && test "x$ac_cv_func_syscall" = "xyes"; then
+  AC_DEFINE(HAVE_RT_TGSIGQUEUEINFO_SYSCALL, 1, [Define if you support the rt_tgsigqueueinfo syscall.])
+fi
+
 dnl Check if we can disable the virtual address space randomization.
 dnl The functionality of setarch -R.
 AC_CHECK_DECLS([ADDR_NO_RANDOMIZE],,, [#include <sys/personality.h>])
--- a/gdb/gnu-nat.c
+++ b/gdb/gnu-nat.c
@@ -1864,7 +1864,7 @@ S_proc_wait_reply (mach_port_t reply, error_t err,
     }
   else if (pid == inf->pid)
     {
-      store_waitstatus (&inf->wait.status, status);
+      store_waitstatus (&inf->wait.status, status, NULL);
       if (inf->wait.status.kind == TARGET_WAITKIND_STOPPED)
 	/* The process has sent us a signal, and stopped itself in a sane
 	   state pending our actions.  */
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -83,6 +83,10 @@
 #define PTRACE_SETREGSET	0x4205
 #endif
 
+#ifndef PTRACE_SETSIGINFO
+# define PTRACE_SETSIGINFO	0x4203
+#endif
+
 /* Does the current host support PTRACE_GETREGSET?  */
 static int have_ptrace_getregset = -1;
 
@@ -891,6 +895,12 @@ i386_linux_resume (struct target_ops *ops,
 	}
     }
 
+  if (TARGET_SIGNAL_NE (signal, TARGET_SIGNAL_0)
+      && ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0,
+		 &TARGET_SIGNAL_SIGINFO (signal))
+	 == -1)
+    perror_with_name (("ptrace (PTRACE_SETSIGINFO)"));
+
   if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1)
     perror_with_name (("ptrace"));
 }
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -39,6 +39,11 @@
 
 
 
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO	0x4202
+# define PTRACE_SETSIGINFO	0x4203
+#endif
+
 #ifdef PT_GET_PROCESS_STATE
 
 static int
@@ -375,6 +380,12 @@ inf_ptrace_resume (struct target_ops *ops,
       request = PT_STEP;
     }
 
+  if (TARGET_SIGNAL_NE (signal, TARGET_SIGNAL_0)
+      && ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0,
+		 &TARGET_SIGNAL_SIGINFO (signal))
+	 == -1)
+    perror_with_name (("ptrace (PTRACE_SETSIGINFO)"));
+
   /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
      where it was.  If GDB wanted it to start some other way, we have
      already written a new program counter value to the child.  */
@@ -394,6 +405,7 @@ inf_ptrace_wait (struct target_ops *ops,
 {
   pid_t pid;
   int status, save_errno;
+  siginfo_t siginfo;
 
   do
     {
@@ -406,6 +418,12 @@ inf_ptrace_wait (struct target_ops *ops,
 	}
       while (pid == -1 && errno == EINTR);
 
+      /* We could also use waitid.  */
+      if (! (pid > 0 && (WIFSIGNALED (status) || WIFSTOPPED (status)))
+	  || (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo)
+	      == -1))
+	memset (&siginfo, 0, sizeof (siginfo));
+
       clear_sigint_trap ();
 
       if (pid == -1)
@@ -464,7 +482,7 @@ inf_ptrace_wait (struct target_ops *ops,
     }
 #endif
 
-  store_waitstatus (ourstatus, status);
+  store_waitstatus (ourstatus, status, &siginfo);
   return pid_to_ptid (pid);
 }
 
--- a/gdb/inf-ttrace.c
+++ b/gdb/inf-ttrace.c
@@ -995,7 +995,7 @@ inf_ttrace_wait (struct target_ops *ops,
       break;
 
     case TTEVT_EXIT:
-      store_waitstatus (ourstatus, tts.tts_u.tts_exit.tts_exitcode);
+      store_waitstatus (ourstatus, tts.tts_u.tts_exit.tts_exitcode, NULL);
       inf_ttrace_num_lwps = 0;
       break;
 
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -24,7 +24,7 @@
 #include "gdb_string.h"
 #include "gdb_wait.h"
 #include "gdb_assert.h"
-#ifdef HAVE_TKILL_SYSCALL
+#if defined HAVE_TKILL_SYSCALL || defined HAVE_RT_TGSIGQUEUEINFO_SYSCALL
 #include <unistd.h>
 #include <sys/syscall.h>
 #endif
@@ -274,6 +274,7 @@ struct simple_pid_list
 {
   int pid;
   int status;
+  siginfo_t siginfo;
   struct simple_pid_list *next;
 };
 struct simple_pid_list *stopped_pids;
@@ -350,6 +351,8 @@ static void linux_nat_async (void (*callback)
 			     void *context);
 static int linux_nat_async_mask (int mask);
 static int kill_lwp (int lwpid, int signo);
+static void kill_lwp_siginfo (struct lwp_info *lp, int signo,
+			      const siginfo_t *siginfop);
 
 static int stop_callback (struct lwp_info *lp, void *data);
 
@@ -365,18 +368,21 @@ static struct lwp_info *find_lwp_pid (ptid_t ptid);
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
 static void
-add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status,
+		 const siginfo_t *siginfop)
 {
   struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
 
   new_pid->pid = pid;
   new_pid->status = status;
+  new_pid->siginfo = *siginfop;
   new_pid->next = *listp;
   *listp = new_pid;
 }
 
 static int
-pull_pid_from_list (struct simple_pid_list **listp, int pid, int *status)
+pull_pid_from_list (struct simple_pid_list **listp, int pid,
+		    int *status_return, siginfo_t *siginfo_return)
 {
   struct simple_pid_list **p;
 
@@ -385,7 +391,9 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *status)
       {
 	struct simple_pid_list *next = (*p)->next;
 
-	*status = (*p)->status;
+	*status_return = (*p)->status;
+	if (siginfo_return)
+	  *siginfo_return = (*p)->siginfo;
 	xfree (*p);
 	*p = next;
 	return 1;
@@ -394,9 +402,9 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *status)
 }
 
 static void
-linux_record_stopped_pid (int pid, int status)
+linux_record_stopped_pid (int pid, int status, const siginfo_t *siginfop)
 {
-  add_to_pid_list (&stopped_pids, pid, status);
+  add_to_pid_list (&stopped_pids, pid, status, siginfop);
 }
 
 
@@ -411,10 +419,12 @@ linux_tracefork_child (void)
   _exit (0);
 }
 
-/* Wrapper function for waitpid which handles EINTR.  */
+/* Wrapper function for waitpid which handles EINTR.  If SIGINFO_RETURN is not
+   NULL it may be filled-in with extended information about the returned
+   signal if it is available.  It will be cleared otherwise.  */
 
 static int
-my_waitpid (int pid, int *status, int flags)
+my_waitpid (int pid, int *status, struct siginfo *siginfo_return, int flags)
 {
   int ret;
 
@@ -424,6 +434,16 @@ my_waitpid (int pid, int *status, int flags)
     }
   while (ret == -1 && errno == EINTR);
 
+  if (siginfo_return != NULL)
+    {
+      /* We could also use waitid.  */
+      if (! (ret > 0 && (WIFSIGNALED (status) || WIFSTOPPED (status)))
+	  || (ptrace (PTRACE_GETSIGINFO, ret, (PTRACE_TYPE_ARG3) 0,
+		      siginfo_return)
+	      == -1))
+	memset (siginfo_return, 0, sizeof (*siginfo_return));
+    }
+
   return ret;
 }
 
@@ -467,7 +487,7 @@ linux_test_for_tracefork (int original_pid)
   if (child_pid == 0)
     linux_tracefork_child ();
 
-  ret = my_waitpid (child_pid, &status, 0);
+  ret = my_waitpid (child_pid, &status, NULL, 0);
   if (ret == -1)
     perror_with_name (("waitpid"));
   else if (ret != child_pid)
@@ -486,7 +506,7 @@ linux_test_for_tracefork (int original_pid)
 	  return;
 	}
 
-      ret = my_waitpid (child_pid, &status, 0);
+      ret = my_waitpid (child_pid, &status, NULL, 0);
       if (ret != child_pid)
 	warning (_("linux_test_for_tracefork: failed to wait for killed child"));
       else if (!WIFSIGNALED (status))
@@ -506,7 +526,7 @@ linux_test_for_tracefork (int original_pid)
   if (ret != 0)
     warning (_("linux_test_for_tracefork: failed to resume child"));
 
-  ret = my_waitpid (child_pid, &status, 0);
+  ret = my_waitpid (child_pid, &status, NULL, 0);
 
   if (ret == child_pid && WIFSTOPPED (status)
       && status >> 16 == PTRACE_EVENT_FORK)
@@ -518,11 +538,11 @@ linux_test_for_tracefork (int original_pid)
 	  int second_status;
 
 	  linux_supports_tracefork_flag = 1;
-	  my_waitpid (second_pid, &second_status, 0);
+	  my_waitpid (second_pid, &second_status, NULL, 0);
 	  ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
 	  if (ret != 0)
 	    warning (_("linux_test_for_tracefork: failed to kill second child"));
-	  my_waitpid (second_pid, &status, 0);
+	  my_waitpid (second_pid, &status, NULL, 0);
 	}
     }
   else
@@ -532,7 +552,7 @@ linux_test_for_tracefork (int original_pid)
   ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
   if (ret != 0)
     warning (_("linux_test_for_tracefork: failed to kill child"));
-  my_waitpid (child_pid, &status, 0);
+  my_waitpid (child_pid, &status, NULL, 0);
 
   restore_child_signals_mask (&prev_mask);
 }
@@ -852,6 +872,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
 		 will notice a pending event, and bypasses actually
 		 resuming the inferior.  */
 	      lp->status = 0;
+	      memset (&lp->siginfo, 0, sizeof (lp->siginfo));
 	      lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
 	      lp->stopped = 0;
 	      lp->resumed = 1;
@@ -1351,7 +1372,7 @@ pid_is_stopped (pid_t pid)
 
 static int
 linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
-			    int *signalled)
+			    int *signalled, struct siginfo *siginfo_return)
 {
   pid_t new_pid, pid = GET_LWP (ptid);
   int status;
@@ -1385,14 +1406,14 @@ linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
   /* Make sure the initial process is stopped.  The user-level threads
      layer might want to poke around in the inferior, and that won't
      work if things haven't stabilized yet.  */
-  new_pid = my_waitpid (pid, &status, 0);
+  new_pid = my_waitpid (pid, &status, siginfo_return, 0);
   if (new_pid == -1 && errno == ECHILD)
     {
       if (first)
 	warning (_("%s is a cloned process"), target_pid_to_str (ptid));
 
       /* Try again with __WCLONE to check cloned processes.  */
-      new_pid = my_waitpid (pid, &status, __WCLONE);
+      new_pid = my_waitpid (pid, &status, siginfo_return, __WCLONE);
       *cloned = 1;
     }
 
@@ -1427,6 +1448,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
 {
   struct lwp_info *lp;
   sigset_t prev_mask;
+  siginfo_t siginfo;
 
   gdb_assert (is_lwp (ptid));
 
@@ -1462,7 +1484,8 @@ lin_lwp_attach_lwp (ptid_t ptid)
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
+      status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled,
+					   &siginfo);
       if (!WIFSTOPPED (status))
 	return -1;
 
@@ -1474,6 +1497,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
 	{
 	  lp->resumed = 1;
 	  lp->status = status;
+	  lp->siginfo = siginfo;
 	}
 
       target_post_attach (GET_LWP (lp->ptid));
@@ -1551,6 +1575,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
 {
   struct lwp_info *lp;
   int status;
+  siginfo_t siginfo;
   ptid_t ptid;
 
   linux_ops->to_attach (ops, args, from_tty);
@@ -1564,7 +1589,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
   lp = add_lwp (ptid);
 
   status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
-				       &lp->signalled);
+				       &lp->signalled, &siginfo);
   if (!WIFSTOPPED (status))
     {
       if (WIFEXITED (status))
@@ -1608,6 +1633,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
 			(long) GET_PID (lp->ptid), status_to_str (status));
 
   lp->status = status;
+  lp->siginfo = siginfo;
 
   if (target_can_async_p ())
     target_async (inferior_event_handler, 0);
@@ -1643,7 +1669,10 @@ get_pending_status (struct lwp_info *lp, int *status)
   if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
     signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal.  */
   else if (lp->status)
-    signo = target_signal_from_host (WSTOPSIG (lp->status));
+    {
+      signo = target_signal_from_host (WSTOPSIG (lp->status));
+      TARGET_SIGNAL_SIGINFO (signo) = lp->siginfo;
+    }
   else if (non_stop && !is_executing (lp->ptid))
     {
       struct thread_info *tp = find_thread_ptid (lp->ptid);
@@ -1913,6 +1942,7 @@ linux_nat_resume (struct target_ops *ops,
       inf = find_inferior_pid (ptid_get_pid (lp->ptid));
       gdb_assert (inf);
       saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+      TARGET_SIGNAL_SIGINFO (saved_signo) = lp->siginfo;
 
       /* Defer to common code if we're gaining control of the
 	 inferior.  */
@@ -1931,6 +1961,7 @@ linux_nat_resume (struct target_ops *ops,
 	  gdb_assert (TARGET_SIGNAL_EQ (signo, TARGET_SIGNAL_0));
 	  signo = saved_signo;
 	  lp->status = 0;
+	  memset (&lp->siginfo, 0, sizeof (lp->siginfo));
 	}
     }
 
@@ -2009,6 +2040,40 @@ kill_lwp (int lwpid, int signo)
   return kill (lwpid, signo);
 }
 
+/* Send an extended signal information to an LWP.  */
+
+static void
+kill_lwp_siginfo (struct lwp_info *lp, int signo, const siginfo_t *siginfop)
+{
+#ifdef HAVE_RT_TGSIGQUEUEINFO_SYSCALL
+  {
+    static int rt_tgsigqueueinfo_failed;
+
+    if (! rt_tgsigqueueinfo_failed)
+      {
+	int ret;
+
+	errno = 0;
+	ret = syscall (__NR_rt_tgsigqueueinfo, GET_PID (lp->ptid),
+		       GET_LWP (lp->ptid), signo, siginfop);
+	if (ret == 0)
+	  return;
+	if (errno == ENOSYS)
+	  rt_tgsigqueueinfo_failed = 1;
+
+	/* We may also fail with EPERM when tkill still can be used.  It will
+	   drop some associated siginfo_t information, though.  As this
+	   siginfo_t information is commonly ignored by the inferior so we
+	   should not stop.  */
+      }
+  }
+#endif
+
+  /* SIGINFOP has to be dropped - give a warning?  */
+
+  kill_lwp (GET_LWP (lp->ptid), signo);
+}
+
 /* Handle a GNU/Linux syscall trap wait response.  If we see a syscall
    event, check if the core is interested in it: if not, ignore the
    event, and keep waiting; otherwise, we need to toggle the LWP's
@@ -2144,11 +2209,17 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
 
 static int
 linux_handle_extended_wait (struct lwp_info *lp, int status,
-			    int stopping)
+			    const siginfo_t *siginfop, int stopping)
 {
   int pid = GET_LWP (lp->ptid);
   struct target_waitstatus *ourstatus = &lp->waitstatus;
   int event = status >> 16;
+  siginfo_t siginfo;
+
+  if (siginfop == NULL)
+    memset (&siginfo, 0, sizeof (siginfo));
+  else
+    siginfo = *siginfop;
 
   if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
       || event == PTRACE_EVENT_CLONE)
@@ -2159,11 +2230,11 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
       ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid);
 
       /* If we haven't already seen the new PID stop, wait for it now.  */
-      if (! pull_pid_from_list (&stopped_pids, new_pid, &status))
+      if (! pull_pid_from_list (&stopped_pids, new_pid, &status, &siginfo))
 	{
 	  /* The new child has a pending SIGSTOP.  We can't affect it until it
 	     hits the SIGSTOP, but we're already attached.  */
-	  ret = my_waitpid (new_pid, &status,
+	  ret = my_waitpid (new_pid, &status, &siginfo,
 			    (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
 	  if (ret == -1)
 	    perror_with_name (_("waiting for new child"));
@@ -2232,7 +2303,10 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 	      new_lp->signalled = 1;
 	    }
 	  else
-	    status = 0;
+	    {
+	      status = 0;
+	      memset (&siginfo, 0, sizeof (siginfo));
+	    }
 
 	  if (non_stop)
 	    {
@@ -2271,9 +2345,13 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 	      new_lp->stopped = 0;
 	      new_lp->resumed = 1;
 
-	      signo = (status
-		       ? target_signal_from_host (WSTOPSIG (status))
-		       : TARGET_SIGNAL_0);
+	      if (status == 0)
+		signo = TARGET_SIGNAL_0;
+	      else
+		{
+		  signo = target_signal_from_host (WSTOPSIG (status));
+		  TARGET_SIGNAL_SIGINFO (signo) = siginfo;
+		}
 
 	      linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
 				    0, signo);
@@ -2349,22 +2427,29 @@ LHEW: Got PTRACE_EVENT_VFORK_DONE from LWP %ld: resuming\n",
 }
 
 /* Wait for LP to stop.  Returns the wait status, or 0 if the LWP has
-   exited.  */
+   exited.  If SIGINFO_RETURN is not NULL it may be filled-in with extended
+   information about the returned signal if it is available.  It will be
+   cleared otherwise.  */
 
 static int
-wait_lwp (struct lwp_info *lp)
+wait_lwp (struct lwp_info *lp, struct siginfo *siginfo_return)
 {
   pid_t pid;
   int status;
   int thread_dead = 0;
+  siginfo_t siginfo_local;
 
   gdb_assert (!lp->stopped);
   gdb_assert (lp->status == 0);
+  
+  if (siginfo_return == NULL)
+    siginfo_return = &siginfo_local;
 
-  pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
+  pid = my_waitpid (GET_LWP (lp->ptid), &status, siginfo_return, 0);
   if (pid == -1 && errno == ECHILD)
     {
-      pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+      pid = my_waitpid (GET_LWP (lp->ptid), &status, siginfo_return,
+			__WCLONE);
       if (pid == -1 && errno == ECHILD)
 	{
 	  /* The thread has previously exited.  We need to delete it
@@ -2404,6 +2489,7 @@ wait_lwp (struct lwp_info *lp)
   if (thread_dead)
     {
       exit_lwp (lp);
+      memset (siginfo_return, 0, sizeof (*siginfo_return));
       return 0;
     }
 
@@ -2417,8 +2503,9 @@ wait_lwp (struct lwp_info *lp)
 	 on handling the event like a regular SIGTRAP from here
 	 on.  */
       status = W_STOPCODE (SIGTRAP);
+      memset (siginfo_return, 0, sizeof (*siginfo_return));
       if (linux_handle_syscall_trap (lp, 1))
-	return wait_lwp (lp);
+	return wait_lwp (lp, siginfo_return);
     }
 
   /* Handle GNU/Linux's extended waitstatus for trace events.  */
@@ -2428,29 +2515,13 @@ wait_lwp (struct lwp_info *lp)
 	fprintf_unfiltered (gdb_stdlog,
 			    "WL: Handling extended status 0x%06x\n",
 			    status);
-      if (linux_handle_extended_wait (lp, status, 1))
-	return wait_lwp (lp);
+      if (linux_handle_extended_wait (lp, status, siginfo_return, 1))
+	return wait_lwp (lp, siginfo_return);
     }
 
   return status;
 }
 
-/* Save the most recent siginfo for LP.  This is currently only called
-   for SIGTRAP; some ports use the si_addr field for
-   target_stopped_data_address.  In the future, it may also be used to
-   restore the siginfo of requeued signals.  */
-
-static void
-save_siginfo (struct lwp_info *lp)
-{
-  errno = 0;
-  ptrace (PTRACE_GETSIGINFO, GET_LWP (lp->ptid),
-	  (PTRACE_TYPE_ARG3) 0, &lp->siginfo);
-
-  if (errno != 0)
-    memset (&lp->siginfo, 0, sizeof (lp->siginfo));
-}
-
 /* Send a SIGSTOP to LP.  */
 
 static int
@@ -2508,7 +2579,10 @@ set_ignore_sigint (struct lwp_info *lp, void *data)
      flag to consume the next one.  */
   if (lp->stopped && lp->status != 0 && WIFSTOPPED (lp->status)
       && WSTOPSIG (lp->status) == SIGINT)
-    lp->status = 0;
+    {
+      lp->status = 0;
+      memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+    }
   else
     lp->ignore_sigint = 1;
 
@@ -2620,8 +2694,9 @@ stop_wait_callback (struct lwp_info *lp, void *data)
   if (!lp->stopped)
     {
       int status;
+      siginfo_t siginfo;
 
-      status = wait_lwp (lp);
+      status = wait_lwp (lp, &siginfo);
       if (status == 0)
 	return 0;
 
@@ -2660,9 +2735,6 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 	         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. */
@@ -2693,11 +2765,12 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 					"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));
+		  kill_lwp_siginfo (lp, WSTOPSIG (lp->status), &lp->siginfo);
 		}
 
 	      /* Save the sigtrap event. */
 	      lp->status = status;
+	      lp->siginfo = siginfo;
 	      return 0;
 	    }
 	  else
@@ -2737,10 +2810,13 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 					  target_pid_to_str (lp->ptid),
 					  status_to_str ((int) status));
 		    }
-		  kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
+		  kill_lwp_siginfo (lp, WSTOPSIG (status), &siginfo);
 		}
 	      else
-		lp->status = status;
+		{
+		  lp->status = status;
+		  lp->siginfo = siginfo;
+		}
 	      return 0;
 	    }
 	}
@@ -2893,8 +2969,11 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data)
       && lp->status != 0
       && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
       && cancel_breakpoint (lp))
-    /* Throw away the SIGTRAP.  */
-    lp->status = 0;
+    {
+      /* Throw away the SIGTRAP.  */
+      lp->status = 0;
+      memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+    }
 
   return 0;
 }
@@ -2902,7 +2981,8 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data)
 /* Select one LWP out of those that have events pending.  */
 
 static void
-select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
+select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status,
+		  siginfo_t *siginfop)
 {
   int num_events = 0;
   int random_selector;
@@ -2910,6 +2990,7 @@ select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
 
   /* Record the wait status for the original LWP.  */
   (*orig_lp)->status = *status;
+  (*orig_lp)->siginfo = *siginfop;
 
   /* Give preference to any LWP that is being single-stepped.  */
   event_lp = iterate_over_lwps (filter,
@@ -2948,10 +3029,12 @@ select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
       /* Switch the event LWP.  */
       *orig_lp = event_lp;
       *status = event_lp->status;
+      *siginfop = event_lp->siginfo;
     }
 
   /* Flush the wait status for the event LWP.  */
   (*orig_lp)->status = 0;
+  memset (&(*orig_lp)->siginfo, 0, sizeof ((*orig_lp)->siginfo));
 }
 
 /* Return non-zero if LP has been resumed.  */
@@ -2987,9 +3070,16 @@ stop_and_resume_callback (struct lwp_info *lp, void *data)
 /* Check if we should go on and pass this event to common code.
    Return the affected lwp if we are, or NULL otherwise.  */
 static struct lwp_info *
-linux_nat_filter_event (int lwpid, int status, int options)
+linux_nat_filter_event (int lwpid, int status, const siginfo_t *siginfop,
+			int options)
 {
   struct lwp_info *lp;
+  siginfo_t siginfo;
+
+  if (siginfop)
+    siginfo = *siginfop;
+  else
+    memset (&siginfo, 0, sizeof (siginfo));
 
   lp = find_lwp_pid (pid_to_ptid (lwpid));
 
@@ -3003,7 +3093,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
      from waitpid before or after the event is.  */
   if (WIFSTOPPED (status) && !lp)
     {
-      linux_record_stopped_pid (lwpid, status);
+      linux_record_stopped_pid (lwpid, status, &siginfo);
       return NULL;
     }
 
@@ -3049,6 +3139,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
 	 on handling the event like a regular SIGTRAP from here
 	 on.  */
       status = W_STOPCODE (SIGTRAP);
+      memset (&siginfo, 0, sizeof (siginfo));
       if (linux_handle_syscall_trap (lp, 0))
 	return NULL;
     }
@@ -3060,17 +3151,12 @@ linux_nat_filter_event (int lwpid, int status, int options)
 	fprintf_unfiltered (gdb_stdlog,
 			    "LLW: Handling extended status 0x%06x\n",
 			    status);
-      if (linux_handle_extended_wait (lp, status, 0))
+      if (linux_handle_extended_wait (lp, status, &siginfo, 0))
 	return NULL;
     }
 
   if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
-    {
-      /* Save the trap's siginfo in case we need it later.  */
-      save_siginfo (lp);
-
-      save_sigtrap (lp);
-    }
+    save_sigtrap (lp);
 
   /* Check if the thread has exited.  */
   if ((WIFEXITED (status) || WIFSIGNALED (status))
@@ -3194,6 +3280,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
   /* An interesting event.  */
   gdb_assert (lp);
   lp->status = status;
+  lp->siginfo = siginfo;
   return lp;
 }
 
@@ -3206,6 +3293,7 @@ linux_nat_wait_1 (struct target_ops *ops,
   struct lwp_info *lp = NULL;
   int options = 0;
   int status = 0;
+  siginfo_t siginfo = { 0 };
   pid_t pid;
 
   if (debug_linux_nat_async)
@@ -3242,6 +3330,7 @@ linux_nat_wait_1 (struct target_ops *ops,
 retry:
   lp = NULL;
   status = 0;
+  memset (&siginfo, 0, sizeof (siginfo));
 
   /* Make sure that of those LWPs we want to get an event from, there
      is at least one LWP that has been resumed.  If there's none, just
@@ -3334,7 +3423,9 @@ retry:
 
       /* Catch the pending SIGSTOP.  */
       status = lp->status;
+      siginfo = lp->siginfo;
       lp->status = 0;
+      memset (&lp->siginfo, 0, sizeof (lp->siginfo));
 
       stop_wait_callback (lp, NULL);
 
@@ -3348,10 +3439,11 @@ retry:
 				"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));
+	  kill_lwp_siginfo (lp, WSTOPSIG (lp->status), &lp->siginfo);
 	}
 
       lp->status = status;
+      lp->siginfo = siginfo;
     }
 
   if (!target_can_async_p ())
@@ -3368,7 +3460,7 @@ retry:
     {
       pid_t lwpid;
 
-      lwpid = my_waitpid (pid, &status, options);
+      lwpid = my_waitpid (pid, &status, &siginfo, options);
 
       if (lwpid > 0)
 	{
@@ -3381,7 +3473,7 @@ retry:
 				  (long) lwpid, status_to_str (status));
 	    }
 
-	  lp = linux_nat_filter_event (lwpid, status, options);
+	  lp = linux_nat_filter_event (lwpid, status, &siginfo, options);
 
 	  /* STATUS is now no longer valid, use LP->STATUS instead.  */
 	  status = 0;
@@ -3416,6 +3508,7 @@ retry:
 			{
 			  /* Throw away the SIGTRAP.  */
 			  lp->status = 0;
+			  memset (&lp->siginfo, 0, sizeof (lp->siginfo));
 
 			  if (debug_linux_nat)
 			    fprintf (stderr,
@@ -3456,7 +3549,8 @@ retry:
 
 		  /* Store the pending event in the waitstatus as
 		     well, because W_EXITCODE(0,0) == 0.  */
-		  store_waitstatus (&lp->waitstatus, lp->status);
+		  store_waitstatus (&lp->waitstatus, lp->status,
+				    &lp->siginfo);
 		}
 
 	      /* Keep looking.  */
@@ -3524,7 +3618,9 @@ retry:
   gdb_assert (lp);
 
   status = lp->status;
+  siginfo = lp->siginfo;
   lp->status = 0;
+  memset (&lp->siginfo, 0, sizeof (lp->siginfo));
 
   /* Don't report signals that GDB isn't interested in, such as
      signals that are neither printed nor stopped upon.  Stopping all
@@ -3535,9 +3631,12 @@ retry:
 
   if (WIFSTOPPED (status))
     {
-      target_signal_t signo = target_signal_from_host (WSTOPSIG (status));
+      target_signal_t signo;
       struct inferior *inf;
 
+      signo = target_signal_from_host (WSTOPSIG (status));
+      TARGET_SIGNAL_SIGINFO (signo) = siginfo;
+
       inf = find_inferior_pid (ptid_get_pid (lp->ptid));
       gdb_assert (inf);
 
@@ -3613,7 +3712,7 @@ retry:
 	 to all LWPs that have had events helps prevent
 	 starvation.  */
       if (pid == -1)
-	select_event_lwp (ptid, &lp, &status);
+	select_event_lwp (ptid, &lp, &status, &siginfo);
 
       /* Now that we've selected our final event LWP, cancel any
 	 breakpoints in other LWPs that have hit a GDB breakpoint.
@@ -3642,7 +3741,7 @@ retry:
       lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
     }
   else
-    store_waitstatus (ourstatus, status);
+    store_waitstatus (ourstatus, status, &siginfo);
 
   if (debug_linux_nat_async)
     fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
@@ -3770,7 +3869,7 @@ kill_wait_callback (struct lwp_info *lp, void *data)
     {
       do
 	{
-	  pid = my_waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
+	  pid = my_waitpid (GET_LWP (lp->ptid), NULL, NULL, __WCLONE);
 	  if (pid != (pid_t) -1)
 	    {
 	      if (debug_linux_nat)
@@ -3792,7 +3891,7 @@ kill_wait_callback (struct lwp_info *lp, void *data)
 
   do
     {
-      pid = my_waitpid (GET_LWP (lp->ptid), NULL, 0);
+      pid = my_waitpid (GET_LWP (lp->ptid), NULL, NULL, 0);
       if (pid != (pid_t) -1)
 	{
 	  if (debug_linux_nat)
@@ -5447,7 +5546,10 @@ linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
 	 event-loop will end up calling target_wait which will collect
 	 these.  */
       if (lwp->status == 0)
-	lwp->status = W_STOPCODE (0);
+	{
+	  lwp->status = W_STOPCODE (0);
+	  memset (&lwp->siginfo, 0, sizeof (lwp->siginfo));
+	}
       async_file_mark ();
     }
   else
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -52,16 +52,15 @@ struct lwp_info
      didn't try to let the LWP run.  */
   int resumed;
 
-  /* If non-zero, a pending wait status.  */
+  /* If non-zero, a pending wait status.  SIGINFO contains extended
+     information for WIFSTOPPED and WIFSIGNALLED cases of STATUS.  For example
+     SIGINFO.SI_ADDR may get used as the address of a hardware watchpoint.  */
   int status;
+  struct siginfo siginfo;
 
   /* Non-zero if we were stepping this LWP.  */
   int step;
 
-  /* Non-zero si_signo if this LWP stopped with a trap.  si_addr may
-     be the address of a hardware watchpoint.  */
-  struct siginfo siginfo;
-
   /* STOPPED_BY_WATCHPOINT is non-zero if this LWP stopped with a data
      watchpoint trap.  */
   int stopped_by_watchpoint;
--- a/gdb/procfs.c
+++ b/gdb/procfs.c
@@ -4209,7 +4209,7 @@ wait_again:
 	}
 
       if (status)
-	store_waitstatus (status, wstat);
+	store_waitstatus (status, wstat, NULL);
     }
 
   return retval;
--- a/gdb/rs6000-nat.c
+++ b/gdb/rs6000-nat.c
@@ -118,6 +118,10 @@ typedef union {
 #define LDI_FD(ldi, arch64)		LDI_FIELD(ldi, arch64, fd)
 #define LDI_FILENAME(ldi, arch64)	LDI_FIELD(ldi, arch64, filename)
 
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO	0x4202
+#endif
+
 extern struct vmap *map_vmap (bfd * bf, bfd * arch);
 
 static void vmap_exec (void);
@@ -567,7 +571,17 @@ rs6000_wait (struct target_ops *ops,
     ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
   /* A normal waitstatus.  Let the usual macros deal with it.  */
   else
-    store_waitstatus (ourstatus, status);
+    {
+      siginfo_t siginfo;
+
+      /* We could also use waitid.  */
+      if (! (pid > 0 && (WIFSIGNALED (status) || WIFSTOPPED (status)))
+	  || (ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo)
+	      == -1))
+	store_waitstatus (ourstatus, status, NULL);
+      else
+	store_waitstatus (ourstatus, status, &siginfo);
+    }
 
   return pid_to_ptid (pid);
 }
--- a/gdb/spu-linux-nat.c
+++ b/gdb/spu-linux-nat.c
@@ -40,6 +40,9 @@
 #define INSTR_SC	0x44000002
 #define NR_spu_run	0x0116
 
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO	0x4202
+#endif
 
 /* Fetch PPU register REGNO.  */
 static ULONGEST
@@ -429,6 +432,7 @@ spu_child_wait (struct target_ops *ops,
   int save_errno;
   int status;
   pid_t pid;
+  siginfo_t siginfo;
 
   do
     {
@@ -465,7 +469,13 @@ spu_child_wait (struct target_ops *ops,
       return inferior_ptid;
     }
 
-  store_waitstatus (ourstatus, status);
+  if (! (ret > 0 && (WIFSIGNALED (status) || WIFSTOPPED (status)))
+      || (ptrace (PTRACE_GETSIGINFO, ret, (PTRACE_TYPE_ARG3) 0, &siginfo)
+	  == -1))
+    store_waitstatus (ourstatus, status, NULL);
+  else
+    store_waitstatus (ourstatus, status, &siginfo);
+
   return pid_to_ptid (pid);
 }
 
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2835,7 +2835,8 @@ generic_mourn_inferior (void)
    HOSTSTATUS is the waitstatus from wait() or the equivalent; store our
    translation of that in OURSTATUS.  */
 void
-store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
+store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus,
+		  const siginfo_t *siginfop)
 {
   if (WIFEXITED (hoststatus))
     {
@@ -2846,11 +2847,15 @@ store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
     {
       ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
       ourstatus->value.sig = target_signal_from_host (WTERMSIG (hoststatus));
+      if (siginfop)
+	TARGET_SIGNAL_SIGINFO (ourstatus->value.sig) = *siginfop;
     }
   else
     {
       ourstatus->kind = TARGET_WAITKIND_STOPPED;
       ourstatus->value.sig = target_signal_from_host (WSTOPSIG (hoststatus));
+      if (siginfop)
+	TARGET_SIGNAL_SIGINFO (ourstatus->value.sig) = *siginfop;
     }
 }
 
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1573,7 +1573,8 @@ extern int remote_timeout;
 /* Functions for helping to write a native target.  */
 
 /* This is for native targets which use a unix/POSIX-style waitstatus.  */
-extern void store_waitstatus (struct target_waitstatus *, int);
+extern void store_waitstatus (struct target_waitstatus *, int,
+			      const siginfo_t *);
 
 /* These are in common/signals.c, but they're only used by gdb.  */
 extern target_signal_t default_target_signal_from_host (struct gdbarch *,
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/siginfo-threads.c
@@ -0,0 +1,447 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010 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 up us 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 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 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 gets unstopped later.
+     Threads get resumed at 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 get 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;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/siginfo-threads.exp
@@ -0,0 +1,37 @@
+# Copyright 2010 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/>.
+
+set testfile "siginfo-threads"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${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.
+
+gdb_test "handle SIGUSR1 nostop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+User defined signal 1"
+gdb_test "handle SIGUSR2 nostop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR2\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+User defined signal 2"
+
+gdb_breakpoint [gdb_get_line_number "break-at-exit"]
+
+gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*"
--- a/include/gdb/signals.h
+++ b/include/gdb/signals.h
@@ -21,6 +21,7 @@
 #ifndef GDB_SIGNALS_H
 #define GDB_SIGNALS_H
 
+#include <signal.h>
 #include "ansidecl.h"
 
 /* The numbering of these signals is chosen to match traditional unix
@@ -68,6 +69,10 @@ enum target_signal_number
 typedef struct
   {
     enum target_signal_number number;
+
+    /* Removing the padding could save 80 out of the 128 bytes of siginfo_t.
+       Currently the largest union field is sifields._sigchld there.  */
+    siginfo_t siginfo;
   }
 target_signal_t;
 
@@ -80,6 +85,7 @@ target_signal_t;
 #undef SET
 
 #define TARGET_SIGNAL_NUMBER(target_signal) (target_signal).number
+#define TARGET_SIGNAL_SIGINFO(target_signal) (target_signal).siginfo
 
 static inline target_signal_t
 target_signal_from_number (enum target_signal_number number)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]