对不起,我无法发布代码来重现这个.我的问题正是我不知道如何调试这个问题.
我正在使用ptrace PTRACE_O_TRACEFORK | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACECLONE跟踪一个进程,它是孩子(和孩子们的孩子).机制很像strace,但目的略有不同,因为我只是跟踪读取或修改的文件.
我的代码(用C编写)在x86-64架构上的Debian wheezy和Debian jessie上运行良好(在i386上也经过了较少的测试).当我尝试在Ubuntu Precise x86-64虚拟机(使用3.2.0内核)上编译和运行时,我遇到了麻烦.
在Precise机器上,我有时发现PTRACE_EVENT_VFORK在vfork呼叫发生后我没有立即收到,而是开始接收事件(几个SIGSTOP事件和一些系统调用)而没有得到PTRACE_EVENT_VFORK事件.我没有看到正在执行的系统调用中有任何可疑的行为,并且行为是不可预测的.
我不知道该尝试将其减少到最小的错误情况,我真的不知道可能出现什么问题,从未见过这种丢失事件的行为.可以想象,差异不是内核,而是我正在跟踪的构建工具(这是python + gcc的组合).
有什么建议?
我最近正在从事类似的工作。我怀疑您早就解决了您的问题或放弃了,但是让我们在此为后代写一个答案。
您注册的各种事件会PTRACE_SETOPTIONS生成与正常ptrace事件不同的消息。但是仍会生成正常事件。一个正常的事件是新分支的进程开始停止,必须从跟踪程序继续进行。
这意味着,如果您已注册事件,则在进行分叉后,您观看的事件PTRACE_O_TRACEFORK(或VFORK)waitpid将为同一过程触发两次。
一个将具有以下状态:
WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP)
Run Code Online (Sandbox Code Playgroud)
另一个将与:
WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) &&
((status >> 16) == PTRACE_EVENT_FORK) /* or VFORK */
Run Code Online (Sandbox Code Playgroud)
内核似乎无法保证它们将以什么顺序到达。我发现它在系统上接近50/50。
为了处理这个问题,我的代码如下所示:
static void
proc_register(struct magic *pwi, pid_t pid, bool fork) {
/*
* When a new process starts two things happen:
* - We get a wait with STOPPED, SIGTRAP, PTRACE_EVENT_{CLONE,FORK,VFORK}
* - We get a wait with STOPPED, SIGSTOP
*
* Those can come in any order, so to get the proc in the right
* state this function should be called twice on every new proc. If
* it's called with fork first, we set the state to NEW_FORKED, if
* it's called with STOP first, we set NEW_STOPPED. Then when the
* other call comes, we set the state to TRACED and continue the
* process.
*/
if ((p = find_proc(pwi, pid)) == NULL) {
p = calloc(1, sizeof(*p));
p->pid = pid;
TAILQ_INSERT_TAIL(&pwi->procs, p, list);
if (fork) {
p->state = NEW_FORKED;
} else {
p->state = NEW_STOPPED;
}
} else {
assert((fork && p->state == NEW_STOPPED) || (!fork && p->state == NEW_FORKED));
p->state = TRACED;
int flags = PTRACE_O_TRACEEXEC|PTRACE_O_TRACEEXIT|PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK;
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, flags))
err(1, "ptrace(SETOPTIONS, %d)", pid);
if (ptrace(PTRACE_CONT, pid, NULL, signal) == -1)
err(1, "ptrace(CONT, %d, %d)", pid, signal);
}
}
[...]
pid = waitpid(-1, &status, __WALL);
if (WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP)) {
proc_register(magic, pid, false);
} else if (WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) && ((status >> 16) == PTRACE_EVENT_FORK)) {
proc_register(magic, pid, true);
} else {
/* ... */
}
Run Code Online (Sandbox Code Playgroud)
进行这项工作的关键是在PTRACE_CONT我们收到两个事件之前不要发送。当弄清楚它是如何工作时,我发送PTRACE_CONT了太多信息,内核欣然接受了它们,有时甚至导致我的进程在PTRACE_EVENT_FORK到达之前就退出了。这使得调试非常困难。
注意:我没有找到任何有关此的文档,也没有说这是应该的方法。我刚刚发现,这使事情像今天一样运转。YMMV。