mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
cdff184f42
If we make GDB report the process EXIT event for the leader thread, instead of whatever is the last thread in the LWP list, as will be done in a latter patch of this series, then gdb.threads/current-lwp-dead.exp starts failing: (gdb) FAIL: gdb.threads/clone-new-thread-event.exp: catch SIGUSR1 (the program exited) This is a testcase race -- the main thread does not wait for the spawned clone "thread" to finish before exiting, so the main program may exit before the second thread is scheduled and reports its SIGUSR1. With the change to make GDB report the EXIT for the leader, the race is 100% reproducible by adding a sleep(), like so: --- c/gdb/testsuite/gdb.threads/clone-new-thread-event.c +++ w/gdb/testsuite/gdb.threads/clone-new-thread-event.c @@ -51,6 +51,7 @@ local_gettid (void) static int fn (void *unused) { + sleep (1); tkill (local_gettid (), SIGUSR1); return 0; } Resulting in: Breakpoint 1, main (argc=1, argv=0x7fffffffd418) at gdb.threads/clone-new-thread-event.c:65 65 stack = malloc (STACK_SIZE); (gdb) continue Continuing. [New LWP 3715562] [Inferior 1 (process 3715555) exited normally] (gdb) FAIL: gdb.threads/clone-new-thread-event.exp: catch SIGUSR1 (the program exited) That inferior exit reported is actually correct. The main thread has indeed exited, and that's the thread that has the right exit code to report to the user, as that's the exit code that is reported to the program's parent. In this case, GDB managed to collect the exit code for the leader thread before reaping the other thread, because in reality, the testcase isn't creating standard threads, it is using raw clone, and the new clones are put in their own thread group. Fix it by making the main thread wait for the child to exit. Also, run the program to completion for completeness. Change-Id: I315cd3dc2b9e860395dcab9658341ea868d7a6bf
90 lines
2.5 KiB
C
90 lines
2.5 KiB
C
/* This testcase is part of GDB, the GNU debugger.
|
|
|
|
Copyright 2009-2022 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/>.
|
|
|
|
Test that GDB doesn't lose an event for a thread it didn't know
|
|
about, until an event is reported for it. */
|
|
|
|
#define _GNU_SOURCE
|
|
#include <sched.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <features.h>
|
|
#ifdef __UCLIBC__
|
|
#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
|
|
#define HAS_NOMMU
|
|
#endif
|
|
#endif
|
|
|
|
#define STACK_SIZE 0x1000
|
|
|
|
static int
|
|
tkill (int lwpid, int signo)
|
|
{
|
|
return syscall (__NR_tkill, lwpid, signo);
|
|
}
|
|
|
|
static pid_t
|
|
local_gettid (void)
|
|
{
|
|
return syscall (__NR_gettid);
|
|
}
|
|
|
|
static int
|
|
fn (void *unused)
|
|
{
|
|
tkill (local_gettid (), SIGUSR1);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
unsigned char *stack;
|
|
int new_pid, status, ret;
|
|
|
|
stack = malloc (STACK_SIZE);
|
|
assert (stack != NULL);
|
|
|
|
new_pid = clone (fn, stack + STACK_SIZE, CLONE_FILES
|
|
#if defined(__UCLIBC__) && defined(HAS_NOMMU)
|
|
| CLONE_VM
|
|
#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
|
|
, NULL, NULL, NULL, NULL);
|
|
assert (new_pid > 0);
|
|
|
|
/* Note the clone call above didn't use CLONE_THREAD, so it actually
|
|
put the new thread in a new thread group. However, the new clone
|
|
is still reported with PTRACE_EVENT_CLONE to GDB, since we didn't
|
|
use CLONE_VFORK (results in PTRACE_EVENT_VFORK) nor set the
|
|
termination signal to SIGCHLD (results in PTRACE_EVENT_FORK), so
|
|
GDB thinks of it as a new thread of the same inferior. It's a
|
|
bit of an odd setup, but it's not important for what we're
|
|
testing, and, it let's us conveniently use waitpid to wait for
|
|
the clone, which you can't with CLONE_THREAD. */
|
|
ret = waitpid (new_pid, &status, __WALL);
|
|
assert (ret == new_pid);
|
|
assert (WIFSIGNALED (status) && WTERMSIG (status) == SIGUSR1);
|
|
|
|
return 0;
|
|
}
|