235 votes

Déterminer la ligne de code qui provoque un défaut de segmentation ?

Comment détermine-t-on où se trouve l'erreur dans le code qui cause un défaut de segmentation ?

Est-ce que mon compilateur ( gcc ) montrent l'emplacement de la faute dans le programme ?

326voto

nc3b Points 6704

GCC ne peut pas le faire, mais GDB (un logiciel de gestion de l'information) peut le faire. Débogueur ) peut certainement le faire. Compilez votre programme en utilisant la fonction -g interrupteur, comme ceci :

gcc program.c -g

Utilisez ensuite gdb :

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

Aquí est un bon tutoriel pour vous aider à démarrer avec GDB.

L'endroit où se produit la ségrégation n'est généralement qu'un indice de l'endroit où se trouve "l'erreur qui la cause" dans le code. L'emplacement donné n'est pas nécessairement celui où réside le problème.

81voto

jwkpiano1 Points 61

En outre, vous pouvez donner valgrind un essai : si vous installez valgrind et exécuter

valgrind --leak-check=full <program>

puis il exécutera votre programme et affichera les traces de pile pour toute erreur de segmentation, ainsi que toute lecture ou écriture de mémoire invalide et toute fuite de mémoire. C'est vraiment très utile.

25voto

Lucas Points 5723

Vous pouvez également utiliser un dump du noyau et l'examiner avec gdb. Pour obtenir des informations utiles, vous devez également compiler avec l'option -g drapeau.

Chaque fois que vous recevez le message :

 Segmentation fault (core dumped)

un fichier de base est écrit dans votre répertoire courant. Et vous pouvez l'examiner avec la commande

 gdb your_program core_file

Le fichier contient l'état de la mémoire lorsque le programme s'est écrasé. Un core dump peut être utile lors du déploiement de votre logiciel.

Assurez-vous que votre système ne fixe pas la taille du fichier core dump à zéro. Vous pouvez la définir comme illimitée avec :

ulimit -c unlimited

Mais attention, les vidages de noyau peuvent devenir énormes.

24voto

asynts Points 853

Il existe un certain nombre d'outils disponibles qui aident à déboguer les défauts de segmentation et je voudrais ajouter mon outil préféré à la liste : Assainisseurs d'adresses (souvent abrégés en ASAN) .

Les compilateurs modernes¹ sont livrés avec l'outil pratique -fsanitize=address ce qui ajoute un peu de temps de compilation et de temps d'exécution pour vérifier davantage les erreurs.

Selon la documentation ces vérifications incluent la capture des défauts de segmentation par défaut. L'avantage ici est que vous obtenez une trace de la pile similaire à la sortie de gdb, mais sans exécuter le programme dans un débogueur. Un exemple :

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

La sortie est légèrement plus compliquée que celle de gdb, mais il y a des avantages :

  • Il n'est pas nécessaire de reproduire le problème pour recevoir une trace de la pile. Il suffit d'activer l'indicateur pendant le développement.

  • Les ASAN détectent bien plus que les défauts de segmentation. De nombreux accès hors limites seront détectés même si cette zone de mémoire était accessible au processus.


C'est-à-dire Clang 3.1+ y GCC 4.8+. .

7voto

Jeremy Friesner Points 16684

Toutes les réponses ci-dessus sont correctes et recommandées ; cette réponse n'est qu'une solution de dernier recours si aucune des approches susmentionnées ne peut être utilisée.

Si tout échoue, vous pouvez toujours recompiler votre programme avec diverses instructions temporaires de débogage et d'impression (par ex. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__); ) parsemés dans ce que vous pensez être les parties pertinentes de votre code. Ensuite, exécutez le programme, et observez ce que le dernier debug-print a imprimé juste avant que le crash ne se produise - vous savez que votre programme est allé jusque là, donc le crash a dû se produire après ce point. Ajoutez ou supprimez des empreintes de débogage, recompilez et exécutez à nouveau le test, jusqu'à ce que vous ayez réduit le problème à une seule ligne de code. À ce stade, vous pouvez corriger le bogue et supprimer toutes les empreintes de débogage temporaires.

C'est assez fastidieux, mais cela a l'avantage de fonctionner à peu près partout -- les seules fois où cela pourrait ne pas fonctionner sont si vous n'avez pas accès à stdout ou stderr pour une raison quelconque, ou si le bogue que vous essayez de corriger est une condition de course dont le comportement change lorsque le timing du programme change (puisque les debug-prints ralentiront le programme et changeront son timing).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X