public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC 5/6] Associate siginfo_t with any signal
@ 2010-07-26 22:52 Jan Kratochvil
  2010-07-27 14:50 ` Daniel Jacobowitz
  2010-08-30  7:21 ` obsoleted: " Jan Kratochvil
  0 siblings, 2 replies; 3+ messages in thread
From: Jan Kratochvil @ 2010-07-26 22:52 UTC (permalink / raw)
  To: gdb-patches

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;
 \f
@@ -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 @@
 
 \f
 
+#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);
 }
 
 \f
@@ -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;
     }
 }
 \f
--- 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)

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

* Re: [RFC 5/6] Associate siginfo_t with any signal
  2010-07-26 22:52 [RFC 5/6] Associate siginfo_t with any signal Jan Kratochvil
@ 2010-07-27 14:50 ` Daniel Jacobowitz
  2010-08-30  7:21 ` obsoleted: " Jan Kratochvil
  1 sibling, 0 replies; 3+ messages in thread
From: Daniel Jacobowitz @ 2010-07-27 14:50 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

On Tue, Jul 27, 2010 at 12:51:22AM +0200, Jan Kratochvil wrote:
> 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.

Mark K. is right.  Host files like signal.h should not be included in
most of GDB, to prevent people from accidentally using the native
types.  There's no promise that a native siginfo_t has any resemblence
to a target siginfo_t.

-- 
Daniel Jacobowitz
CodeSourcery

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

* obsoleted: Re: [RFC 5/6] Associate siginfo_t with any signal
  2010-07-26 22:52 [RFC 5/6] Associate siginfo_t with any signal Jan Kratochvil
  2010-07-27 14:50 ` Daniel Jacobowitz
@ 2010-08-30  7:21 ` Jan Kratochvil
  1 sibling, 0 replies; 3+ messages in thread
From: Jan Kratochvil @ 2010-08-30  7:21 UTC (permalink / raw)
  To: gdb-patches

[rfc 7/9]#2 Associate siginfo_t with any signal
http://sourceware.org/ml/gdb-patches/2010-08/msg00487.html

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

end of thread, other threads:[~2010-08-30  7:21 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-26 22:52 [RFC 5/6] Associate siginfo_t with any signal Jan Kratochvil
2010-07-27 14:50 ` Daniel Jacobowitz
2010-08-30  7:21 ` obsoleted: " Jan Kratochvil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).