You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
6.5 KiB

/*
* 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 <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#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();
}