This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC 5/6] Associate siginfo_t with any signal
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Tue, 27 Jul 2010 00:51:22 +0200
- Subject: [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)