mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 16:36:21 +00:00
152 lines
4.3 KiB
C
152 lines
4.3 KiB
C
// This tests handling of signals sent from outside the process in the
|
|
// following combinations: sync and async signals, caught and uncaught
|
|
// signals, and while blocking or not blocking in a syscall. This exercises
|
|
// various different paths in Valgrind's signal handling.
|
|
//
|
|
// It does this by installing signal handlers for one signal S, spawning
|
|
// another process P, sending S from P multiple times (all caught), then
|
|
// sending another signal from P (not caught).
|
|
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
|
|
static const struct timespec bip = { 0, 1000000000 / 5 }; // 0.2 seconds.
|
|
|
|
static void handler(int sig)
|
|
{
|
|
}
|
|
|
|
static void install_handler(int sig, void (*sig_handler)(int))
|
|
{
|
|
struct sigaction sa;
|
|
sa.sa_handler = sig_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sigaction(sig, &sa, 0);
|
|
}
|
|
|
|
/* Kill our child, but use a separate kill command. This is so that
|
|
it's running independently of Valgrind, and so is async with
|
|
respect to thread scheduling. */
|
|
static void do_kill(int pid, int sig)
|
|
{
|
|
int status;
|
|
int killer;
|
|
int ret;
|
|
|
|
killer = vfork();
|
|
if (killer == -1) {
|
|
perror("killer/vfork");
|
|
exit(1);
|
|
}
|
|
|
|
// In the child, exec 'kill' in order to send the signal.
|
|
if (killer == 0) {
|
|
char sigbuf[20];
|
|
char pidbuf[20];
|
|
sprintf(sigbuf, "-%d", sig);
|
|
sprintf(pidbuf, "%d", pid);
|
|
execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL);
|
|
perror("exec failed");
|
|
exit(1);
|
|
}
|
|
|
|
// In the parent, just wait for the child and then check it ran ok.
|
|
do
|
|
ret = waitpid(killer, &status, 0);
|
|
while (ret == -1 && errno == EINTR);
|
|
|
|
if (ret != killer) {
|
|
perror("kill/waitpid");
|
|
exit(1);
|
|
}
|
|
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
|
fprintf(stderr, "kill %d failed status=%s %d\n", killer,
|
|
WIFEXITED(status) ? "exit" : "signal",
|
|
WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void test(int block, int caughtsig, int fatalsig)
|
|
{
|
|
int pid;
|
|
int status;
|
|
int i;
|
|
|
|
fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ",
|
|
block, caughtsig, fatalsig);
|
|
|
|
pid = fork();
|
|
if (pid == -1) {
|
|
perror("fork");
|
|
exit(1);
|
|
}
|
|
|
|
// In the child, install the signal handler, then wait for the signal to
|
|
// arrive:
|
|
// - if 'block' is set, wait on a system call;
|
|
// - otherwise, wait in client code (by spinning).
|
|
// The alarm() calls is so that if something breaks, we don't get stuck.
|
|
if (pid == 0) {
|
|
install_handler(caughtsig, handler);
|
|
alarm(10);
|
|
|
|
for (;;)
|
|
if (block) {
|
|
pause();
|
|
}
|
|
}
|
|
|
|
// In the parent, send the signals.
|
|
nanosleep(&bip, 0); // Wait for child to get going.
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
do_kill(pid, caughtsig); // Should be caught.
|
|
nanosleep(&bip, 0);
|
|
do_kill(pid, caughtsig); // Ditto.
|
|
do_kill(pid, caughtsig); // Ditto.
|
|
}
|
|
|
|
nanosleep(&bip, 0);
|
|
|
|
do_kill(pid, fatalsig); // Should kill it.
|
|
|
|
// Check that the child behaved as expected when it received the signals.
|
|
if (waitpid(pid, &status, 0) != pid) {
|
|
fprintf(stderr, "FAILED: waitpid failed: %s\n", strerror(errno));
|
|
|
|
} else if (!WIFSIGNALED(status) || WTERMSIG(status) != fatalsig) {
|
|
fprintf(stderr, "FAILED: child exited with unexpected status %s %d\n",
|
|
WIFEXITED(status) ? "exit" : "signal",
|
|
WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
|
|
|
|
} else {
|
|
fprintf(stderr, "PASSED\n");
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
/* Restore default behaviour of SIGHUP when forked from cron. */
|
|
install_handler(SIGHUP, SIG_DFL);
|
|
|
|
test(/*non-blocked*/0, /* sync*/SIGSEGV, /* sync*/SIGBUS);
|
|
test(/*non-blocked*/0, /* sync*/SIGSEGV, /*async*/SIGHUP);
|
|
test(/*non-blocked*/0, /*async*/SIGUSR1, /* sync*/SIGBUS);
|
|
test(/*non-blocked*/0, /*async*/SIGUSR1, /*async*/SIGHUP);
|
|
test(/* blocked*/1, /* sync*/SIGSEGV, /* sync*/SIGBUS);
|
|
test(/* blocked*/1, /* sync*/SIGSEGV, /*async*/SIGHUP);
|
|
test(/* blocked*/1, /*async*/SIGUSR1, /* sync*/SIGBUS);
|
|
test(/* blocked*/1, /*async*/SIGUSR1, /*async*/SIGHUP);
|
|
|
|
return 0;
|
|
}
|