Futexes and blocking in the kernel - c

I was reading some documentation and trying out a few samples of code that issue the futex system call in Linux. I read that if a mutex is acquired by thread_a using FUTEX_LOCK_PI, and say if another thread thread_b tries to acquire the same, the latter(thread_b) is blocked in the kernel.
What exactly is meant by "blocked in the kernel"? Is it that the execution of thread_b in userspace does not resume? In the following example, the second thread seems to issue the printf after the syscall as well. Would that not suggest that it is not blocked in the kernel? What am I missing?
/*
* This example demonstrates that when futex_lock_pi is called twice, the
* second call is blocked inside the kernel.
*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int mutex = 0;
#define __NR_futex 240
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1
#define FUTEX_FD 2
#define FUTEX_REQUEUE 3
#define FUTEX_CMP_REQUEUE 4
#define FUTEX_WAKE_OP 5
#define FUTEX_LOCK_PI 6
#define FUTEX_UNLOCK_PI 7
#define FUTEX_TRYLOCK_PI 8
#define FUTEX_WAIT_REQUEUE_PI 11
#define FUTEX_CMP_REQUEUE_PI 12
void *thread(void *arg) {
int ret = 0;
pid_t tid = gettid();
printf("Entering thread[%d]\n", tid);
ret = syscall(__NR_futex, &mutex, FUTEX_LOCK_PI, 1, NULL, NULL, 0);
printf("Thread %d returned from kernel\n", tid);
printf("Value of mutex=%d\n", mutex);
printf("Return value from syscall=%d\n", ret);
}
int main(int argc, const char *argv[]) {
pthread_t t1, t2;
if (pthread_create(&t1, NULL, thread, NULL)) {
printf("Could not create thread 1\n");
return -1;
}
if (pthread_create(&t2, NULL, thread, NULL)) {
printf("Could not create thread 2\n");
return -1;
}
// Loop infinitely
while ( 1 ) { }
return 0;
}
The output can be found below :-
Entering thread[952]
Thread 952 returned from kernel
Entering thread[951]
Value of mutex=-2147482696
Return value from syscall=0
Thread 951 returned from kernel
Value of mutex=-1073740873
Return value from syscall=0

blocked in the kernel means that the thread is put to sleep state until an events wakes it up, in your case the event is the mutex to get available.
I changed a little bit your code to illustrate:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread(void *unused) {
printf("Entering thread %d\n", syscall(SYS_gettid));
pthread_mutex_lock(&mutex);
sleep(2);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, const char *argv[]) {
pthread_t t1, t2;
printf("using mutex #%p\n", &mutex);
if (pthread_create(&t1, NULL, thread, &t1)) {
printf("Could not create thread 1\n");
return -1;
}
if (pthread_create(&t2, NULL, thread, &t2)) {
printf("Could not create thread 2\n");
return -1;
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
The code is basically the same but I use explicitly a mutex of the pthread lib.
If I run this code with strace, I get:
using mutex #0x6010a0
Process 19688 attached
Entering thread 19688
Process 19689 attached
Entering thread 19689
[pid 19689] futex(0x6010a0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 19688] futex(0x6010a0, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 19689] <... futex resumed> ) = 0
[pid 19688] +++ exited with 0 +++
[pid 19689] futex(0x6010a0, FUTEX_WAKE_PRIVATE, 1) = 0
[pid 19689] +++ exited with 0 +++
+++ exited with 0 +++
You may notice that we do not see the first thread taking the mutex, BUT we can see the next one caling futex for waiting (FUTEX_WAIT_PRIVATE). This is due to the fact that futex doesn't get called when the mutex is being taken.
But then you can see that the first thread (id 19688 here) then eventually calls futex(FUTEX_WAKE_PRIVATE) which tells the kernel to wake the other thread now the utex is free.
You may have noticed that the first call
[pid 19689] futex(0x6010a0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
is unfinished, which means that the process was hanged waiting for the kernel to finish the job and give back the hand.
Then [pid 19689] <... futex resumed> ) = 0 sketches that the call is eventually finished (obviously because the mutex was released)

Related

How to use semaphores to control progress of 2 threads?

I learned the concept of semaphore. And I'm trying to implement it.
I've been trying to implement it for over 19 hours, but I can't do it, so I'm writing to ask for your help.
It checks the progress of the current two threads, just as it does with CV, and if both threads output entered, it can resume the subsequent operation.
Below is the full text of the code.
`
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
void *child1(void *arg) {
printf("child thread 1 entered!\n");
// call semaphoreshere here
printf("child thread 1 exits!\n");
return NULL;
}
void *child2(void *arg) {
printf("child thread 2: entered!\n");
// call semaphores here
printf("child thread 2: exits\n");
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
printf("parent thread: begin\n");
// init semaphores here
// // sem_init(&empty, 0, 0);
// // sem_init(&full, 0, 0); //Code tried but not working properly
pthread_create(&p1, NULL, child1, NULL);
pthread_create(&p2, NULL, child2, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("parent thread: end\n");
return 0;
}
`
Using ONLY TWO semaphores within the code, this attempts to control the thread's internal execution order so that both threads must output the ~~entered message before it can exit.
The execution result I want is as follows.
>>>
parent thread: begin
child thread 1 entered!
child thread 2: entered!
child thread 2: exits
child thread 1 exits!
parent thread: end
>>>
parent thread: begin
child thread 2 entered!
child thread 1: entered!
child thread 1: exits
child thread 2 exits!
parent thread: end
Like this, I want to implement only the role of monitoring each other to see if they've entered.
I'd appreciate your help.
thanks.
What you want to do (in semaphoore-thinking) is:
thread 1 waits till thread 2 is done before it exits.
thread 2 waits till thread 1 is done before it exits.
Which leads to the following code, which I modified a bit because I cannot stand global variables.
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct SharedContext_tag {
sem_t t1done;
sem_t t2done;
} SharedContext_t;
void *child1(void* arg) {
SharedContext_t* ctx = (SharedContext_t*) arg;
printf("child thread 1: entered!\n");
sem_post(&ctx->t1done);
sem_wait(&ctx->t2done);
printf("child thread 1 exits!\n");
return NULL;
}
void *child2(void* arg) {
SharedContext_t* ctx = (SharedContext_t*) arg;
printf("child thread 2: entered!\n");
sem_post(&ctx->t2done);
sem_wait(&ctx->t1done);
printf("child thread 2: exits!\n");
return NULL;
}
int main(int argc, const char* argv[]) {
pthread_t p1;
pthread_t p2;
SharedContext_t context;
sem_init(&context.t1done, 0, 0);
sem_init(&context.t2done, 0, 0);
printf("parent thread: begin\n");
pthread_create(&p1, NULL, child1, &context);
pthread_create(&p2, NULL, child2, &context);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("parent thread: end\n");
sem_close(&context.t1done);
sem_close(&context.t2done);
return 0;
}
On my machine at this time (being careful here!), the output is as required:
> ./sema
parent thread: begin
child thread 1: entered!
child thread 2: entered!
child thread 2: exits!
child thread 1 exits!
parent thread: end
In order for it to work, you need to link against the real-time library, librt. You do so by adding -pthread to your compile command.
> clang-13 -g -O0 -pthread -o sema sema.c

How a thread can commit suicide with syscall only in C Linux

I wrote the following code, the goal is to make thread to suicide with syscall (without calling pthread_exit()).
So I create two threads and select one that get its thread id, and send SIGKILL to itself with tkill()
The problem is that all the process terminates, not only the selected thread.
Why is that? How can I makes thread to suicide with syscall only?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
void *myThreadFun(void *vargp)
{
int tid = syscall(SYS_gettid);
if(tid %2 ==0)
{
printf("kill thread id ! = %ld \n",tid);
syscall(SYS_tkill,tid,9);
}
while(1)
{
printf("Thread ID: %d\n", tid);
sleep(2);
}
}
int main()
{
int i;
pthread_t tid;
printf("main thread id = %ld \n",syscall(SYS_gettid));
for (i = 0; i < 2; i++)
pthread_create(&tid, NULL, myThreadFun, NULL);
char tmp;
scanf("%c",&tmp);
return 0;
}
output:
main thread id = 11911
kill thread id ! = 11912
Killed
There is no "kill this thread only" signal disposition; there are only "ignore", "terminate the entire process", "terminate the entire process and dump a core file", "stop (pause) the entire process", and "continue the entire process if it is stopped (paused)". You cannot even choose the disposition per se; only between the default disposition for that particular signal, "ignore", or an userspace signal handler function.
There is really only one option: the SYS_exit syscall.
In C, you can do this via
#include <unistd.h>
#include <sys/syscall.h>
static inline void exit_thread(void) __attribute__((noreturn));
static inline void exit_thread(void)
{
syscall(SYS_exit, 0);
}
A real-world example always beats a snippet, I think. Consider the following program:
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
void *thread_function(void *payload)
{
#ifdef DO_EXIT
const int exit_code = (int)(intptr_t)payload;
fprintf(stderr, "Running thread_function(%p); calling syscall(SYS_exit, %d).\n", payload, exit_code);
fflush(stderr);
syscall(SYS_exit, exit_code);
#else
fprintf(stderr, "Running thread_function(%p); calling pthread_exit(%p).\n", payload, payload);
fflush(stderr);
pthread_exit(payload);
#endif
return NULL; /* Never reached */
}
int main(void)
{
void *const payload = thread_function; /* Just some random pointer value */
pthread_t thread_id;
void *thread_status;
int err;
printf("Calling pthread_create(&thread_id, NULL, thread_function, %p): ", payload);
fflush(stdout);
err = pthread_create(&thread_id, NULL, thread_function, payload);
if (err) {
printf("Failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("Success.\n");
printf("Calling pthread_join(thread_id, &thread_status): ");
fflush(stdout);
err = pthread_join(thread_id, &thread_status);
if (err) {
printf("Failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("Success; thread_status == %p.\n", thread_status);
return EXIT_SUCCESS;
}
Save it as example.c, then compile one version, ex1, that uses pthread_exit():
gcc -Wall -Wextra -O2 example.c -pthread -o ex1
and another, ex2, that uses syscall(SYS_exit,):
gcc -Wall -Wextra -DDO_EXIT -O2 example.c -pthread -o ex2
To see exactly what is happening, run each example under strace, logging each clone, write, futex, exit, and exit_group syscalls the program makes:
strace -f -e clone,write,futex,exit,exit_group -o ex1.log ./ex1
strace -f -e clone,write,futex,exit,exit_group -o ex2.log ./ex2
On my machine, ex1.log looks like
28703 write(1, "Calling pthread_create(&thread_i"..., 75) = 75
28703 clone(child_stack=0x7f11f9c14fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f11f9c159d0, tls=0x7f11f9c15700, child_tidptr=0x7f11f9c159d0) = 28704
28703 write(1, "Success.\n", 9) = 9
28703 write(1, "Calling pthread_join(thread_id, "..., 49) = 49
28703 futex(0x7f11f9c159d0, FUTEX_WAIT, 28704, NULL <unfinished ...>
28704 write(2, "Running thread_function(0x563f0c"..., 79) = 79
28704 futex(0x7f11f94141a0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
28704 exit(0) = ?
28703 <... futex resumed> ) = 0
28704 +++ exited with 0 +++
28703 write(1, "Success; thread_status == 0x563f"..., 42) = 42
28703 exit_group(0) = ?
28703 +++ exited with 0 +++
and ex2.log looks like
28707 write(1, "Calling pthread_create(&thread_i"..., 75) = 75
28707 clone(child_stack=0x7f42c7db8fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f42c7db99d0, tls=0x7f42c7db9700, child_tidptr=0x7f42c7db99d0) = 28708
28707 write(1, "Success.\n", 9) = 9
28708 write(2, "Running thread_function(0x556b57"..., 80 <unfinished ...>
28707 write(1, "Calling pthread_join(thread_id, "..., 49 <unfinished ...>
28708 <... write resumed> ) = 80
28707 <... write resumed> ) = 49
28708 exit(1472596720 <unfinished ...>
28707 futex(0x7f42c7db99d0, FUTEX_WAIT, 28708, NULL <unfinished ...>
28708 <... exit resumed>) = ?
28707 <... futex resumed> ) = 0
28708 +++ exited with 240 +++
28707 write(1, "Success; thread_status == (nil)."..., 33) = 33
28707 exit_group(0) = ?
28707 +++ exited with 0 +++
From their differences, and the fact that this is GNU C library version 2.27 running on x86-64 architecture, we can make a couple of important observations:
The GNU pthreads implementation uses a futex to pass the thread function return value, when the thread exits and is reaped (using pthread_join()) by another thread.
Aside from the futex used to pass the return value, pthread_exit() calls the exit syscall with exit status 0.
(In fact, the man 3 pthread_exit man page says so explicitly.)
When the thread exits (using the exit syscall), the exit status code is irrelevant.
If our thread function uses the syscall directly, cleanup functions registered by atexit() and pthread_push() will not be called, and if pthread_join() is called on this thread, the return value will essentially be (void *)0 (== NULL in Linux, printed as (nil) by printf()/fprintf() %p format specifier).

what happen if the main thread finish but peer threads are not?

I'm new to C and multithreading programming, just a question on what happen if the main thread finish but peer threads are not, does the process still wait until all peer threads finish or the process terminates immediately right after the main thread is done?
When the main function returns, or you call exit, then the system will terminate the process and all threads running in that process.
If you have background (detached) threads that you want to keep running, you have to end the "main" thread only, not the process. How to do that is platform-dependent, but on POSIX platforms (e.g. Linux or macOS) then you call pthread_exit instead of exit.
If you don't have any background threads then you need to join all the running threads to keep them from being forcibly terminated.
Here is an example program which illustrates the preceding answer on GLIBC/Linux operating system. The program creates a detached secondary thread and if it is not passed a parameter, the main thread exits otherwise it calls pthread_exit():
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
void *th_entry(void *p)
{
int i;
printf("Secondary thread starting...\n");
for (i = 0; i < 5; i ++) {
printf(".");
fflush(stdout);
sleep(1);
}
printf("\nSecondary thread exiting\n");
return NULL;
}
int main(int ac, char *av[])
{
int rc;
pthread_t tid;
printf("Main thread starting...\n");
rc = pthread_create(&tid, NULL, th_entry, NULL);
if (rc != 0) {
errno = rc;
fprintf(stderr, "pthread_create(): '%m' (%d)\n", errno);
return 1;
}
// Detach the secondary thread
rc = pthread_detach(tid);
if (rc != 0) {
errno = rc;
fprintf(stderr, "pthread_detach(): '%m' (%d)\n", errno);
return 1;
}
// Some dummy work
sleep(1);
if (av[1]) {
printf("Main thread exiting\n");
return 0;
} else {
printf("Main thread calling pthread_exit()\n");
pthread_exit(NULL);
}
return 0;
}
Compile it:
$ gcc td.c -l pthread
When the program is not passed any parameter, the main thread calls pthread_exit() and the process ends when the secondary thread finishes.
$ ./a.out
Main thread starting...
Secondary thread starting...
.Main thread calling pthread_exit()
....
Secondary thread exiting
$
When the program is passed a parameter, the main thread calls exit() (it returns 0 but it is equivalent) and the process ends immediately terminating the secondary thread:
$ ./a.out 1
Main thread starting...
Secondary thread starting...
.Main thread exiting
$

Detached thread won't exit although it runs pthread_exit?

I have been dealing with a problem in a thread pool for some days now. I tried all types of different things but I can't seem to solve the issue. I have made a simple version that reproduces the problem.
Code:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
struct bsem_t bsem;
pthread_t threads[2];
/* Binary semaphore */
typedef struct bsem_t {
pthread_mutex_t mutex;
pthread_cond_t cond;
int v;
} bsem_t;
void bsem_post(bsem_t *bsem) {
pthread_mutex_lock(&bsem->mutex);
bsem->v = 1;
pthread_cond_broadcast(&bsem->cond);
pthread_mutex_unlock(&bsem->mutex);
}
void bsem_wait(bsem_t *bsem) {
pthread_mutex_lock(&bsem->mutex);
while (bsem->v != 1) {
pthread_cond_wait(&bsem->cond, &bsem->mutex);
}
bsem->v = 0;
pthread_mutex_unlock(&bsem->mutex);
}
/* Being called by each thread on SIGUSR1 */
void thread_exit(){
printf("%u: pthread_exit()\n", (int)pthread_self());
pthread_exit(NULL);
}
/* Startpoint for each thread */
void thread_do(){
struct sigaction act;
act.sa_handler = thread_exit;
sigaction(SIGUSR1, &act, NULL);
while(1){
bsem_wait(&bsem); // Each thread is blocked here
puts("Passed semaphore");
}
}
/* Main */
int main(){
bsem.v = 0;
pthread_create(&threads[0], NULL, (void *)thread_do, NULL);
pthread_create(&threads[1], NULL, (void *)thread_do, NULL);
pthread_detach(threads[0]);
pthread_detach(threads[1]);
puts("Created threads");
sleep(2);
pthread_kill(threads[0], SIGUSR1);
pthread_kill(threads[1], SIGUSR1);
puts("Killed threads");
sleep(10);
return 0;
}
What the code does, is create two threads. Both threads wait on a binary semaphore (bsem_wait). Then while they are waiting I send a SIGUSR1 signal to both resulting on pthread_exit() being executed on each thread. On my terminal it shows that everything goes as planned..
Output:
Created threads
Killed threads
2695145216: pthread_exit()
2686752512: pthread_exit()
The problem
Although the output seems correct, using pstree shows that only one of two threads die. The other thread stays alive until the whole program exits. Why is this?
Update
Replacing my custom binary semaphore with a normal semaphore seems to solve this for no apparent reason..
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <semaphore.h>
sem_t sem;
pthread_t threads[2];
/* Caller thread will exit */
void thread_exit(){
printf("%u: pthread_exit()\n", (int)pthread_self());
pthread_exit(NULL);
}
/* Startpoint for each thread */
void* thread_do(){
struct sigaction act;
act.sa_handler = thread_exit;
sigaction(SIGUSR1, &act, NULL);
while(1){
sem_wait(&sem); // Each thread is blocked here
puts("Passed semaphore");
}
}
/* Main */
int main(){
sem_init(&sem, 0, 0); // Normal semaphore
pthread_create(&threads[0], NULL, thread_do, NULL);
pthread_create(&threads[1], NULL, thread_do, NULL);
pthread_detach(threads[0]);
pthread_detach(threads[1]);
puts("Created threads in pool");
sleep(2);
//PROBLEM
pthread_kill(threads[0], SIGUSR1);
pthread_kill(threads[1], SIGUSR1);
puts("Destroyed pool");
sleep(10);
return 0;
}
You can't get there from here
pthread_exit() is not listed in the "signal safe funtions" of the signal(7) man page.
rewrite your code to have the pthread_exit call outside of the signal handler.
So the issue seemed to be a deadlock!
The problem is that each thread is waiting inside the bsem_wait of the binary semaphore in different locations:
void bsem_wait(bsem_t *bsem) {
pthread_mutex_lock(&bsem->mutex); // THREAD 2 BLOCKED HERE
while (bsem->v != 1) {
pthread_cond_wait(&bsem->cond, &bsem->mutex); // THREAD 1 WAITING HERE
}
bsem->v = 0;
pthread_mutex_unlock(&bsem->mutex);
}
Thread 1 in this case is the fastest thread. Thread 2 is the slower one. When I run the signal to kill a thread, the waiting thread unblocks and exits as expected. The problem is that it never unlocks the mutex. So the blocked thread (2) remains blocked forever. For some reason, the thread won't be terminated because it is waiting on the mutex.
Just adding an unblock before exiting, solves the issue:
void thread_exit(){
printf("%u: pthread_exit()\n", (int)pthread_self());
pthread_mutex_unlock(&mutex); // NEW CODE
pthread_exit(NULL);
}
This ofcourse is a hack to demonstrate what is happening and shouldn't be used. I am going to follow Jasen's advice and get rid of the signal handler all together and solve it some other way. Namely I have to assure that the thread goes through the whole bsem_wait!

Can a Thread trace a Process?

I have a multithreades process that has to control the execution of one other process.
To do so, from one of the threads I use Ptrace.
This is how the tracee is created and launched.
switch( childPID=fork() ){
case -1:
perror("fork()");
return -1;
case 0 :
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execve(execPath,NULL,NULL);
return -1;
default:
break;
}
This is how the process is run
while (1) {
ptrace(PTRACE_CONT, childPID, 0, 0);
waitpid( childPID, &status, 0);
// inspect status and break in some cases
...
...
}
I have a similar non multithreades application that works perfectly, load exec and inspect stack and memory without problems. But when I try this configuration on the multithreades one the process I create does not run at all.
My question is. How can I trace a process from a thread ? Do I have to change the way I attach the process?
The code at the end of the post is one answer to the question.
You can have a thread that trace a process.
If someone is interested, the problem I was experimenting was that, for some unintelligible reasons, the tracer thread was not the one sending all the tracing commands. One of them was calling the fork and having the responsibility of trace, one other was sending
ptrace(PTRACE_CONT, childPID, 0, 0);
ptrace (PTRACE_GETREGS, childPID, 0, registers);
and the resulting error was: ptrace (PTRACE_GETREGS,..) Couldn't get registers: No such process
#include <pthread.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/reg.h>
#include <sys/user.h>
#define NUM_THREADS 9
int childPID;
int fatherPID;
void print_registers(struct user_regs_struct *registers){
printf("\tReg ebx 0x%lx\n",registers->ebx);
printf("\tReg ecx 0x%lx\n",registers->ecx);
printf("\tReg edx 0x%lx\n",registers->edx);
printf("\tReg esi 0x%lx\n",registers->esi);
printf("\tReg edi 0x%lx\n",registers->edi);
printf("\tReg ebp 0x%lx\n",registers->ebp);
printf("\tReg eax 0x%lx\n",registers->eax);
printf("\tReg xds 0x%lx\n",registers->xds);
printf("\tReg xes 0x%lx\n",registers->xes);
printf("\tReg xfs 0x%lx\n",registers->xfs);
printf("\tReg xgs 0x%lx\n",registers->xgs);
printf("\tReg orig_eax 0x%lx\n",registers->orig_eax);
printf("\tReg eip 0x%lx\n",registers->eip);
printf("\tReg xcs 0x%lx\n",registers->xcs);
printf("\tReg eflags 0x%lx\n",registers->eflags);
printf("\tReg esp 0x%lx\n",registers->esp);
printf("\tReg xss 0x%lx\n",registers->xss);
}
int load(char * execPath){
switch( childPID=fork() ){
case -1:
perror("fork()");
return -1;
case 0 :
if( access(execPath, X_OK)==-1){
printf("\tAcces denied to\n",execPath);
}
else {
printf("\tChild Process pid :%d %d\n",childPID,getpid());
if(ptrace(PTRACE_TRACEME, 0, NULL, NULL)<0){
perror("ptrace(PTRACE_TRACEME)");
return -1;
}
execve(execPath,NULL,NULL);
perror("execve()");
}
return -1;
default:
wait(NULL);
fatherPID=getpid();
printf("\tParent Process pid :%d %d\n",fatherPID,childPID);
if (ptrace(PTRACE_SETOPTIONS, childPID, 0, PTRACE_O_TRACEEXIT)){
perror("stopper: ptrace(PTRACE_SETOPTIONS, ...)");
return -1;
}
break;
}
return -1;
}
void registers(){
printf("\t##Command get_registers#\n");
struct user_regs_struct * registers = (struct user_regs_struct*)(calloc(1, sizeof(struct user_regs_struct)));
long ret = ptrace (PTRACE_GETREGS, childPID, 0, registers);
if (ret <0) perror("ptrace (PTRACE_GETREGS,..) Couldn't get registers");
print_registers(registers);
free(registers);
}
int continuE(){
int status = 0;
int signo;
long long_var=0;
// to continue the execution is needed to trigger the event
while (1) {
ptrace(PTRACE_CONT, childPID, 0, 0);
waitpid( childPID, &status, 0);
if (WIFEXITED(status))
printf("Child exited by %d\n",WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf(" child process terminated by a signal %d \n",WTERMSIG(status) );
if (WIFSTOPPED(status)) {
signo = WSTOPSIG(status);
//printf("Child stopped by %d\n",signo);
}
// we had the sigtrap and we are at the exec
if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXEC<<8))){
printf("\t###Stopped the tracee at EXEC, with status %d###\n",WEXITSTATUS(status));
ptrace(PTRACE_GETEVENTMSG, childPID,0,&long_var);
printf("\t###PTRACE_GETEVENTMSG result %lu ,%d ###\n",long_var,WEXITSTATUS(long_var));
}
// we have a sigtrap and we are on the exit
// we could think to take out PTRACE_O_TRACEEXIT
if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))){
printf("\t###Stopped the tracee at EXIT###\n");
signo= SIGHUP;
}
// normal cases
if ((signo == SIGTRAP) || (signo == SIGTERM) ||(signo ==SIGINT) || (signo == SIGHUP)
|| ( signo == SIGSEGV) ){
break;
}
}
return signo;
}
void *work(void *threadid)
{
long tid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
load("/home/rtems/plibeagleeye/Plib/Tests/bin/stanford.o");
registers();
continuE();
registers();
pthread_exit(NULL);
}
void *work2(void *threadid)
{
long tid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
int rc;
long *taskids;
void *status;
taskids = (long *) malloc( NUM_THREADS * sizeof(long));
long t=0;
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
taskids[t] = 0;
rc = pthread_create(&threads[t], &attr, work, (void *)taskids[t]);
for(t=1; t<NUM_THREADS; t++){
taskids[t] = t;
printf("Creating thread %ld\n", t);
rc = pthread_create(&threads[t], &attr, work2, (void *)taskids[t]);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_attr_destroy(&attr);
for(t=0; t<NUM_THREADS; t++){
rc = pthread_join(threads[t], &status);
if (rc) {
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status);
}
printf("Ciaoz all threads finished their jobs\n");
free(taskids);
/* Last thing that main() should do */
pthread_exit(NULL);
return 0;
}
The thing that really surprise me is that there is no indications on which thread is the tracer. ptrace(PTRACE_TRACEME, 0, NULL, NULL) the 0 seems to work perfectly.
In a multi-threaded application in order to trace the program you need to use ptrace for each and particular thread the parent process spawns by using ptrace(PTRACE_foo, pid, ...) where pid is the thread id of the process. In order to trace the parent itself then use ptrace with pid = 0 in the parent code. ptrace is strictly to a particular thread only.
hope u found what u were looking after...
[EDIT]
I've made a mistake concerning the question interpretation.
Answering the comment below: According with the manual PTRACE_TRACEME does not attach the tracee to the main thread but to the parent one.
PTRACE_TRACEME -- Indicate that this process is to be traced by its parent.
[THE OLD UNPROPER ANSWER]
The trace is per thread, you need attach each thread individually. Your code just attach to the main thread of the process called by execve.
from README-linux-ptrace:
Attachment and subsequent commands are per thread: in a multithreaded process, every thread can be individually attached to a (potentially different) tracer, or left not attached and thus not debugged. Therefore, "tracee" always means "(one) thread", never "a (possibly multithreaded) process".
You can do it catching the SIGTRAP signal (from ptrace man):
If the PTRACE_O_TRACEEXEC option is not in effect, all successful calls to execve(2) by the traced process will cause it to be sent a SIGTRAP signal, giving the parent a chance to gain control before the new program begins execution.
and using PTRACE_GETEVENTMSG to recover the pid:
Retrieve a message (as an unsigned long) about the ptrace event that just happened, placing it at the address data in the tracer. For PTRACE_EVENT_EXIT, this is the tracee's exit status. For PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_VFORK_DONE, and PTRACE_EVENT_CLONE, this is the PID of the new process. (addr is ignored.)
and then using PTRACE_ATTACH for attach to the recovered new pid.

Resources