Vous utilisez valgrind pour tester votre programme compilé , pas le code source.
Tony
6
La réponse donnée ci-dessous par @RageD est correcte, pourquoi ne l'acceptez-vous pas?
Pratik Singhal
1
Une fuite est causée par quelque chose que vous ne faites pas - c.-à-d. mémoire allouée libre. Par conséquent, Valgrind ne peut pas vous montrer «où» se trouve la fuite - vous seul savez où la mémoire allouée n'est plus nécessaire. Cependant, en vous indiquant quelle allocation n'est pas libre () d, en traçant l'utilisation de cette mémoire à travers votre programme, vous devriez être en mesure de déterminer où elle devrait obtenir free () d. Une erreur courante consiste à quitter une fonction par erreur sans libérer la mémoire allouée.
Pas pour insulter l'OP, mais pour ceux qui viennent à cette question et sont encore nouveaux sur Linux - vous devrez peut-être installer Valgrind sur votre système.
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
Valgrind est facilement utilisable pour le code C / C ++, mais peut même être utilisé pour d'autres langages lorsqu'il est configuré correctement (voir ceci pour Python).
Pour exécuter Valgrind , passez l'exécutable en argument (avec tous les paramètres au programme).
--leak-check=full: "chaque fuite individuelle sera montrée en détail"
--show-leak-kinds=all: Affiche tous les types de fuites "définies, indirectes, possibles, atteignables" dans le rapport "complet".
--track-origins=yes: Préférez la sortie utile à la vitesse. Cela permet de suivre les origines des valeurs non initialisées, ce qui peut être très utile pour les erreurs de mémoire. Pensez à l'éteindre si Valgrind est trop lent.
--verbose: Peut vous informer du comportement inhabituel de votre programme. Répétez pour plus de verbosité.
--log-file: Ecrire dans un fichier. Utile lorsque la sortie dépasse l'espace du terminal.
Enfin, vous aimeriez voir un rapport Valgrind qui ressemble à ceci:
HEAP SUMMARY:
in use at exit:0 bytes in 0 blocks
total heap usage:636 allocs,636 frees,25,393 bytes allocatedAll heap blocks were freed -- no leaks are possible
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
J'ai une fuite, mais OÙ ?
Donc, vous avez une fuite de mémoire et Valgrind ne dit rien de significatif. Peut-être quelque chose comme ça:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
Jetons un coup d'œil au code C que j'ai écrit aussi:
#include<stdlib.h>int main(){char* string = malloc(5*sizeof(char));//LEAK: not freed!return0;}
Eh bien, il y a eu 5 octets perdus. Comment est-ce arrivé? Le rapport d'erreur dit simplement
mainet malloc. Dans un programme plus vaste, ce serait très difficile à traquer. Cela est dû à la manière dont l'exécutable a été compilé . Nous pouvons en fait obtenir des détails ligne par ligne sur ce qui n'a pas fonctionné. Recompilez votre programme avec un indicateur de débogage (j'utilise gccici):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall-ggdb3 main.c # add -ggdb3 to it
Maintenant, avec cette version de débogage, Valgrind pointe vers la ligne exacte de code
allouant la mémoire qui a été divulguée! (Le libellé est important: ce n'est peut-être pas exactement où se trouve votre fuite, mais ce qui a été divulgué. La trace vous aide à trouver
où .)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
Techniques de débogage des fuites et des erreurs de mémoire
Utilisez www.cplusplus.com ! Il a une excellente documentation sur les fonctions C / C ++.
Conseils généraux pour les fuites de mémoire:
Assurez-vous que votre mémoire allouée dynamiquement est effectivement libérée.
N'allouez pas de mémoire et oubliez d'attribuer le pointeur.
N'écrasez pas un pointeur par un nouveau à moins que l'ancienne mémoire ne soit libérée.
Conseils généraux pour les erreurs de mémoire:
Accédez et écrivez aux adresses et aux index dont vous êtes sûr de vous appartenir. Les erreurs de mémoire sont différentes des fuites; ce ne sont souvent que IndexOutOfBoundsException
des problèmes de type.
N'accédez pas ou n'écrivez pas dans la mémoire après l'avoir libérée.
Parfois, vos fuites / erreurs peuvent être liées les unes aux autres, un peu comme un IDE découvrant que vous n'avez pas encore tapé de parenthèse fermante. La résolution d'un problème peut en résoudre d'autres, alors recherchez-en un qui semble être un bon coupable et appliquez certaines de ces idées:
Énumérez les fonctions de votre code qui dépendent / dépendent du code «offensant» qui a l'erreur de mémoire. Suivez l'exécution du programme (peut-être même peut- gdbêtre) et recherchez les erreurs de précondition / postcondition. L'idée est de suivre l'exécution de votre programme tout en se concentrant sur la durée de vie de la mémoire allouée.
Essayez de commenter le bloc de code "offensant" (dans des limites raisonnables, afin que votre code se compile toujours). Si l'erreur Valgrind disparaît, vous avez trouvé où elle se trouve.
Si tout le reste échoue, essayez de le rechercher. Valgrind a aussi de la documentation !
Un regard sur les fuites et les erreurs courantes
Surveillez vos pointeurs
60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
En tant qu'assistant d'enseignement, j'ai souvent vu cette erreur. L'élève utilise une variable locale et oublie de mettre à jour le pointeur d'origine. L'erreur ici est de remarquer que reallocpeut en fait déplacer la mémoire allouée ailleurs et changer l'emplacement du pointeur. Nous partons ensuite resizeArraysans dire
array->dataoù le tableau a été déplacé.
Écriture non valide
1 errors in context 1 of 1:Invalid write of size 1
at 0x4005CA: main (main.c:10)Address0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
Et le code:
#include<stdlib.h>#include<stdint.h>int main(){char* alphabet = calloc(26,sizeof(char));for(uint8_t i =0; i <26; i++){*(alphabet + i)='A'+ i;}*(alphabet +26)='\0';//null-terminate the string?
free(alphabet);return0;}
Notez que Valgrind nous indique la ligne de code commentée ci-dessus. Le tableau de taille 26 est indexé [0,25], c'est pourquoi il *(alphabet + 26)s'agit d'une écriture invalide - elle est hors limites. Une écriture non valide est un résultat courant d'erreurs hors par un. Regardez le côté gauche de votre opération d'affectation.
Lecture non valide
1 errors in context 1 of 1:Invalid read of size 1
at 0x400602: main (main.c:9)Address0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
Et le code:
#include<stdlib.h>#include<stdint.h>int main(){char* destination = calloc(27,sizeof(char));char* source = malloc(26*sizeof(char));for(uint8_t i =0; i <27; i++){*(destination + i)=*(source + i);//Look at the last iteration.}
free(destination);
free(source);return0;}
Valgrind nous indique la ligne commentée ci-dessus. Regardez la dernière itération ici, qui est *(destination + 26) = *(source + 26);. Cependant, *(source + 26)est à nouveau hors limites, de la même manière que l'écriture invalide. Les lectures non valides sont également un résultat courant d'erreurs off-by-one. Regardez le côté droit de votre opération d'affectation.
Les topia Open Source (U / Dys)
Comment savoir si la fuite est à moi? Comment trouver ma fuite lorsque j'utilise le code de quelqu'un d'autre? J'ai trouvé une fuite qui n'est pas la mienne; dois-je faire quelque chose? Ce sont toutes des questions légitimes. Tout d'abord, 2 exemples du monde réel qui montrent 2 classes de rencontres communes.
#include<jansson.h>#include<stdio.h>int main(){char* string ="{ \"key\": \"value\" }";json_error_t error;json_t* root = json_loads(string,0,&error);//obtaining a pointerjson_t* value = json_object_get(root,"key");//obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value));//use value
json_decref(value);//Do I free this pointer?
json_decref(root);//What about this one? Does the order matter?return0;}
C'est un programme simple: il lit une chaîne JSON et l'analyse. Dans la fabrication, nous utilisons des appels de bibliothèque pour faire l'analyse pour nous. Jansson effectue les allocations nécessaires de manière dynamique car JSON peut contenir des structures imbriquées de lui-même. Cependant, cela ne signifie pas que nous decrefou «libérons» la mémoire qui nous est donnée de chaque fonction. En fait, ce code que j'ai écrit ci-dessus lance à la fois une "lecture invalide" et une "écriture invalide". Ces erreurs disparaissent lorsque vous supprimez la decrefligne pour value.
Pourquoi? La variable valueest considérée comme une "référence empruntée" dans l'API Jansson. Jansson garde une trace de sa mémoire pour vous, et vous avez simplement decref
des structures JSON indépendantes les unes des autres. La leçon ici:
lisez la documentation . Vraiment. C'est parfois difficile à comprendre, mais ils vous expliquent pourquoi ces choses se produisent. Au lieu de cela, nous avons
des questions existantes sur cette erreur de mémoire.
Quel est le problème avec ce code ? Il fuit constamment ~ 212 Kio de mémoire pour moi. Prenez un moment pour y réfléchir. Nous activons puis désactivons SDL. Répondre? Il n'y a rien de mal.
Cela peut sembler bizarre au début . À vrai dire, les graphiques sont désordonnés et parfois vous devez accepter certaines fuites comme faisant partie de la bibliothèque standard. La leçon ici: vous n'avez pas besoin d'étouffer toutes les fuites de mémoire . Parfois, vous devez simplement supprimer les fuites, car ce sont des problèmes connus pour lesquels vous ne pouvez rien faire . (Ce n'est pas ma permission d'ignorer vos propres fuites!)
Réponses au vide
Comment savoir si la fuite est la mienne?
C'est. (99% sûr, de toute façon)
Comment trouver ma fuite lorsque j'utilise le code de quelqu'un d'autre?
Il y a de fortes chances que quelqu'un d'autre l'ait déjà trouvé. Essayez Google! Si cela échoue, utilisez les compétences que je vous ai données ci-dessus. Si cela échoue et que vous voyez principalement des appels d'API et peu de votre propre trace de pile, consultez la question suivante.
J'ai trouvé une fuite qui n'est pas la mienne; dois-je faire quelque chose?
Oui! La plupart des API ont des moyens de signaler les bogues et les problèmes. Utilise les! Aidez à redonner aux outils que vous utilisez dans votre projet!
Lectures complémentaires
Merci d'être resté avec moi aussi longtemps. J'espère que vous avez appris quelque chose, en essayant de m'occuper du large éventail de personnes qui arrivent à cette réponse. J'espère que vous avez demandé certaines choses en cours de route: Comment fonctionne l'allocateur de mémoire de C? Qu'est-ce qu'une fuite de mémoire et une erreur de mémoire? En quoi sont-ils différents des segfaults? Comment fonctionne Valgrind? Si vous en aviez, nourrissez votre curiosité:
Bien meilleure réponse, dommage que ce ne soit pas la réponse acceptée.
A. Smoliak
Je crois que c'est une bonne pratique de faire une telle chose, j'en ai fait quelques-unes moi
A. Smoliak
1
Puis-je marquer cette réponse et l'utiliser comme référence future pour moi-même? Bon travail!
Zap
l' memcheckoutil est-il activé par défaut?
abhiarora le
@abhiarora Oui. La page de manuel nous indique que memcheckc'est l'outil par défaut:--tool=<toolname> [default: memcheck]
Joshua Detwiler
146
Essaye ça:
valgrind --leak-check=full -v ./your_program
Tant que valgrind est installé, il passera par votre programme et vous dira ce qui ne va pas. Il peut vous donner des indications et des endroits approximatifs où vos fuites peuvent être trouvées. Si vous faites une erreur de segmentation, essayez de l'exécuter gdb.
Réponses:
Comment exécuter Valgrind
Pas pour insulter l'OP, mais pour ceux qui viennent à cette question et sont encore nouveaux sur Linux - vous devrez peut-être installer Valgrind sur votre système.
Valgrind est facilement utilisable pour le code C / C ++, mais peut même être utilisé pour d'autres langages lorsqu'il est configuré correctement (voir ceci pour Python).
Pour exécuter Valgrind , passez l'exécutable en argument (avec tous les paramètres au programme).
Les drapeaux sont, en bref:
--leak-check=full
: "chaque fuite individuelle sera montrée en détail"--show-leak-kinds=all
: Affiche tous les types de fuites "définies, indirectes, possibles, atteignables" dans le rapport "complet".--track-origins=yes
: Préférez la sortie utile à la vitesse. Cela permet de suivre les origines des valeurs non initialisées, ce qui peut être très utile pour les erreurs de mémoire. Pensez à l'éteindre si Valgrind est trop lent.--verbose
: Peut vous informer du comportement inhabituel de votre programme. Répétez pour plus de verbosité.--log-file
: Ecrire dans un fichier. Utile lorsque la sortie dépasse l'espace du terminal.Enfin, vous aimeriez voir un rapport Valgrind qui ressemble à ceci:
J'ai une fuite, mais OÙ ?
Donc, vous avez une fuite de mémoire et Valgrind ne dit rien de significatif. Peut-être quelque chose comme ça:
Jetons un coup d'œil au code C que j'ai écrit aussi:
Eh bien, il y a eu 5 octets perdus. Comment est-ce arrivé? Le rapport d'erreur dit simplement
main
etmalloc
. Dans un programme plus vaste, ce serait très difficile à traquer. Cela est dû à la manière dont l'exécutable a été compilé . Nous pouvons en fait obtenir des détails ligne par ligne sur ce qui n'a pas fonctionné. Recompilez votre programme avec un indicateur de débogage (j'utilisegcc
ici):Maintenant, avec cette version de débogage, Valgrind pointe vers la ligne exacte de code allouant la mémoire qui a été divulguée! (Le libellé est important: ce n'est peut-être pas exactement où se trouve votre fuite, mais ce qui a été divulgué. La trace vous aide à trouver où .)
Techniques de débogage des fuites et des erreurs de mémoire
IndexOutOfBoundsException
des problèmes de type.Parfois, vos fuites / erreurs peuvent être liées les unes aux autres, un peu comme un IDE découvrant que vous n'avez pas encore tapé de parenthèse fermante. La résolution d'un problème peut en résoudre d'autres, alors recherchez-en un qui semble être un bon coupable et appliquez certaines de ces idées:
gdb
être) et recherchez les erreurs de précondition / postcondition. L'idée est de suivre l'exécution de votre programme tout en se concentrant sur la durée de vie de la mémoire allouée.Un regard sur les fuites et les erreurs courantes
Surveillez vos pointeurs
Et le code:
En tant qu'assistant d'enseignement, j'ai souvent vu cette erreur. L'élève utilise une variable locale et oublie de mettre à jour le pointeur d'origine. L'erreur ici est de remarquer que
realloc
peut en fait déplacer la mémoire allouée ailleurs et changer l'emplacement du pointeur. Nous partons ensuiteresizeArray
sans direarray->data
où le tableau a été déplacé.Écriture non valide
Et le code:
Notez que Valgrind nous indique la ligne de code commentée ci-dessus. Le tableau de taille 26 est indexé [0,25], c'est pourquoi il
*(alphabet + 26)
s'agit d'une écriture invalide - elle est hors limites. Une écriture non valide est un résultat courant d'erreurs hors par un. Regardez le côté gauche de votre opération d'affectation.Lecture non valide
Et le code:
Valgrind nous indique la ligne commentée ci-dessus. Regardez la dernière itération ici, qui est
*(destination + 26) = *(source + 26);
. Cependant,*(source + 26)
est à nouveau hors limites, de la même manière que l'écriture invalide. Les lectures non valides sont également un résultat courant d'erreurs off-by-one. Regardez le côté droit de votre opération d'affectation.Les topia Open Source (U / Dys)
Comment savoir si la fuite est à moi? Comment trouver ma fuite lorsque j'utilise le code de quelqu'un d'autre? J'ai trouvé une fuite qui n'est pas la mienne; dois-je faire quelque chose? Ce sont toutes des questions légitimes. Tout d'abord, 2 exemples du monde réel qui montrent 2 classes de rencontres communes.
Jansson : une bibliothèque JSON
C'est un programme simple: il lit une chaîne JSON et l'analyse. Dans la fabrication, nous utilisons des appels de bibliothèque pour faire l'analyse pour nous. Jansson effectue les allocations nécessaires de manière dynamique car JSON peut contenir des structures imbriquées de lui-même. Cependant, cela ne signifie pas que nous
decref
ou «libérons» la mémoire qui nous est donnée de chaque fonction. En fait, ce code que j'ai écrit ci-dessus lance à la fois une "lecture invalide" et une "écriture invalide". Ces erreurs disparaissent lorsque vous supprimez ladecref
ligne pourvalue
.Pourquoi? La variable
value
est considérée comme une "référence empruntée" dans l'API Jansson. Jansson garde une trace de sa mémoire pour vous, et vous avez simplementdecref
des structures JSON indépendantes les unes des autres. La leçon ici: lisez la documentation . Vraiment. C'est parfois difficile à comprendre, mais ils vous expliquent pourquoi ces choses se produisent. Au lieu de cela, nous avons des questions existantes sur cette erreur de mémoire.SDL : une bibliothèque graphique et de jeux
Quel est le problème avec ce code ? Il fuit constamment ~ 212 Kio de mémoire pour moi. Prenez un moment pour y réfléchir. Nous activons puis désactivons SDL. Répondre? Il n'y a rien de mal.
Cela peut sembler bizarre au début . À vrai dire, les graphiques sont désordonnés et parfois vous devez accepter certaines fuites comme faisant partie de la bibliothèque standard. La leçon ici: vous n'avez pas besoin d'étouffer toutes les fuites de mémoire . Parfois, vous devez simplement supprimer les fuites, car ce sont des problèmes connus pour lesquels vous ne pouvez rien faire . (Ce n'est pas ma permission d'ignorer vos propres fuites!)
Réponses au vide
Comment savoir si la fuite est la mienne?
C'est. (99% sûr, de toute façon)
Comment trouver ma fuite lorsque j'utilise le code de quelqu'un d'autre?
Il y a de fortes chances que quelqu'un d'autre l'ait déjà trouvé. Essayez Google! Si cela échoue, utilisez les compétences que je vous ai données ci-dessus. Si cela échoue et que vous voyez principalement des appels d'API et peu de votre propre trace de pile, consultez la question suivante.
J'ai trouvé une fuite qui n'est pas la mienne; dois-je faire quelque chose?
Oui! La plupart des API ont des moyens de signaler les bogues et les problèmes. Utilise les! Aidez à redonner aux outils que vous utilisez dans votre projet!
Lectures complémentaires
Merci d'être resté avec moi aussi longtemps. J'espère que vous avez appris quelque chose, en essayant de m'occuper du large éventail de personnes qui arrivent à cette réponse. J'espère que vous avez demandé certaines choses en cours de route: Comment fonctionne l'allocateur de mémoire de C? Qu'est-ce qu'une fuite de mémoire et une erreur de mémoire? En quoi sont-ils différents des segfaults? Comment fonctionne Valgrind? Si vous en aviez, nourrissez votre curiosité:
malloc
l'allocateur de mémoire de Cla source
memcheck
outil est-il activé par défaut?memcheck
c'est l'outil par défaut:--tool=<toolname> [default: memcheck]
Essaye ça:
valgrind --leak-check=full -v ./your_program
Tant que valgrind est installé, il passera par votre programme et vous dira ce qui ne va pas. Il peut vous donner des indications et des endroits approximatifs où vos fuites peuvent être trouvées. Si vous faites une erreur de segmentation, essayez de l'exécuter
gdb
.la source
your_program
== le nom de l'exécutable ou la commande que vous utilisez pour exécuter votre application.Tu peux courir:
la source
Vous pouvez créer un alias dans le fichier .bashrc comme suit
Donc, chaque fois que vous voulez vérifier les fuites de mémoire, faites simplement
Cela générera un fichier journal Valgrind dans le répertoire actuel.
la source