Domanda

Una volta un collega mi ha detto che l'ultima opzione quando tutto non è riuscito a eseguire il debug su Linux era usare strace .

Ho cercato di imparare la scienza dietro questo strano strumento, ma non sono un guru dell'amministratore di sistema e non ho davvero ottenuto risultati.

  • Che cos'è esattamente e cosa fa?
  • Come e in quali casi dovrebbe essere usato?
  • Come deve essere compreso ed elaborato l'output?

In breve, in parole semplici , come funzionano queste cose?

È stato utile?

Soluzione

Panoramica di Strace
lo strace può essere visto come un debugger leggero. Consente a un programmatore / utente di scoprire rapidamente come un programma interagisce con il sistema operativo. Lo fa monitorando le chiamate e i segnali di sistema.

Utilizza
Buono per quando non hai il codice sorgente o non vuoi essere disturbato a esaminarlo davvero.
Inoltre, utile per il tuo codice se non hai voglia di aprire GDB, ma sei solo interessato a comprendere l'interazione esterna.

Una buona piccola introduzione
Mi sono imbattuto in questa introduzione per utilizzare lo strace solo l'altro giorno: strace ciao mondo

Altri suggerimenti

In parole semplici, strace traccia tutte le chiamate di sistema emesse da un programma insieme ai loro codici di ritorno. Pensa a cose come operazioni su file / socket e molto più oscure.

È molto utile se hai qualche conoscenza pratica di C poiché qui le chiamate di sistema rappresenterebbero in modo più accurato le chiamate di libreria C standard.

Supponiamo che il tuo programma sia / usr / local / bin / tosse. Usa semplicemente:

strace /usr/local/bin/cough <any required argument for cough here>

o

strace -o <out_file> /usr/local/bin/cough <any required argument for cough here>

per scrivere in 'out_file'.

Tutto l'output di strace andrà a stderr (attenzione, il volume puro di esso spesso richiede un reindirizzamento a un file). Nei casi più semplici, il tuo programma si interromperà con un errore e sarai in grado di vedere quali sono le sue ultime interazioni con il sistema operativo nell'output di strace.

Ulteriori informazioni dovrebbero essere disponibili con:

man strace

strace elenca tutte le chiamate di sistema effettuate dal processo a cui è applicato. Se non sai cosa significano le chiamate di sistema, non sarai in grado di ottenere molto chilometraggio da esso.

Tuttavia, se il tuo problema riguarda file o percorsi o valori di ambiente, l'esecuzione di strace sul programma problematico e il reindirizzamento dell'output su un file e quindi il grepping di quel file per la tua stringa path / file / env possono aiutarti a vedere qual è il tuo programma in realtà tentando di fare, distinto da quello che ti aspettavi.

Strace si distingue come uno strumento per studiare i sistemi di produzione in cui non è possibile eseguire questi programmi con un debugger. In particolare, abbiamo usato lo strace nelle seguenti due situazioni:

  • Il programma foo sembra essere in stallo ed è diventato insensibile. Questo potrebbe essere un obiettivo per gdb; tuttavia, non abbiamo sempre avuto il codice sorgente o talvolta abbiamo avuto a che fare con linguaggi di script che non erano semplici da eseguire con un debugger. In questo caso, esegui strace su un programma già in esecuzione e otterrai l'elenco delle chiamate di sistema effettuate. Ciò è particolarmente utile se stai studiando un'applicazione client / server o un'applicazione che interagisce con un database
  • Indagare perché un programma è lento. In particolare, ci eravamo appena trasferiti in un nuovo file system distribuito e il nuovo throughput del sistema era molto lento. Puoi specificare lo strace con l'opzione '-T' che ti dirà quanto tempo è stato impiegato in ogni chiamata di sistema. Ciò ha contribuito a determinare il motivo per cui il file system stava rallentando le cose.

Per un esempio di analisi usando strace vedi la mia risposta a questa domanda .

Uso sempre strace per eseguire il debug dei problemi di autorizzazione. La tecnica va così:

$ strace -e trace=open,stat,read,write gnome-calculator

Dove gnome-calcolatore è il comando che si desidera eseguire.

strace -tfp Il PID monitorerà le chiamate di sistema del processo PID, quindi possiamo eseguire il debug / monitorare lo stato del nostro processo / programma.

Strace può essere utilizzato come strumento di debug o come profiler primitivo.

Come debugger, puoi vedere come sono state chiamate, eseguite e quali restituiscono determinate chiamate di sistema. Questo è molto importante, in quanto ti consente di vedere non solo un programma fallito, ma PERCHÉ un programma ha fallito. Di solito è solo il risultato di una pessima codifica che non rileva tutti i possibili risultati di un programma. Altre volte sono solo percorsi codificati per i file. Senza fatica puoi indovinare cosa è andato storto dove e come. Con Strace ottieni una suddivisione di un syscall, di solito solo guardare un valore di ritorno ti dice molto.

La profilazione è un altro uso. Puoi usarlo per cronometrare l'esecuzione di ogni syscalls singolarmente o come aggregato. Anche se questo potrebbe non essere sufficiente per risolvere i tuoi problemi, almeno restringerà notevolmente l'elenco dei potenziali sospetti. Se vedi molte coppie fopen / close su un singolo file, probabilmente apri e chiudi inutilmente i file ogni esecuzione di un ciclo, invece di aprirlo e chiuderlo al di fuori di un ciclo.

Ltrace è il cugino stretto di Strace, anche molto utile. Devi imparare a differenziare dove si trova il collo di bottiglia. Se un'esecuzione totale è di 8 secondi e si spendono solo 0,05 secondi per le chiamate di sistema, quindi la ricerca del programma non ti farà molto bene, il problema è nel tuo codice, che di solito è un problema logico o il programma effettivamente ha bisogno impiegare così tanto tempo a correre.

Il problema più grande con strace / ltrace sta leggendo il loro output. Se non sai come vengono effettuate le chiamate, o almeno i nomi delle chiamate / funzioni, sarà difficile decifrarne il significato. Anche sapere cosa restituiscono le funzioni può essere molto utile, soprattutto per diversi codici di errore. Mentre è un dolore da decifrare, a volte restituiscono davvero una perla di conoscenza; una volta ho visto una situazione in cui ho finito gli inode, ma non lo spazio libero, quindi tutte le solite utility non mi hanno dato alcun avviso, non sono riuscito a creare un nuovo file. Leggere il codice di errore dall'output di strace mi ha indicato la giusta direzione.

Strace è uno strumento che ti dice come l'applicazione interagisce con il tuo sistema operativo.

Lo fa dicendoti quale sistema operativo chiama la tua applicazione e con quali parametri li chiama.

Ad esempio, vedi quali file tenta di aprire il tuo programma e se la chiamata ha esito positivo.

Puoi eseguire il debug di tutti i tipi di problemi con questo strumento. Ad esempio, se l'applicazione dice che non riesce a trovare una libreria che sai di aver installato, Strace ti direbbe dove l'applicazione sta cercando quel file.

E questa è solo una punta dell'iceberg.

strace è un buon strumento per imparare come il tuo programma effettua varie chiamate di sistema (richieste al kernel) e riporta anche quelle che hanno fallito insieme al valore di errore associato a quell'errore. Non tutti i guasti sono bug. Ad esempio, un codice che sta cercando di cercare un file potrebbe visualizzare un errore ENOENT (nessun file o directory del genere) ma potrebbe essere uno scenario accettabile nella logica del codice.

Un buon caso di utilizzo di strace è il debug delle condizioni di gara durante la creazione di file temporanei. Ad esempio, un programma che potrebbe creare file aggiungendo l'ID di processo (PID) a una stringa predeterminata potrebbe incontrare problemi in scenari multi-thread. [Un PID + TID (ID processo + ID thread) o una migliore chiamata di sistema come mkstemp risolveranno questo problema.

È anche utile per il debug degli arresti anomali. Puoi trovare questo (mio) articolo su strace e debug degli arresti anomali utile.

Mi sono piaciute alcune delle risposte in cui si legge strace che controlla come interagisci con il tuo sistema operativo.

Questo è esattamente ciò che possiamo vedere. Il sistema chiama. Se confronti strace e ltrace la differenza è più evidente.

<*>gt;ltrace -c cd
Desktop  Documents  Downloads  examples.desktop  Music  Pictures  Public  Templates  Videos
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 15.52    0.004946         329        15 memcpy
 13.34    0.004249          94        45 __ctype_get_mb_cur_max
 12.87    0.004099        2049         2 fclose
 12.12    0.003861          83        46 strlen
 10.96    0.003491         109        32 __errno_location
 10.37    0.003303         117        28 readdir
  8.41    0.002679         133        20 strcoll
  5.62    0.001791         111        16 __overflow
  3.24    0.001032         114         9 fwrite_unlocked
  1.26    0.000400         100         4 __freading
  1.17    0.000372          41         9 getenv
  0.70    0.000222         111         2 fflush
  0.67    0.000214         107         2 __fpending
  0.64    0.000203         101         2 fileno
  0.62    0.000196         196         1 closedir
  0.43    0.000138         138         1 setlocale
  0.36    0.000114         114         1 _setjmp
  0.31    0.000098          98         1 realloc
  0.25    0.000080          80         1 bindtextdomain
  0.21    0.000068          68         1 opendir
  0.19    0.000062          62         1 strrchr
  0.18    0.000056          56         1 isatty
  0.16    0.000051          51         1 ioctl
  0.15    0.000047          47         1 getopt_long
  0.14    0.000045          45         1 textdomain
  0.13    0.000042          42         1 __cxa_atexit
------ ----------- ----------- --------- --------------------
100.00    0.031859                   244 total
gt;strace -c cd Desktop Documents Downloads examples.desktop Music Pictures Public Templates Videos % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 7 read 0.00 0.000000 0 1 write 0.00 0.000000 0 11 close 0.00 0.000000 0 10 fstat 0.00 0.000000 0 17 mmap 0.00 0.000000 0 12 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 3 brk 0.00 0.000000 0 2 rt_sigaction 0.00 0.000000 0 1 rt_sigprocmask 0.00 0.000000 0 2 ioctl 0.00 0.000000 0 8 8 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 2 getdents 0.00 0.000000 0 2 2 statfs 0.00 0.000000 0 1 arch_prctl 0.00 0.000000 0 1 set_tid_address 0.00 0.000000 0 9 openat 0.00 0.000000 0 1 set_robust_list 0.00 0.000000 0 1 prlimit64 ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 93 10 total

D'altra parte c'è ltrace che traccia le funzioni.

<*>

Anche se ho controllato più volte i manuali, non ho trovato l'origine del nome strace ma è probabilmente la traccia delle chiamate di sistema, poiché questo è ovvio.

Ci sono tre note più grandi da dire su strace .

Nota 1: Entrambe queste funzioni strace e ltrace utilizzano la chiamata di sistema ptrace . Quindi la chiamata di sistema ptrace è efficacemente come funziona strace .

  

La chiamata di sistema ptrace () fornisce un mezzo con cui un processo (il          "Tracciante") può osservare e controllare l'esecuzione di un altro processo          (il "tracee"), ed esaminare e modificare la memoria del tracee e          registri. Viene utilizzato principalmente per implementare il debug di breakpoint          e traccia delle chiamate di sistema.

Nota 2: Esistono diversi parametri che è possibile utilizzare con strace , poiché strace può essere molto dettagliato. Mi piace sperimentare -c che è come un riassunto delle cose. Sulla base di -c puoi selezionare una chiamata di sistema come -e trace = open dove vedrai solo quella chiamata. Questo può essere interessante se stai esaminando quali file verranno aperti durante il comando che stai tracciando. E, naturalmente, puoi usare grep per lo stesso scopo, ma nota che devi reindirizzare in questo modo 2 > & amp; 1 | grep etc per capire che i file di configurazione sono referenziati quando il comando è stato emesso.

Nota 3: trovo questa nota molto importante. Non sei limitato a un'architettura specifica. strace ti lascerà a bocca aperta, poiché può rintracciare binari di architetture diverse. inserisci qui la descrizione dell'immagine

Esempio eseguibile minimo

Se un concetto non è chiaro, c'è un esempio più semplice che non hai visto che lo spiega.

In questo caso, quell'esempio è il ciao mondo Linux x86_64 assembly indipendente (no libc) hello:

hello.s

.text
.global _start
_start:
    /* write */
    mov $1, %rax    /* syscall number */
    mov $1, %rdi    /* stdout */
    mov $msg, %rsi  /* buffer */
    mov $len, %rdx  /* buffer len */
    syscall

    /* exit */
    mov $60, %rax   /* exit status */
    mov 
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
, %rdi /* syscall number */ syscall msg: .ascii "hello\n" len = . - msg

GitH / a>.

Assembla ed esegui:

hello

Emette il previsto:

env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log

Ora usiamo strace su quell'esempio:

execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6)                  = 6
exit(0)                                 = ?
+++ exited with 0 +++

Usiamo:

strace.log ora contiene:

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *msg = "hello\n";
    write(1, msg, 6);
    return 0;
}

Con un esempio così minimo, ogni singolo carattere dell'output è evidente:

  • esegui riga: mostra come strace ha eseguito hello.out , inclusi gli argomenti CLI e l'ambiente come documentato in man execve

  • Riga
  • write : mostra la chiamata di sistema di scrittura che abbiamo effettuato. 6 è la lunghezza della stringa " hello \ n " .

    = 6 è il valore di ritorno della chiamata di sistema, che come documentato in man 2 write è il numero di byte scritti.

  • Riga
  • exit : mostra la chiamata di sistema di uscita che abbiamo effettuato. Non esiste alcun valore restituito, poiché il programma è stato chiuso!

Esempi più complessi

L'applicazione di strace è ovviamente per vedere quale sistema chiama i programmi complessi che stanno effettivamente facendo per aiutare il debug / ottimizzare il tuo programma.

In particolare, la maggior parte delle chiamate di sistema che potresti incontrare in Linux hanno wrapper glibc, molti di loro da POSIX .

Internamente, i wrapper glibc usano l'assemblaggio in linea più o meno in questo modo: Come richiamare una chiamata di sistema tramite sysenter nell'assembly inline?

Il prossimo esempio che dovresti studiare è un POSIX write ciao mondo:

main.c

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

Compila ed esegui:

write(1, "hello\n", 6)                  = 6
exit_group(0)                           = ?
+++ exited with 0 +++

Questa volta, vedrai che un gruppo di chiamate di sistema vengono effettuate da glibc prima di main per configurare un ambiente piacevole per main.

Questo perché non stiamo usando un programma indipendente, ma piuttosto un programma glibc più comune, che consente la funzionalità libc.

Quindi, ad ogni estremità, strace.log contiene:

<*>

Quindi concludiamo che la funzione POSIX write usa, sorpresa !, la chiamata di sistema Linux write .

Osserviamo anche che return 0 porta ad una chiamata exit_group invece di exit . Ah, non sapevo di questo! Questo è il motivo per cui strace è così bello. man exit_group quindi spiega:

  

Questa chiamata di sistema equivale a exit (2) tranne per il fatto che termina non solo il thread chiamante, ma tutti i thread nel gruppo di thread del processo chiamante.

Ed ecco un altro esempio in cui ho studiato quale chiamata di sistema dlopen utilizza: https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in- linux / 462710 # 462710

Testato in Ubuntu 16.04, GCC 6.4.0, kernel Linux 4.4.0.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top