Code:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <signal.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <string.h>
volatile int timeout_g =0;
volatile int stop_loop_g = 0;
struct Node
{
void *data;
struct Node *next;
};
void push(struct Node** head_ref, void *new_data, size_t data_size)
{
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = malloc(data_size);
new_node->next = (*head_ref);
int i;
for (i=0; i<data_size; i++)
*(char *)(new_node->data + i) = *(char *)(new_data + i);
(*head_ref) = new_node;
}
struct Node* get_tid_list(int pid)
{
struct Node* tids_list = NULL;
static const char task_path[] = "/proc/%d/task";
char procdir[sizeof(task_path)+sizeof(int) *3];
DIR * dir;
int tid;
if(sprintf(procdir, task_path, pid)>0 && ( dir = opendir(procdir))!= NULL)
{
struct dirent *de;
while((de = readdir(dir))!=NULL)
{
if(de->d_fileno ==0)
continue;
char dummy ;
if (sscanf(de->d_name, "%d%c" , &tid, &dummy)!=1)
{
continue;
}
push (&tids_list, &tid, sizeof(int));
}
}
return tids_list;
}
int wait_signal_or_timeout (int tid)
{
struct timespec timeout;
timeout.tv_sec = 2;
timeout.tv_nsec =0;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, & set , NULL);
int ret = sigtimedwait(&set , NULL, &timeout);
if(ret == SIGCHLD)
{
goto exit;
}
if(ret == SIGINT)
{
stop_loop_g = 1;
goto exit;
}
if(errno == EAGAIN)
{
timeout_g = 1 ;
goto exit;
}
//printf("error , tid = %d \n",tid);
printf("error , ret= %d , errno = %s\n",ret, strerror(errno));
exit:
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
if(timeout_g || stop_loop_g)
return -1;
else
return 0;
}
void main(int argv, char *argc[])
{
int pid = atoi(argc[1]);
int status ;
struct Node* tids_list = get_tid_list(pid);
while(tids_list != NULL && !stop_loop_g)
{
int tid = *(int*)tids_list->data;
//printf("tid = %d \n",tid);
if(ptrace(PTRACE_ATTACH, tid, NULL,NULL) == -1 )
{
printf("error attach tid = %d \n", tid );
tids_list = tids_list->next;
continue;
}
if(waitpid(tid, &status, __WALL)==-1)
{
printf("error waitpid tid = %d \n", tid );
tids_list = tids_list->next;
continue;
}
timeout_g = 0;
time_t finish_time = time(NULL) +3;//check during 3 sec.
int flag =0;
int use_syscall=0;
while ((long)time(NULL) -(long)finish_time <0 && !timeout_g)
{
if(ptrace(PTRACE_SYSCALL, tid, NULL, NULL) == -1)
{
printf("error. tid = %d .\n",tid);
break;
}
if(wait_signal_or_timeout(tid) ==-1)
break;
if(flag ==0)//enter syscall
{
flag = 1;
int command = ptrace(PTRACE_PEEKUSER,tid, 8 * ORIG_RAX,NULL);
if(command == SYS_write)
{
use_syscall =1;
printf("tid = %d , call to WRITE syscall = %d \n",tid, command);
}
}
else //exit from syscall
{
flag = 0;
}
}
if(use_syscall)
{
printf("tid = %d , use write syscall \n",tid);
}
else
{
printf("tid = %d , didn't use write syscall \n",tid);
}
if(ptrace(PTRACE_DETACH, tid, NULL , NULL) <0)
{
//can't detach from tid , maybe because tid not stop
syscall(SYS_kill, tid , SIGSTOP); //send sigstop so we can detach from tid
if(waitpid(tid , &status, __WALL) == -1)
{
printf("error while send SIGSTOP \n");
}
if(ptrace(PTRACE_DETACH , tid, NULL , NULL) <0)
{
printf("ERROR detach again ! \n");
}
syscall(SYS_kill, tid , SIGCONT);
}
tids_list = tids_list->next;
}
}
Here is test program
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * write_each_sec(void *vargp)
{
printf("start write_each_sec \n");
while(1)
{
sleep(1);
printf("a\n" );
}
return NULL;
}
void * only_sleep(void *vargp)
{
printf("start only_sleep \n");
while(1)
{
sleep(1);
}
return NULL;
}
int main()
{
pthread_t thread_id[30];
int i ;
for(i= 0; i<30;i++)
{
if(i%2==0)
pthread_create(thread_id[i], NULL, write_each_sec, NULL);
else
pthread_create(&thread_id[i], NULL, only_sleep, NULL);
}
for(i= 0; i<30;i++)
{
pthread_join(thread_id[i], NULL);
}
}
Test program create half threads that using write syscall and half threads that not using write syscall.