* test/many_looping_threads.c: Remove.
* test/.gitignore: Remove many_looping_threads.
* test/Makefile (PROGS): Likewise.
(many_looping_threads): Remove.
* tests/looping_threads.c: New file.
* tests/looping_threads.test: New test.
* tests/.gitignore: Add looping_threads.
* tests/Makefile.am (check_PROGRAMS): Likewise.
(looping_threads_LDADD): New variable.
(MISC_TESTS, XFAIL_TESTS): Add looping_threads.test.
childthread
clone
leaderkill
-many_looping_threads
mmap_offset_decode
mtd
seccomp
PROGS = \
sig skodic clone leaderkill childthread \
sigkill_rain wait_must_be_interruptible threaded_execve \
- mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap \
- many_looping_threads
+ mtd ubi seccomp sfd mmap_offset_decode x32_lseek x32_mmap
all: $(PROGS)
childthread: LDFLAGS += -pthread
-many_looping_threads: LDFLAGS += -pthread
-
clean distclean:
rm -f *.o core $(PROGS) *.gdb
+++ /dev/null
-// This testcase, when run with large number of threads
-// under stace -f, may never finish because strace does not
-// ensure any fairness in thread scheduling:
-// it restarts threads as they stop. If daughter threads crowd out
-// the "mother" and _they_ get continually restarted by strace,
-// the end of spawning loop will never be reached.
-//
-// Also, it is a testcase which triggers the
-// "strace: Exit of unknown pid 32457 seen"
-// message when on testcase exit, strace sees deaths of newly-attached
-// threads _before_ their first syscall stop.
-//
-#include <stdio.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <stdlib.h>
-
-static int thd_no;
-
-static void *sub_thd(void *c)
-{
- dprintf(1, "sub-thread %d created\n", ++thd_no);
- for (;;)
- getuid();
- return NULL;
-}
-
-int main(int argc, char *argv[])
-{
- int i;
- pthread_t *thd;
- int num_threads = 1;
-
- if (argv[1])
- num_threads = atoi(argv[1]);
-
- thd = malloc(num_threads * sizeof(thd[0]));
- dprintf(1, "test start, num_threads:%d...\n", num_threads);
-
- for (i = 0; i < num_threads; i++) {
- pthread_create(&thd[i], NULL, sub_thd, NULL);
- dprintf(1, "after pthread_create\n");
- }
-
- /* Exit. This kills all threads */
- return 0;
-}
llseek
localtime
lookup_dcookie
+looping_threads
lseek
lstat
lstat64
ksysent \
list_sigaction_signum \
localtime \
+ looping_threads \
mmsg-silent \
mmsg_name-v \
msg_control-v \
fstatat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
ftruncate64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
localtime_LDADD = $(clock_LIBS) $(LDADD)
+looping_threads_LDADD = -lpthread $(LDADD)
lstat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
mmap64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
mmap64_Xabbrev_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64
kill_child.test \
ksysent.test \
localtime.test \
+ looping_threads.test \
opipe.test \
options-syntax.test \
pc.test \
XFAIL_TESTS_ =
XFAIL_TESTS_m32 = $(STACKTRACE_TESTS)
XFAIL_TESTS_mx32 = $(STACKTRACE_TESTS)
-XFAIL_TESTS = $(XFAIL_TESTS_$(MPERS_NAME)) $(XFAIL_TESTS_$(ARCH))
+XFAIL_TESTS = $(XFAIL_TESTS_$(MPERS_NAME)) $(XFAIL_TESTS_$(ARCH)) \
+ looping_threads.test
TEST_LOG_COMPILER = env
AM_TEST_LOG_FLAGS = STRACE_ARCH=$(ARCH) STRACE_NATIVE_ARCH=$(NATIVE_ARCH) \
--- /dev/null
+/*
+ * Check tracing of looping threads.
+ *
+ * Copyright (c) 2009-2019 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "tests.h"
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+static void *
+thread(void *arg)
+{
+ for (;;)
+ getuid();
+ return arg;
+}
+
+int
+main(int ac, const char *av[])
+{
+ assert(ac == 3);
+
+ int timeout = atoi(av[1]);
+ assert(timeout > 0);
+
+ int num_threads = atoi(av[2]);
+ assert(num_threads > 0);
+
+ /*
+ * Unblock all signals.
+ */
+ static sigset_t mask;
+ if (sigprocmask(SIG_SETMASK, &mask, NULL))
+ perror_msg_and_fail("sigprocmask");
+
+ /*
+ * Reset SIGALRM and SIGHUP signal handlers.
+ */
+ static const struct sigaction sa_def = { .sa_handler = SIG_DFL };
+ if (sigaction(SIGHUP, &sa_def, NULL))
+ perror_msg_and_fail("sigaction SIGHUP");
+ if (sigaction(SIGALRM, &sa_def, NULL))
+ perror_msg_and_fail("sigaction SIGALRM");
+
+ /*
+ * Create a new process group.
+ */
+ if (setpgid(0, 0))
+ perror_msg_and_fail("setpgid");
+
+ /*
+ * Set an alarm clock.
+ */
+ alarm(timeout);
+
+ /*
+ * When the main process terminates, the process group becomes orphaned.
+ * If any member of the orphaned process group is stopped, then
+ * a SIGHUP signal followed by a SIGCONT signal is sent to each process
+ * in the orphaned process group.
+ * Create a process in a stopped state to activate this behaviour.
+ */
+ const pid_t stopped = fork();
+ if (stopped < 0)
+ perror_msg_and_fail("fork");
+ if (!stopped) {
+ raise(SIGSTOP);
+ _exit(0);
+ }
+
+ /*
+ * Wait for the process to stop.
+ */
+ int status;
+ if (waitpid(stopped, &status, WUNTRACED) != stopped)
+ perror_msg_and_fail("waitpid WUNTRACED");
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ error_msg_and_fail("waitpid WUNTRACED: "
+ "unexpected wait status %d", status);
+ /*
+ * Create all threads in a subprocess, this guarantees that
+ * their tracer will not be their parent.
+ */
+ pid_t pid = fork();
+ if (pid < 0)
+ perror_msg_and_fail("fork");
+ if (!pid) {
+ for (int i = 0; i < num_threads; i++) {
+ pthread_t t;
+ if ((errno = pthread_create(&t, NULL, thread, NULL))) {
+ if (EAGAIN == errno)
+ break;
+ perror_msg_and_fail("pthread_create #%d", i);
+ }
+ }
+
+ /* This terminates all threads created above. */
+ _exit(0);
+ }
+
+ if (waitpid(pid, &status, 0) != pid)
+ perror_msg_and_fail("waitpid");
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ error_msg_and_fail("waitpid: unexpected wait status %d",
+ status);
+
+ /*
+ * Make the process group orphaned.
+ */
+ return 0;
+}
--- /dev/null
+#!/bin/sh
+#
+# Check tracing of looping threads.
+#
+# Copyright (c) 2009-2019 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+. "${srcdir=.}/init.sh"
+. "${srcdir=.}/PTRACE_SEIZE.sh"
+
+run_prog ../orphaned_process_group > /dev/null
+
+run_prog_skip_if_failed date +%s > /dev/null
+s0="$(date +%s)"
+
+check_prog nproc
+inc="$(nproc)"
+[ "$inc" -ge 1 ] || inc=1
+
+timeout_2="$(($TIMEOUT_DURATION/2))"
+timeout_8="$(($TIMEOUT_DURATION/8))"
+nproc=1
+
+run_prog "../$NAME" "$timeout_8" "$nproc"
+
+while :; do
+ run_strace -f -qq -enone -esignal=none "../$NAME" "$timeout_2" "$nproc"
+
+ s1="$(date +%s)"
+ [ "$(($s1-$s0))" -lt "$timeout_8" ] ||
+ break
+
+ nproc="$(($nproc+$inc))"
+done
+
+warn_ "$ME_: nproc=$nproc elapsed=$(($s1-$s0))"