[gdb/testsuite] Fix failure in gdb.threads/signal-sigtrap.exp
The test-case gdb.threads/signal-sigtrap.exp:
- installs a signal handler called sigtrap_handler for SIGTRAP,
- sets a breakpoint on sigtrap_handler, and
- expects the breakpoint to trigger after issuing "signal SIGTRAP".
Usually, that happens indeed:
...
(gdb) signal SIGTRAP^M
Continuing with signal SIGTRAP.^M
^M
Thread 1 "signal-sigtrap" hit Breakpoint 2, sigtrap_handler (sig=5)^M
28 }^M
(gdb) PASS: $exp: sigtrap thread 1: signal SIGTRAP reaches handler
...
Occasionally, I run into this failure on openSUSE Tumbleweed:
...
(gdb) signal SIGTRAP^M
Continuing with signal SIGTRAP.^M
^M
Thread 1 "signal-sigtrap" received signal SIGTRAP, Trace/breakpoint trap.^M
__pthread_create_2_1 () at pthread_create.c:843^M
(gdb) FAIL: $exp: sigtrap thread 1: signal SIGTRAP reaches handler
...
AFAIU, the problem is in the situation that is setup before issuing that
command, by running to a breakpoint in thread_function:
...
void *thread_function (void *arg) {
return NULL;
}
int main (void) {
pthread_t child_thread;
signal (SIGTRAP, sigtrap_handler);
pthread_create (&child_thread, NULL, thread_function, NULL);
pthread_join (child_thread, NULL);
return 0;
}
...
In the passing case, thread 2 is stopped in thread_function, and thread 1 is
stopped somewhere in pthread_join:
...
(gdb) info threads^M
Id Target Id Frame ^M
1 Thread ... (LWP ...) "signal-sigtrap" __futex_abstimed_wait_common64 ()
* 2 Thread ... (LWP ...) "signal-sigtrap" thread_function ()
...
In the failing case, thread 2 is stopped in thread_function, but thread 1 is
stopped somewhere in pthread_create:
...
(gdb) info threads^M
Id Target Id Frame ^M
1 Thread ... (LWP ...) "signal-sigtrap" __GI___clone3 ()
* 2 Thread ... (LWP ...) "signal-sigtrap" thread_function ()
...
What I think happens is that pthread_create blocks SIGTRAP at some point, and
if the "signal SIGTRAP" command is issued while that is the case, the signal
becomes pending and consequently there's no longer a guarantee that the signal
will be delivered to the inferior.
Instead the signal will be handled by gdb like this:
...
(gdb) info signals SIGTRAP
Signal Stop Print Pass to program Description
SIGTRAP Yes Yes No Trace/breakpoint trap
...
Fix this by adding a barrier that ensures that pthread_create is done before
we issue the "signal SIGTRAP" command.
Likewise in test-case gdb.threads/signal-command-handle-nopass.exp.
Using the fixed test-case, I tested my theory by explicitly blocking SIGTRAP:
...
+ sigset_t old_ss, new_ss;
+ sigemptyset (&new_ss);
+ sigaddset (&new_ss, SIGTRAP);
+ sigprocmask (SIG_BLOCK, &new_ss, &old_ss);
+
/* Make sure that pthread_create is done once the breakpoint on
thread_function triggers. */
pthread_barrier_wait (&barrier);
pthread_join (child_thread, NULL);
+ sigprocmask (SIG_SETMASK, &old_ss, NULL);
...
and managed to reproduce the same failure:
...
(gdb) signal SIGTRAP^M
Continuing with signal SIGTRAP.^M
[Thread 0x7ffff7c00700 (LWP 13254) exited]^M
^M
Thread 1 "signal-sigtrap" received signal SIGTRAP, Trace/breakpoint trap.^M
0x00007ffff7c80056 in __GI___sigprocmask () sigprocmask.c:39^M
(gdb) FAIL: $exp: sigtrap thread 1: signal SIGTRAP reaches handler
...
Tested on x86_64-linux.
PR testsuite/26867
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=26867
diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
index 6d82bd6..548d0d3 100644
--- a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
+++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
@@ -21,6 +21,8 @@
#include <pthread.h>
#include <signal.h>
+static pthread_barrier_t barrier;
+
void
handler (int sig)
{
@@ -35,6 +37,13 @@ thread_function (void *arg)
usleep (1);
}
+void *
+thread_function_sync (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+ return thread_function (arg);
+}
+
int
main (void)
{
@@ -42,7 +51,15 @@ main (void)
int i;
signal (SIGUSR1, handler);
- pthread_create (&child_thread, NULL, thread_function, NULL);
+
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ pthread_create (&child_thread, NULL, thread_function_sync, NULL);
+
+ /* Make sure that pthread_create is done once the breakpoint on
+ thread_function triggers. */
+ pthread_barrier_wait (&barrier);
+
pthread_join (child_thread, NULL);
return 0;
diff --git a/gdb/testsuite/gdb.threads/signal-sigtrap.c b/gdb/testsuite/gdb.threads/signal-sigtrap.c
index 24625ba..7c903a1 100644
--- a/gdb/testsuite/gdb.threads/signal-sigtrap.c
+++ b/gdb/testsuite/gdb.threads/signal-sigtrap.c
@@ -20,6 +20,8 @@
#include <pthread.h>
#include <signal.h>
+static pthread_barrier_t barrier;
+
void
sigtrap_handler (int sig)
{
@@ -31,6 +33,13 @@ thread_function (void *arg)
return NULL;
}
+void *
+thread_function_sync (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+ return thread_function (arg);
+}
+
int
main (void)
{
@@ -38,7 +47,13 @@ main (void)
signal (SIGTRAP, sigtrap_handler);
- pthread_create (&child_thread, NULL, thread_function, NULL);
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ pthread_create (&child_thread, NULL, thread_function_sync, NULL);
+
+ /* Make sure that pthread_create is done once the breakpoint on
+ thread_function triggers. */
+ pthread_barrier_wait (&barrier);
pthread_join (child_thread, NULL);