From dca18cb6a100fa87d6478a465e48ddc1d9ed943a Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Mon, 23 Sep 2024 09:53:54 +0200 Subject: [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 --- .../gdb.threads/signal-command-handle-nopass.c | 19 ++++++++++++++++++- gdb/testsuite/gdb.threads/signal-sigtrap.c | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) 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 #include +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 #include +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); -- cgit v1.1