2015-05-02 17 views
10

Es tut mir leid, dass ich Code nicht zur Verfügung stellen kann, um dies zu reproduzieren. Mein Problem ist gerade, dass ich nicht weiß, wie man dieses Problem debuggt.gelegentlich fehlt PTRACE_EVENT_VFORK bei der Ausführung von Ptrace

Ich benutze ptrace mit PTRACE_O_TRACEFORK | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACECLONE, um einen Prozess zu verfolgen, und es ist Kinder (und die Kinder der Kinder). Der Mechanismus ist ähnlich wie strace, aber mit etwas anderen Zwecken, da ich nur Dateien lese, die gelesen oder geändert werden.

Mein Code (in C geschrieben) funktioniert gut auf Debian Wheezy und Debian Jessie auf der x86-64-Architektur (und auch weniger getestet auf i386). Wenn ich versuche, auf einer Ubuntu Precise x86-64 virtuellen Maschine (die einen Kernel 3.2.0 verwendet) zu kompilieren und zu laufen, stoße ich in Schwierigkeiten.

Auf der Precise Maschine, ich manchmal feststellen, dass ich nicht erhalten eine PTRACE_EVENT_VFORK unmittelbar nach einem vfork Aufruf geschieht, sondern beginnen Ereignisse empfangen (ein paar SIGSTOP Ereignisse und einige Systemaufrufe), ohne jemals die immer PTRACE_EVENT_VFORK Ereignis. Ich sehe nichts Verdächtiges in den Systemaufrufen, die durchgeführt werden, und das Verhalten ist nicht vorhersehbar.

Ich bin mir nicht sicher, was ich versuchen sollte, dies auf einen minimalen Fehlerfall zu reduzieren, und ich habe wirklich keine Ahnung, was schief gehen könnte, noch nie zuvor dieses Verhalten von fehlenden Ereignissen gesehen. Es ist denkbar, dass der Unterschied nicht der Kernel ist, sondern die Build-Tools, die ich verfolge (was eine Kombination von python + gcc ist).

Irgendwelche Vorschläge?

+0

Wenn hier niemand helfen kann, fragen Sie auf der Linux-Kernel-Mailingliste. (Unwahrscheinlich zu helfen, aber einen Versuch wert.) Als Alternative zu 'ptrace' könnten Sie den [LD_PRELOAD-Trick] (http://stackoverflow.com/q/426230/) verwenden, um Aufrufe von' open', 'abzufangen Lesen ',' Schreiben' und 'Schließen'. Und viel Glück; Das hört sich ekelhaft an. – Nemo

+1

Ich habe LD_PRELOAD vermieden, da ich möchte, dass mein Code statisch verknüpfte Binärdateien verfolgen kann. Und ehrlich gesagt habe ich Angst vor Linux-Kernel! lol :) –

+0

Ich stimme zu LD_PRELOAD ist kein vernünftiger/gültiger Weg, dies zu tun. Leider kenne ich die Ursache für die Vfork-Trace-Fehler nicht. Wenn Sie den seccomp-Verfolgungsmodus anstelle des herkömmlichen ptrace-Stils verwenden können, ist er möglicherweise weniger fehleranfällig und portabler. –

Antwort

2

Ich arbeitete vor kurzem an etwas ähnlichem. Ich vermute, du hast dein Problem schon lange gelöst oder aufgegeben, aber lasst uns hier eine Antwort für die Nachwelt schreiben.

Die verschiedenen Ereignisse, die Sie mit PTRACE_SETOPTIONS registrieren, generieren Nachrichten, die sich von den normalen Ereignissen ptrace unterscheiden. Aber die normalen Ereignisse werden noch generiert. Ein normales Ereignis ist, dass ein neu verzweigter Prozess angehalten wird und vom Tracer fortgesetzt werden muss.

Das bedeutet, wenn Sie Ereignisse registriert haben, die Sie mit PTRACE_O_TRACEFORK (oder VFORK) sehen, wird waitpid zweimal nach einem Fork für denselben Prozess ausgelöst.

Man wird mit einem Status sein, der:

WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP) 

Der andere wird mit:

WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) && 
    ((status >> 16) == PTRACE_EVENT_FORK) /* or VFORK */ 

Es scheint keine Garantie aus dem Kernel zu sein, in welcher Reihenfolge sie ankommen. Ich fand es fast 50/50 auf meinem System.

dies mein Code so etwas behandeln aussieht:

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 { 
      /* ... */ 
    } 

Der Schlüssel zu dieser Arbeit nicht PTRACE_CONT senden sollte, bis wir beiden Events zu erhalten. Als ich herausgefunden habe, wie das funktioniert, habe ich PTRACE_CONT viel zu viel gesendet und der Kernel hat sie glücklich akzeptiert, was manchmal sogar dazu geführt hat, dass meine Prozesse lange vor dem Eintreffen von PTRACE_EVENT_FORK beendet wurden. Dies machte es ziemlich schwer zu debuggen.

N.B. Ich habe keine Dokumentation darüber oder irgendetwas gefunden, das sagt, dass es so ist, wie es sein sollte.Ich habe gerade herausgefunden, dass die Dinge so funktionieren, wie es heute ist. YMMV.