Come si determina dove si trova l'errore nel codice che causa un errore di segmentazione ?
Il mio compilatore ( gcc
) può mostrare la posizione dell'errore nel programma?
Come si determina dove si trova l'errore nel codice che causa un errore di segmentazione ?
Il mio compilatore ( gcc
) può mostrare la posizione dell'errore nel programma?
Risposte:
GCC non può farlo, ma GDB (un debugger ) sicuramente può farlo . Compila il tuo programma usando l' -g
interruttore, in questo modo:
gcc program.c -g
Quindi usa gdb:
$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
Ecco un bel tutorial per iniziare con GDB.
Dove si verifica il segfault è generalmente solo un indizio su dove "l'errore che causa" è nel codice. La posizione indicata non è necessariamente dove si trova il problema.
bt
come scorciatoia per backtrace
.
Inoltre, puoi valgrind
provare: se installi valgrind
ed esegui
valgrind --leak-check=full <program>
quindi eseguirà il programma e visualizzerà le tracce dello stack per eventuali segfault, nonché eventuali letture o scritture di memoria non valide e perdite di memoria. È davvero molto utile.
--leak-check=full
non aiuterà a eseguire il debug dei segfault. È utile solo per il debug delle perdite di memoria.
Puoi anche usare un dump core e quindi esaminarlo con gdb. Per ottenere informazioni utili è inoltre necessario compilare con la -g
bandiera.
Ogni volta che ricevi il messaggio:
Segmentation fault (core dumped)
un file core viene scritto nella directory corrente. E puoi esaminarlo con il comando
gdb your_program core_file
Il file contiene lo stato della memoria in caso di arresto anomalo del programma. Un dump principale può essere utile durante la distribuzione del software.
Assicurarsi che il sistema non imposti la dimensione del file di dump principale su zero. Puoi impostarlo su illimitato con:
ulimit -c unlimited
Attenzione però! che i core dump possono diventare enormi.
Sono disponibili numerosi strumenti che aiutano a eseguire il debug degli errori di segmentazione e vorrei aggiungere il mio strumento preferito all'elenco: Disinfettante per indirizzi (spesso abbreviato ASAN) .
I compilatori moderni¹ sono dotati di un comodo -fsanitize=address
flag, che aggiunge un po 'di tempo di compilazione e sovraccarico del tempo di esecuzione, il che comporta un maggiore controllo degli errori.
Secondo la documentazione, questi controlli includono la rilevazione di errori di segmentazione per impostazione predefinita. Il vantaggio qui è che si ottiene una traccia dello stack simile all'output di gdb, ma senza eseguire il programma all'interno di un debugger. Un esempio:
int main() {
volatile int *ptr = (int*)0;
*ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
#0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
#1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
#2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING
L'output è leggermente più complicato di quello che gdb avrebbe prodotto ma ci sono aspetti positivi:
Non è necessario riprodurre il problema per ricevere una traccia dello stack. È sufficiente abilitare la bandiera durante lo sviluppo.
Gli ASAN catturano molto di più dei semplici errori di segmentazione. Molti accessi fuori limite verranno catturati anche se l'area di memoria era accessibile al processo.
¹ Questo è Clang 3.1+ e GCC 4.8+ .
La risposta di Lucas sui core dump è buona. Nel mio .cshrc ho:
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
per visualizzare il backtrace inserendo 'core'. E il timbro della data, per essere sicuro di guardare il file giusto :(.
Aggiunto : se esiste un bug di corruzione dello stack , il backtrace applicato al dump principale è spesso spazzatura. In questo caso, l'esecuzione del programma all'interno di gdb può dare risultati migliori, secondo la risposta accettata (supponendo che l'errore sia facilmente riproducibile). E attenzione anche a più processi che scaricano core simultaneamente; alcuni SO aggiungono il PID al nome del file core.
ulimit -c unlimited
di abilitare i core dump in primo luogo.
Tutte le risposte di cui sopra sono corrette e consigliate; questa risposta è intesa solo come ultima risorsa se nessuno dei suddetti approcci può essere utilizzato.
Se tutto il resto fallisce, puoi sempre ricompilare il tuo programma con varie dichiarazioni temporanee di debug-print (es. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
) Sparse in quelle che ritieni siano le parti rilevanti del tuo codice. Quindi esegui il programma e osserva quale è stata l'ultima stampa di debug stampata poco prima che si verificasse l'incidente: sai che il tuo programma è arrivato così lontano, quindi l'incidente deve essere avvenuto dopo quel punto. Aggiungi o rimuovi debug-print, ricompila ed esegui di nuovo il test, fino a quando non lo hai ridotto a una singola riga di codice. A quel punto è possibile correggere il bug e rimuovere tutte le stampe di debug temporanee.
È abbastanza noioso, ma ha il vantaggio di lavorare praticamente ovunque - le uniche volte potrebbe non essere se non hai accesso a stdout o stderr per qualche motivo, o se il bug che stai cercando di correggere è una gara -condizione il cui comportamento cambia quando cambiano i tempi del programma (poiché le stampe di debug rallentano il programma e cambiano i suoi tempi)