Come dovrebbe essere usato lo strace?
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?
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.
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
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:
-
env -i ASDF = qwer
per controllare le variabili di ambiente: https://unix.stackexchange.com/questions/48994/how-to-run-a-program-in-a-clean-environment-in-bash -
-s999 -v
per mostrare informazioni più complete sui registri
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 comestrace
ha eseguitohello.out
, inclusi gli argomenti CLI e l'ambiente come documentato inman 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 inman 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.