进程A中的waitpid()可以';t在进程B中的pthread_create()在coredump之前创建的线程中捕获SIGTRAP
首先,启动进程B(请参见下面的进程A中的waitpid()可以';t在进程B中的pthread_create()在coredump之前创建的线程中捕获SIGTRAP,c,multithreading,unix,signals,ptrace,C,Multithreading,Unix,Signals,Ptrace,首先,启动进程B(请参见下面的mt.cpp),它将使用pthread\u create()创建一个线程。主线程和新线程的ppid、pid和tid将为进程A输出,然后它们都启动一个for循环并引发SIGTRAP,进程A中的waitpid()应捕捉到这一点 第二,使用进程B的pid启动进程A(参见下面的attach.cpp)。进程A将通过ptrace(ptrace\u attach,…)连接到进程B,然后使用while(true)中的waitpid()等待信号事件,调用ptrace(ptrace\u
mt.cpp
),它将使用pthread\u create()
创建一个线程。主线程和新线程的ppid
、pid
和tid
将为进程A输出,然后它们都启动一个for循环并引发SIGTRAP
,进程A中的waitpid()
应捕捉到这一点
第二,使用进程B的pid
启动进程A(参见下面的attach.cpp
)。进程A将通过ptrace(ptrace\u attach,…)
连接到进程B,然后使用while(true)
中的waitpid()
等待信号事件,调用ptrace(ptrace\u CONT,…)
如果获得SIGTRAP
,或者,如果获得SIGSTOP
,则中断循环
现在的问题是:
进程A可以捕获进程B的主线程引发的SIGTRAP
,并成功调用ptrace(ptrace\u CONT,…)
,然后进程B将继续按预期执行
但是
当进程B的新线程引发SIGTRAP
时,进程A未能ptrace(ptrace\u CONT,…)
使用errmsg
“没有这样的进程”,因为进程B使用errmsg
“跟踪/断点陷阱(内核转储)”转储了内核。
此外,WIFSTOPPED(status)
变为false,WIFSIGNALED(status)
变为true
我知道SIGTRAP
的默认操作是终止进程,似乎SIGTRAP
在终止操作之后而不是之前被转移到进程A,因此进程A没有机会继续进程B
我尝试了gdb
而不是进程A,两个SIGTRAP
都可以成功捕获并继续。因此,进程A的代码中一定有错误
下面是作为进程A执行的attach.cpp
:
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
pid_t pid = 0;
int ret = 0;
int status = 0;
if (argc > 1) {
pid = atoi(argv[1]);
printf("pid=%d\n", pid);
}
ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
printf("attach ret=%d\n", ret);
waitpid(pid, &status, 0);
ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
printf("cont ret=%d\n", ret);
while (true) {
ret = waitpid(pid, &status, WUNTRACED);
printf("\nwaitpid ret=%d.\n", ret);
int sig = 0;
if (WIFSIGNALED(status)) {
printf("WIFSIGNALED\n");
sig = WTERMSIG(status);
} else if (WIFSTOPPED(status)) {
printf("WIFSTOPPED\n");
sig = WSTOPSIG(status);
} else {
printf("other status %d\n", status);
}
if (SIGTRAP == sig) {
ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
printf("SIGTRAP cont ret=%d err=%s\n", ret, strerror(errno));
} else if (SIGSTOP == sig) {
ret = ptrace(PTRACE_DETACH, pid, NULL, NULL);
printf("SIGSTOP detach ret=%d\n", ret);
break;
} else {
printf("other signal %d\n", sig);
}
sleep(2);
}
return 0;
}
$ ./attach 30389
pid=30389
attach ret=0
cont ret=0
waitpid ret=30389.
WIFSTOPPED
SIGTRAP cont ret=0 err=Success
waitpid ret=30389.
WIFSIGNALED
SIGTRAP cont ret=-1 err=No such process
^C
结果如下:
- 如果您想自己执行这两个程序,请确保在启动进程B后尽快启动进程A(连接)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
void *func(void * arg)
{
printf("child ppid=%d pid=%d tid=%d\n", getppid(), getpid(), gettid());
int i = 0;
for (; i < 5; i++) {
printf("child loop i=%d\n", i);
sleep(2);
}
printf("\nchild before SIGTRAP\n", gettid());
raise(SIGTRAP);
printf("child after SIGTRAP\n\n", gettid());
for (; i < 8; i++) {
printf("child loop i=%d\n", i);
sleep(2);
}
return NULL;
}
int main(void)
{
printf("parent ppid=%d pid=%d tid=%d\n", getppid(), getpid(), gettid());
pthread_t tid;
pthread_create(&tid, NULL, func, NULL);
int i = 0;
for (; i < 3; i++) {
printf("parent loop i=%d\n", i);
sleep(2);
}
printf("\nparent before SIGTRAP\n", gettid());
raise(SIGTRAP);
printf("parent after SIGTRAP\n\n", gettid());
for (; i < 10; i++) {
printf("parent loop i=%d\n", i);
sleep(2);
}
pthread_join(tid, NULL);
return 0;
}
$ ./mt
parent ppid=12238 pid=30389 tid=30389
parent loop i=0
child ppid=12238 pid=30389 tid=30390
child loop i=0
parent loop i=1
child loop i=1
parent loop i=2
child loop i=2
parent before SIGTRAP
child loop i=3
parent after SIGTRAP
parent loop i=3
child loop i=4
parent loop i=4
child before SIGTRAP
Trace/breakpoint trap (core dumped)
过程A:
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
pid_t pid = 0;
int ret = 0;
int status = 0;
if (argc > 1) {
pid = atoi(argv[1]);
printf("pid=%d\n", pid);
}
ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
printf("attach ret=%d\n", ret);
waitpid(pid, &status, 0);
ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
printf("cont ret=%d\n", ret);
while (true) {
ret = waitpid(pid, &status, WUNTRACED);
printf("\nwaitpid ret=%d.\n", ret);
int sig = 0;
if (WIFSIGNALED(status)) {
printf("WIFSIGNALED\n");
sig = WTERMSIG(status);
} else if (WIFSTOPPED(status)) {
printf("WIFSTOPPED\n");
sig = WSTOPSIG(status);
} else {
printf("other status %d\n", status);
}
if (SIGTRAP == sig) {
ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
printf("SIGTRAP cont ret=%d err=%s\n", ret, strerror(errno));
} else if (SIGSTOP == sig) {
ret = ptrace(PTRACE_DETACH, pid, NULL, NULL);
printf("SIGSTOP detach ret=%d\n", ret);
break;
} else {
printf("other signal %d\n", sig);
}
sleep(2);
}
return 0;
}
$ ./attach 30389
pid=30389
attach ret=0
cont ret=0
waitpid ret=30389.
WIFSTOPPED
SIGTRAP cont ret=0 err=Success
waitpid ret=30389.
WIFSIGNALED
SIGTRAP cont ret=-1 err=No such process
^C
Linux
PTRACE\u ATTACH
请求,尽管其参数名为pid,但将只跟踪该线程
您可以通过将此函数添加到程序并在两个线程中调用它来验证这一点:
#define trprefix "TracerPid:"
int tracerpid()
{
char stfile[100], buf[512];
sprintf(stfile, "/proc/self/task/%d/status", (int)gettid());
int trpid = -1;
FILE *st = fopen(stfile, "r");
if (st != NULL) {
while (fgets(buf, sizeof buf, st) != NULL) {
if (strncmp(buf, trprefix, strlen(trprefix)) == 0)
trpid = atoi(buf+strlen(trprefix));
}
fclose(st);
}
return trpid;
}
您将看到父线程的跟踪器PID是“附加”进程的跟踪器PID,而子线程的跟踪器PID是0
当子线程引发SIGTRAP
时,线程没有跟踪器,因此将采取SIGTRAP
的默认操作-整个进程将被终止。这就是为什么跟踪程序说waitpid返回了wifsignalid
要解决此问题,请执行以下操作:
- 在“mt”程序中,将调用移动到
to be,使其位于第一个延迟循环之后,这将给您足够的时间在创建新线程之前连接到进程pthread_create
- 将其添加到
ptrace(ptrace\u attach,…)之后的“attach”程序中;waitpid(…)代码>:
errno = 0; ret = ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); printf("setoptions ret=%d err=%s\n", ret, strerror(errno));
选项将允许您的程序跟踪目标通过PTRACE\u O\u TRACECLONE
创建的每个线程clone
- 将所有
转换为waitpid(pid,…)
,以便程序等待任何线程waitpid(-1,…)
.cpp
文件,但代码是C。