Valgrind
Valgrind
Valgrind est un puissant outil de débogage qui permet de détecter des problèmes de mémoire. Il peut trouver des fuites de mémoire (memory leak), mais aussi des accès à de la mémoire non allouée ou désallouée, des désallocations multiples et autres. Si vous avez une erreur de segmentation (segmentation fault), broken pipe ou bus error lorsque vous exécutez votre programme, vous avez vraisemblablement un problème avec l'utilisation de la mémoire. Valgrind fait partie des logiciels de base installés sur les grappes de Calcul Canada; il peut donc être utilisé sans avoir à charger un module.
Instructions AVX-512
En date de décembre 2020, les versions actuelles ne peuvent pas utiliser les instructions AVX-512 qu'on trouve sur les plus récents processeurs Intel et un message semblable sera affiché :
vex amd64->IR: unhandled instruction bytes: 0x62 0xF1 0xFE 0x8 0x6F 0x8B 0xE8 0xFF 0xFF 0xFF
vex amd64->IR: REX=0 REX.W=0 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR: VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR: PFX.66=0 PFX.F2=0 PFX.F3=0
==35839== valgrind: Unrecognised instruction at address 0x4e68448.
==35839== at 0x4E68448: if_posix_open (in /cvmfs/soft.computecanada.ca/easybuild/software/2020/avx512/Compiler/gcc9/openmpi/4.0.3/lib/libopen-pal.so.40.20.3)
==35839== by 0x4E2C44A: mca_base_framework_components_open (in /cvmfs/soft.computecanada.ca/easybuild/software/2020/avx512/Compiler/gcc9/openmpi/4.0.3/lib/libopen-pal.so.40.20.3)
...
Prenez note que l'environnement par défaut sur Béluga utilise les instructions AVX-2. Pour contourner ce problème, chargez d'abord l'environnement AVX-2 avec
[name@server ~]$ module load arch/avx2
puis recompilez votre application à partir de zéro pour vous assurer que le binaire ne contient aucune de ces instructions.
Préparer votre programme
Pour extraire de l'information utile, vous devez d'abord compiler votre code avec les informations de débogage. Avec la plupart des compilateurs, vous devez pour ce faire ajouter l'option -g à la compilation.
Certaines optimisations très pointues qui génèrent des opérations non reconnues peuvent aussi générer des erreurs lorsque vous exécutez Valgrind; c’est le cas notamment avec certaines bibliothèques mathématiques. Puisque vous ne voulez pas diagnostiquer des problèmes de mémoire dans la bibliothèque, mais dans votre propre code, nous vous recommandons de compiler votre code en liant sur une version non optimisée des bibliothèques qui n’utilise pas ces opérations, par exemple sur l’implémentation Netlib de BLAS et LAPACK. Ceci peut servir à diagnostiquer les problèmes, mais liez sur des bibliothèques optimisées quand vient le temps d’exécuter les simulations.
Utilisation
Lorsque votre programme est compilé avec les bonnes options, exécutez-le dans Valgrind ainsi :
[name@server ~]$ valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./your_program
Pour plus d'information, nous vous conseillons cette page.
Quelques conseils
- Lorsque vous utilisez Valgrind, votre code s'exécute dans une instance qui valide tous les accès à la mémoire et il s'exécutera donc beaucoup plus lentement. Pour effectuer un test, choisissez un problème plus petit que ce que vous exécuteriez normalement.
- Il n'est pas nécessaire que votre programme se termine avec une erreur de segmentation ou autre afin que Valgrind puisse détecter des erreurs. Très fréquemment, des problèmes mineurs d'accès mémoire, par exemple la lecture d’un seul élément au-delà des limites d'un tableau, passent inaperçus alors que les problèmes plus graves causent une erreur de segmentation.
Quelques messages typiques
Voici quelques problèmes que Valgrind peut détecter et les messages d'erreur qu'il produit.
Fuites de mémoire
Le message d'erreur pour une fuite de mémoire sera produit à la fin de l'exécution du programme et ressemblera à ceci :
==2116== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2116== at 0x1B900DD0: malloc (vg_replace_malloc.c:131)
==2116== by 0x804840F: main (in /home/cprogram/example1)
Utilisation non valide de pointeurs et dépassement de bornes
Si vous tentez d'écrire dans un pointeur non alloué, ou en dehors des bornes de mémoire allouées, le message d'erreur ressemblera à ceci :
==9814== Invalid write of size 1
==9814== at 0x804841E: main (example2.c:6)
==9814== Address 0x1BA3607A is 0 bytes after a block of size 10 alloc'd
==9814== at 0x1B900DD0: malloc (vg_replace_malloc.c:131)
==9814== by 0x804840F: main (example2.c:5)
Utilisation de variables non initialisées
Si vous utilisez une variable non initialisée, le message d'erreur sera le suivant :
==17943== Conditional jump or move depends on uninitialised value(s)
==17943== at 0x804840A: main (example3.c:6)