线程可以跟踪进程吗?

时间:2013-09-11 09:47:32

标签: c multithreading pthreads ptrace

我有一个多节点进程,必须控制另一个进程的执行。 为此,我从其中一个线程使用Ptrace。 这就是tracee的创建和启动方式。

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;
}

这是流程的运行方式

while (1) {
    ptrace(PTRACE_CONT, childPID, 0, 0);
    waitpid( childPID, &status, 0);
    // inspect status and break in some cases
    ...
    ...
}

我有一个类似的非multithreades应用程序,完美地工作,加载exec并检查堆栈和内存没有问题。但是当我在multithreades上尝试这个配置时,我创建的进程根本不会运行。

我的问题是。如何从线程中跟踪进程?我是否必须改变附加过程的方式?

3 个答案:

答案 0 :(得分:1)

在多线程应用程序中,为了跟踪程序,你需要为每个特定线程使用ptrace,父进程使用ptrace(PTRACE_foo,pid,...)生成,其中pid是进程的线程id 。为了跟踪父本身,请在父代码中使用ptrace和pid = 0。 ptrace仅限于特定的线程。 希望你找到你所照顾的......

答案 1 :(得分:1)

帖子末尾的代码是问题的一个答案。 您可以拥有一个跟踪流程的主题。


如果有人感兴趣,我正在尝试的问题是,由于一些无法理解的原因,跟踪线程不是发送所有跟踪命令的线程。其中一个是调用fork并负责跟踪,另一个是发送

  1. ptrace(PTRACE_CONT,childPID,0,0);
  2. ptrace(PTRACE_GETREGS,childPID,0,寄存器);
  3. ,结果错误是:ptrace(PTRACE_GETREGS,..)无法获取寄存器:没有这样的过程


    #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;
    
    }
    

    让我感到惊讶的是,没有迹象表明哪个线程是跟踪器。 ptrace(PTRACE_TRACEME,0,NULL,NULL)0看起来效果很好。

答案 2 :(得分:0)

[编辑]

我对问题解释犯了错误。

回答下面的评论:根据手册,PTRACE_TRACEME不会将tracee附加到主线程,而是附加到主线程。

  

PTRACE_TRACEME - 表示该进程将由其父进程跟踪。

[旧的不道德的回答]

跟踪是每个线程,您需要单独附加每个线程。您的代码只附加到execve调用的进程的主线程。

来自README-linux-ptrace:

  

附件和后续命令是每个线程:在多线程进程中,每个线程可以单独附加到(可能不同的)跟踪器,或者不附加,因此不进行调试。因此,“tracee”总是意味着“(一个)线程”,而不是“一个(可能是多线程的)进程”。

你可以捕捉到SIGTRAP信号(来自ptrace man):

  

如果PTRACE_O_TRACEEXEC选项没有生效,跟踪进程对execve(2)的所有成功调用都将使其被发送SIGTRAP信号,使父母有机会在新程序开始执行之前获得控制权。 / p>

并使用PTRACE_GETEVENTMSG恢复pid:

  

检索刚刚发生的ptrace事件的消息(作为unsigned long),将其放在跟踪器中的地址数据中。对于PTRACE_EVENT_EXIT,这是tracee的退出状态。对于PTRACE_EVENT_FORK,PTRACE_EVENT_VFORK,PTRACE_EVENT_VFORK_DONE和PTRACE_EVENT_CLONE,这是新进程的PID。 (addr被忽略。)

然后使用PTRACE_ATTACH附加到恢复的新pid。