/* * Copyright 2002-2019 Intel Corporation. * * This software is provided to you as Sample Source Code as defined in the accompanying * End User License Agreement for the Intel(R) Software Development Products ("Agreement") * section 1.L. * * This software and the related documents are provided as is, with no express or implied * warranties, other than those that are expressly stated in the License. */ /* * This test application creates 2 processes: * Parent process: Just to monitor whether child process got into deadlock or not. * Child process: create 2 threads: * Thread A (main thread): * 1. Set a timer to signal this thread periodically. * 2. Call in a loop to DoToolAnalysis function that will be used as a slot to insert analysis func * Thread B * 1. Call the sched_yield() system call in a loop. * * The tool will use PIN_AddSyscallEntryFunction(OnSyscall, 0) to add instumentation func that holds and releases local lock (on Thread B) * It will also add an instrumentation func on thread A (before DoToolAnalysis) that will get the same local lock and do a long busy loop before releasing the lock. * * The expected scenario is that: * 1. Thread A will take the local lock and wait on long busy loop * 2. Thread B will wakeup and will take VM lock (due to the sys call) * 3. Thraed B will try to take local lock (call-back) and fail since thread A holds the lock. * 4. An async timer signal on thread A will cause it to get VM lock ---DEADLOCK--- */ #include #include #include #include #include #include #include #include #include #include #define COUNT 10 #define USLEEP_TIME 25000 #define DEADLOCK_TIMEOUT 10 typedef void (*PF)(); volatile unsigned SigCount = 0; static void Handle(int); static void TimeoutHandler(int); static void *DoSysCallTillSignalsDone(void *); void DoToolAnalysis(); void sig_hand(int); int main() { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) { fprintf(stderr, "Unable to mask SIGCHLD"); return 1; } signal(SIGCHLD, sig_hand); int fd[2]; if (pipe(fd) == -1){ fprintf(stderr, "Unable to open pipe "); return 1; } pid_t child = fork(); if (child) { // Parent proc close(fd[1]); // Close unused write end struct timespec timeout; timeout.tv_sec = 1200; //20 mins timeout.tv_nsec = 0; char buf[2]; read(fd[0], &buf, 1); // wait until child activate timer close(fd[0]); // This process waits enough for the child process to complete if (sigtimedwait(&sigset, NULL, &timeout) < 0) { if (errno == EAGAIN) { fprintf(stderr, "Timeout, killing child\n"); kill (child, SIGKILL); return 1; } else { fprintf(stderr, "Error in sigtimedwait"); return 1; } } int status; // In case child process is still running the test fails (assuming deadlock occurred) if (waitpid(child, &status, 0) < 0) { fprintf(stderr, "Error on waitpid"); return 1; } if(WIFEXITED(status) && WEXITSTATUS(status)==0) { fprintf(stderr, "Parent - terminate OK\n"); return 0; } return 1; } else { // inside child close(fd[0]); // Close unused read end struct sigaction sigact; struct itimerval itval; pthread_t tid; sigact.sa_handler = Handle; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); if (sigaction(SIGVTALRM, &sigact, 0) == -1) { fprintf(stderr, "Child - Unable to set up handler\n"); return 1; } struct sigaction sigact_timeout; sigact_timeout.sa_handler = TimeoutHandler; sigact_timeout.sa_flags = 0; sigfillset(&sigact_timeout.sa_mask); if (sigaction(SIGALRM, &sigact_timeout, 0) == -1) { fprintf(stderr, "Child - Unable to set up timeout handler\n"); return 1; } // Create a new thread that calls a system call in a loop if (pthread_create(&tid, 0, DoSysCallTillSignalsDone, 0) != 0) { fprintf(stderr, "Child - Unable to create thread\n"); return 1; } // Set timer to create an async signals itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = USLEEP_TIME; itval.it_value.tv_sec = 0; itval.it_value.tv_usec = USLEEP_TIME; if (setitimer(ITIMER_VIRTUAL, &itval, 0) == -1) { fprintf(stderr, "Child - Unable to set up timer\n"); return 1; } close(fd[1]); // send EOF to signal parent that timer is on alarm(DEADLOCK_TIMEOUT); /* * Call an analysis function in a loop . */ while (SigCount < COUNT) { volatile PF doToolAnalysis = DoToolAnalysis; doToolAnalysis(); } itval.it_value.tv_sec = 0; itval.it_value.tv_usec = 0; if (setitimer(ITIMER_VIRTUAL, &itval, 0) == -1) { fprintf(stderr, "Child - Unable to disable timer\n"); return 1; } pthread_join(tid, 0); fprintf(stderr, "Child - terminate OK\n"); return 0; } } void sig_hand(int a) {}; static void Handle(int sig) { SigCount++; printf("Signal Count: %u\n", SigCount); alarm(DEADLOCK_TIMEOUT); } static void TimeoutHandler(int a) { printf("Deadlock timeout occured, it seems that PIN couldn't get the app and tool out of a deadlock situation. SigCount: %u\n", SigCount); exit(1); } static void *DoSysCallTillSignalsDone(void *v) { /* * Call a system call in the loop . */ while (SigCount < COUNT) { sched_yield(); } return 0; } void DoToolAnalysis() { /* Pin tool places an instrumentation point here */ /* * Yield the processor to give the child thread a chance to run. * Otherwise, there is a danger that other thread will starve while * trying to acquire the PIN_LOCK in the tool. */ sched_yield(); }