[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
This commit is contained in:
Tom de Vries 2024-09-23 09:53:54 +02:00
parent bee9d00688
commit dca18cb6a1
2 changed files with 34 additions and 2 deletions

View File

@ -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;

View File

@ -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);