Ответ 1
Недавно я работал над чем-то похожим. Я подозреваю, что вы давно решили свою проблему или сдались, но напишите здесь ответ для потомков.
Различные события, которые вы регистрируете с помощью PTRACE_SETOPTIONS
, генерируют сообщения, отличные от обычных событий ptrace
. Но нормальные события все еще сгенерированы. Одним из обычных событий является то, что новый разветвленный процесс начинает останавливаться и должен быть продолжен из трассировщика.
Это означает, что если вы зарегистрировали события, которые вы смотрите с помощью PTRACE_O_TRACEFORK
(или VFORK) waitpid
, вы будете запускать дважды для одного и того же процесса после вилки.
Один будет со статусом, который:
WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP)
Другой будет:
WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) &&
((status >> 16) == PTRACE_EVENT_FORK) /* or VFORK */
Кажется, нет гарантии от ядра, в каком порядке они будут поступать. Я обнаружил, что он близок к 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 called with fork first, we set the state to NEW_FORKED, if
* it 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 {
/* ... */
}
Ключом к выполнению этой работы было не отправлять PTRACE_CONT
, пока мы не получим оба события. Когда я выяснял, как это работает, я слишком много отправлял PTRACE_CONT
, и ядро с радостью принимало их, что иногда приводило к тому, что мои процессы выходили задолго до того, как PTRACE_EVENT_FORK
прибыл. Это затруднило отладку.
N.B. Я не нашел никакой документации об этом или что-либо, говоря, что так оно и должно быть. Я только узнал, что это заставляет работать так, как сегодня. YMMV.