2015-05-02 10 views
10

Mi dispiace che non posso postare il codice per riprodurre questo. Il mio problema è precisamente che non so come risolvere il problema.di tanto in tanto manca PTRACE_EVENT_VFORK durante l'esecuzione ptrace

Sto usando ptrace con PTRACE_O_TRACEFORK | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACECLONE per rintracciare un processo e sono i bambini (ei figli dei bambini). Il meccanismo è molto simile a strace, ma con scopi leggermente diversi, poiché sto semplicemente monitorando i file che vengono letti o modificati.

Il mio codice (scritto in C) funziona bene su Debian wheezy e Debian jessie sull'architettura x86-64 (e anche meno testato su i386). Quando provo a compilare ed eseguire su una macchina virtuale Ubuntu Precision x86-64 (che usa un kernel 3.2.0), ho dei problemi.

Sulla macchina precisa, mi volte scoprire che non ricevo un PTRACE_EVENT_VFORK subito dopo una chiamata vfork accade, ma invece iniziare a ricevere gli eventi (un paio di SIGSTOP eventi, e un paio di chiamate di sistema) senza mai ottenere il PTRACE_EVENT_VFORK evento. Non vedo nulla di sospetto nelle chiamate di sistema eseguite e il comportamento non è prevedibile.

Non sono sicuro di cosa provare a ridurlo a un errore minimo, e non ho davvero idea di cosa potrebbe andare storto, non avendo mai visto prima questo comportamento degli eventi mancanti. È concepibile che la differenza non sia il kernel, ma piuttosto gli strumenti di compilazione che sto tracciando (che è una combinazione di python + gcc).

Qualche suggerimento?

+0

Se nessuno qui può aiutarti, prova a chiedere sulla mailing list linux-kernel. (Difficile da aiutare, ma vale la pena provare.) In alternativa a 'ptrace', potresti usare il [trucco LD_PRELOAD] (http://stackoverflow.com/q/426230/) per intercettare le chiamate a' open', ' leggi', 'scrivi' e' chiudi'. E buona fortuna; questo sembra brutto. – Nemo

+1

Ho evitato LD_PRELOAD, dal momento che voglio che il mio codice sia in grado di tracciare i binari collegati staticamente. E francamente, ho paura del linux-kernel! lol :) –

+0

Accetto LD_PRELOAD non è un modo corretto/valido per farlo. Sfortunatamente non conosco la causa dei guasti alla traccia vfork. Se è possibile utilizzare la modalità di tracciamento seccomp anziché quella legacy ptrace, potrebbe essere meno soggetta a errori e più portabile. –

risposta

2

stavo lavorando su qualcosa di simile di recente. Sospetto che tu abbia risolto il tuo problema molto tempo fa o ti sia arreso, ma scriviamo una risposta qui per i posteri.

I vari eventi registrati con PTRACE_SETOPTIONS generano messaggi diversi dai normali eventi ptrace. Ma gli eventi normali sono ancora generati. Un evento normale è che un processo appena avviato si interrompe e deve essere continuato dal tracciante.

Ciò significa che se si sono registrati eventi che si guardano con PTRACE_O_TRACEFORK (o VFORK) waitpid si attiverà due volte per lo stesso processo dopo una forcella.

Uno sarà con uno stato che è:

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

L'altro sarà con:

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

non sembra essere alcuna garanzia dal kernel in quale ordine lo faranno arrivo. L'ho trovato vicino al 50/50 sul mio sistema.

per gestire questo mio codice simile a questa:

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

la chiave per fare questo lavoro è stato quello di non inviare PTRACE_CONT finché non avremo ricevuto entrambi gli eventi. Quando per capire come funziona inviavo PTRACE_CONT modo troppo e il kernel felicemente li accettavo che a volte ha portato anche ai miei processi in uscita molto prima PTRACE_EVENT_FORK arrivato. Ciò ha reso molto difficile il debug.

N.B. Non ho trovato alcuna documentazione su questo o su qualsiasi cosa dicendo che questo è il modo in cui dovrebbe essere.Ho appena scoperto che questo fa funzionare le cose come sono oggi. YMMV.

Problemi correlati