blob: 98d99388a7a6561fce4601dd8f42a60566250e4f [file] [log] [blame]
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#include "util.h"
static int child_to_parent[2];
static void* run_child_thread(__attribute__((unused)) void* p) {
setpriority(PRIO_PROCESS, 0, 10);
atomic_printf("off-main-thread id = %d\n", sys_gettid());
write(child_to_parent[1], "x", 1);
pause();
return NULL;
}
static int do_child(void) {
pthread_t thread;
pthread_create(&thread, NULL, run_child_thread, NULL);
pause();
return 66;
}
/* Do a busy delay loop that changes registers so won't trigger
rr's spinlock-detection heuristic */
static char delay(void) {
#if defined(__x86_64__) || defined(__i386__)
asm("mov $10000000,%%ecx\n\t"
"1: loop 1b\n\t"
: : : "ecx");
return 0;
#else
/* Does this actually change registers on ARM??? */
int i;
static volatile char ch;
for (i = 0; i < 10000000; ++i) {
ch = i % 3;
}
return ch;
#endif
}
int main(int argc, __attribute__((unused)) char** argv) {
pid_t child;
pid_t exec_child;
int status;
char cc;
char* execv_argv[] = {"/proc/self/exe", "dummy", NULL};
if (argc > 1) {
return 99;
}
pipe(child_to_parent);
child = fork();
if (!child) {
return do_child();
}
read(child_to_parent[0], &cc, 1);
atomic_printf("sending SIGTERM to %d\n", child);
kill(child, SIGTERM);
/* Delay a bit. During this delay the child's main thread
will handle SIGTERM, rr will schedule it and process the SIGTERM,
triggering a thread-group exit; the child main thread will proceed
to its PTRACE_EVENT_EXIT stop, rr will process that too, and the
child main thread will complete exit and go into zombie state.
rr will not schedule the child off-main thread since it has lower priority,
so that thread will advance to its PTRACE_EVENT_EXIT stop and wait there. */
delay();
atomic_printf("sending SIGKILL to %d\n", child);
kill(child, SIGKILL);
/* Delay a bit more. During this delay the child's off-main thread will
respond to the SIGKILL by exiting its PTRACE_EVENT_EXIT stop and going into
zombie state. rr still won't schedule that thread and doesn't know anything
about it exiting. */
delay();
atomic_printf("Sleeping...\n");
/* Do an execve and let the other task run during that */
exec_child = fork();
if (exec_child == 0) {
execve("/proc/self/exe", execv_argv, NULL);
}
test_assert(exec_child == waitpid(exec_child, &status, 0));
test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 99);
atomic_puts("EXIT-SUCCESS");
return 0;
}