سؤال

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

  1. ptrace(PTRACE_CONT, childPID, 0, 0);
  2. 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top