Determinare la riga di codice che causa un errore di segmentazione?


151

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?


5
Nessun gcc / gdb non può. Puoi scoprire dove si è verificato il segfault, ma l'errore reale potrebbe trovarsi in una posizione totalmente diversa.

Risposte:


218

GCC non può farlo, ma GDB (un debugger ) sicuramente può farlo . Compila il tuo programma usando l' -ginterruttore, 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.


28
Si noti che dove si verifica il segfault è generalmente solo un indizio su dove "l'errore che causa" è nel codice. Un indizio importante, ma non è necessariamente dove risiede il problema.
mpez0

9
Puoi anche usare (bt completo) per ottenere maggiori dettagli.
ant2009,


2
Utilizzare btcome scorciatoia per backtrace.
Rustyx,

43

Inoltre, puoi valgrindprovare: se installi valgrinded 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.


2
+1, Valgrind è molto più veloce / facile da usare per individuare errori di memoria. Su build non ottimizzate con simboli di debug, ti dice esattamente dove si è verificato un segfault e perché.
Tim Post

1
Purtroppo il mio segfault scompare durante la compilazione con -g -O0 e combinato con valgrind.
JohnMudd,

2
--leak-check=fullnon aiuterà a eseguire il debug dei segfault. È utile solo per il debug delle perdite di memoria.
ks1322,

@JohnMudd Ho un segfault visualizzato solo circa l'1% dei file di input testati, se ripeti l'input fallito non fallirà. Il mio problema è stato causato dal multithreading. Finora non ho capito la linea di codice che causa questo problema. Per il momento sto usando un nuovo tentativo per nascondere questo problema. Se si utilizza l'opzione -g, l'errore scompare!
Kemin Zhou

18

Puoi anche usare un dump core e quindi esaminarlo con gdb. Per ottenere informazioni utili è inoltre necessario compilare con la -gbandiera.

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 passato ad arch-linux di recente. La mia directory corrente non contiene il file di dump principale. Come posso generarlo?
Abhinav,

Non lo generi; Linux lo fa. I dump core vengono archiviati in posizioni diverse su Linuces diverso - Google around. Per Arch Linux, leggi questo wiki.archlinux.org/index.php/Core_dump
Mawg dice di ripristinare Monica

7

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=addressflag, 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+ .


Questo mi è molto utile. Ho un bug molto sottile che si verifica casualmente con una frequenza dell'1% circa. Elaboro un gran numero di file di input con (16 passaggi principali; ognuno eseguito da un binario C o C ++ diverso). Un passaggio successivo attiverà l'errore di segmentazione solo in modo casuale a causa del multi-threading. È difficile eseguire il debug. Questa opzione ha attivato l'output delle informazioni di debug almeno mi ha dato un punto di partenza per la revisione del codice per trovare la posizione del bug.
Kemin Zhou,

2

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.


4
e non dimenticare ulimit -c unlimiteddi abilitare i core dump in primo luogo.
James Morris,

@James: corretto. Lucas l'ha già menzionato. E per quelli di noi che sono ancora bloccati nel csh, usare 'limite'. E non sono mai stato in grado di leggere gli stackdump di CYGWIN (ma non ci provo da 2 o 3 anni).
Joseph Quinsey,

2

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)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.