[Powered by Google Translate] [Valgrind]
[Nate Hardison, Université de Harvard]
C'est CS50, CS50.TV]
Quelques-uns des bugs les plus difficiles dans les programmes C
proviennent de la mauvaise gestion de la mémoire.
Il ya un grand nombre de façons de visser les choses,
en ce compris affecter un montant erroné de la mémoire,
oublier d'initialiser les variables,
l'écriture avant ou après la fin d'un tampon,
et la libération de garder les temps de mémoire multiples.
Les symptômes vont de plantages intermittents
aux valeurs mystérieusement écrasés,
souvent dans des lieux et des temps très éloignés de l'erreur initiale.
Déterminer le problème observé remonter à la cause racine sous-jacente
Il peut être difficile,
mais heureusement, il ya un programme appelé Valgrind utile
qui peut faire beaucoup pour aider.
>> Vous exécutez un programme en vertu Valgrind pour permettre
vérification approfondie des allocations de mémoire de tas et des accès.
Lorsque Valgrind détecte un problème, il vous donne immédiatement,
information directe qui vous permet de
plus facilement trouver et corriger le problème.
Valgrind également des rapports sur des problèmes de mémoire moins mortelles,
telles que les fuites de mémoire, l'allocation de mémoire tas,
et d'oublier de le libérer.
Comme notre compilateur, Clang, dans notre débogueur GDB,
Valgrind est un logiciel libre, et il est installé sur l'appareil.
Valgrind s'exécute sur votre exécutable binaire,
pas votre. c ou fichiers. h de code source,
alors assurez-vous que vous avez compilé une copie mise à jour de votre programme
en utilisant Clang Ou Faire.
Ensuite, l'exécution de votre programme sous Valgrind peut être
aussi simple que de précéder la commande de programme standard avec le mot Valgrind,
qui démarre Valgrind et exécute le programme à l'intérieur de celui-ci.
Lors du démarrage, Valgrind fait un peu complexe
calibrage de configurer le fichier exécutable pour les vérifications de mémoire,
donc cela peut prendre un peu pour se lever et courir.
Le programme va alors s'exécuter normalement, que ce soit beaucoup plus lentement,
et quand il sera fini, Valgrind affiche un résumé de son utilisation de la mémoire.
Si tout va bien, il va ressembler à quelque chose comme ceci:
Dans ce cas,. / Clean_program
est le chemin d'accès au programme que je veux courir.
Et tandis que celui-ci ne prend aucun argument,
si c'était le cas je venais de les virer à la fin de la commande comme d'habitude.
Programme Clean est juste un programme peu ridicule que j'ai créé
qui alloue l'espace pour un bloc de ints sur le tas,
mettre des valeurs à l'intérieur d'eux, et libère le bloc entier.
C'est ce que vous filmez pour, aucune erreur et pas de fuites.
>> Un autre indicateur important est le nombre total d'octets alloués.
Selon le programme, si vos allocations sont les méga-octets ou plus,
vous faites probablement quelque chose de mal.
Êtes-vous stocker inutilement des doublons?
Utilisez-vous le tas de stockage, alors qu'il serait préférable d'utiliser la pile?
Ainsi, les erreurs de mémoire peut être vraiment mal.
Les plus manifestes provoquer des accidents spectaculaires,
mais même alors, il peut encore être difficile à cerner
exactement ce qui a mené à l'accident.
Plus insidieusement, un programme avec une erreur de mémoire
pouvez toujours compiler proprement
et peut encore semble pas fonctionner correctement
parce que vous avez réussi à avoir de la chance la plupart du temps.
Après plusieurs "bons résultats",
vous pourriez penser qu'un accident est un coup de chance de l'ordinateur,
mais l'ordinateur ne se trompe jamais.
>> Exécution Valgrind peut vous aider à repérer la cause des erreurs de mémoire visibles
ainsi que de trouver tapi erreurs que vous n'avez même pas encore au courant.
Chaque fois que Valgrind détecte un problème, il affiche les informations à propos de ce qu'il a observé.
Chaque élément est assez laconique -
la ligne de source de l'instruction fautive, quel est le problème,
et un peu d'info sur la mémoire impliqués -
mais c'est souvent assez d'information pour attirer votre attention au bon endroit.
Voici un exemple de Valgrind en cours d'exécution sur un programme bogué
qui effectue une lecture incorrecte de la mémoire heap.
Nous voyons aucune erreur ou alerte en compilation.
Uh-oh, le résumé des erreurs dit qu'il ya deux erreurs -
deux lectures invalide de la taille 4 - octets, ce qui est.
Les deux mauvais lit s'est produite dans la fonction principale de invalid_read.c,
le premier sur la ligne 16 et la seconde sur la ligne 19.
Regardons le code.
On dirait que le premier appel à printf essaie de lire un int après la fin de notre bloc de mémoire.
Si nous regardons en arrière à la sortie Valgrind,
nous voyons que Valgrind nous a dit exactement cela.
L'adresse que nous essayons de lire commence 0 octets
delà de l'extrémité du bloc de taille 16 octets -
quatre compteurs 32 bits entiers que nous attribués.
Autrement dit, l'adresse que nous essayions de lire commence juste à la fin de notre bloc,
comme nous le voyons dans notre appel printf mauvais.
Maintenant, invalide lectures peuvent sembler ne pas que les grandes d'un accord,
mais si vous utilisez ces données pour contrôler le débit de votre programme -
par exemple, dans le cadre d'une instruction if ou boucle -
alors les choses peuvent aller mal en silence.
Regardez comment je peux exécuter le programme invalid_read
et rien hors de l'ordinaire qui se passe.
Effrayant, hein?
>> Maintenant, penchons-nous sur certains types plus d'erreurs que vous pouvez rencontrer dans votre code,
et nous allons voir comment Valgrind les détecte.
Nous venons de voir un exemple de invalid_read,
alors maintenant nous allons vérifier une invalid_write.
Encore une fois, aucune erreur ou alerte en compilation.
Bon, Valgrind dit qu'il ya deux erreurs dans ce programme -
et invalid_write et un invalid_read.
Voyons maintenant ce code.
On dirait que nous avons une instance de la strlen classique plus un bug.
Le code n'est pas malloc un octet supplémentaire de l'espace
pour le caractère / 0,
alors quand str copie est allé à écrire à ssubstrlen "CS50 rocks!"
il a écrit 1 octet après la fin de notre bloc.
Le invalid_read vient quand nous faisons notre appel à printf.
Printf finit la lecture de mémoire invalide quand il lit le fichier / 0 caractère
qu'il n'y paraît à la fin de cette chaîne E c'est l'impression.
Mais rien de tout cela échappé Valgrind.
On voit qu'il a attiré l'invalid_write dans le cadre de la copie str
à la ligne 11 de la principale, et la invalid_read fait partie de printf.
Rock on, Valgrind.
Encore une fois, cela ne semble pas être un gros problème.
Nous pouvons exécuter ce programme à plusieurs reprises en dehors de Valgrind
et ne pas voir les symptômes d'erreurs.
>> Toutefois, penchons-nous sur une légère variation de ce voir
comment les choses peuvent devenir vraiment mauvais.
Alors, d'accord, nous abusent des choses plus que juste un peu dans ce code.
Nous ne sommes qu'à l'allocation d'espace sur le tas pour les deux chaînes
la longueur de roches CS50,
cette fois, se souvenant de l'/ 0 caractère.
Mais alors que nous jetons dans une chaîne de super-long dans le bloc de mémoire
S qui pointe.
Quel effet cela aura-t sur le bloc de mémoire qui pointe à T?
Eh bien, si les points de T à mémoire qui est juste à côté de S,
vient juste après,
alors nous pourrions avoir écrit sur une partie de T.
Nous allons exécuter ce code.
Regardez ce qui s'est passé.
Les chaînes dont nous avons stockés dans nos blocs de tas deux semblaient avoir imprimé correctement.
Rien ne semble pas mauvaise du tout.
Cependant, revenons à notre code et
commentez la ligne où nous copier CS50 roches
dans le second bloc de mémoire, désigné par t.
Maintenant, quand nous exécuter ce code nous devrions
uniquement le contenu du premier bloc de mémoire imprimer.
Whoa, même si nous n'avons pas str copie
tous les caractères dans le bloc de tas second, celui pointé par T,
nous obtenons une impression.
En effet, la chaîne nous en peluche dans notre premier bloc
est sorti le premier bloc et dans le second bloc,
faisant tout semble normal.
Valgrind, cependant, nous raconte l'histoire vraie.
Nous y voilà.
Toutes les personnes invalides lectures et écritures.
>> Regardons un exemple d'un autre genre d'erreur.
Ici, nous faisons quelque chose de regrettable.
Nous saisissons l'espace pour un int sur le tas,
et on initialise un pointeur int - p - pour pointer vers cet espace.
Cependant, alors que notre pointeur est initialisé,
les données qu'il a pointant vers ce que vient d'ordure est dans cette partie du tas.
Ainsi, lorsque nous chargeons ces données en int i,
Nous techniquement initialiser i,
mais nous le faisons avec les données indésirables.
L'appel à faire valoir, ce qui est une macro débogage à portée de main
défini dans le bien nommé affirmer bibliothèque,
annulerait le programme si son état test échoue.
Autrement dit, si i n'est pas 0.
Selon ce qui était dans le segment de mémoire, pointée par p,
ce programme pourrait fonctionner parfois et ne parviennent pas à d'autres moments.
Si cela fonctionne, nous ne faisons que de la chance.
Le compilateur ne sera pas attraper cette erreur, mais Valgrind volonté certaine.
Là, nous voyons l'erreur découlant de notre utilisation de ces données inutiles.
>> Lorsque vous allouer de la mémoire heap mais ne le libérer ou de le libérer,
que l'on appelle une fuite.
Pour une petite éphémère programme qui tourne et quitte immédiatement,
fuites sont relativement inoffensifs,
mais pour un projet de plus grande taille et / ou la longévité,
même une petite fuite peut aggraver en quelque chose de majeur.
Pour CS50, nous nous attendons à ce que vous
prendre soin de libérer toute la mémoire que vous allouez tas,
puisque nous voulons vous de construire les compétences nécessaires pour gérer correctement le processus manuel
requis par C.
Pour ce faire, votre programme devrait avoir une exacte
one-to-one correspondance entre malloc et des appels gratuits.
Heureusement, Valgrind peut vous aider avec des fuites de mémoire aussi.
Voici un programme qui fuit appelé fuite.C qui alloue
l'espace sur le tas, écrit à elle, mais ne la libérer.
On le compile avec Make et l'exécuter sous Valgrind,
et nous voyons que, même si nous avons des erreurs de mémoire pas,
nous avons une fuite.
Il ya 16 octets définitivement perdus,
ce qui signifie que le pointeur vers la mémoire n'était pas dans la portée lorsque le programme quitte.
Maintenant, Valgrind ne nous donne pas une tonne d'informations à propos de la fuite,
mais si nous suivons cette petite note qui lui donne vers le fond de son rapport
relancer avec - Fuite-check = plein
pour voir tous les détails de fuite de mémoire,
nous aurons plus d'informations.
Maintenant, dans le résumé tas,
Valgrind nous dit où la mémoire qui a été perdu a été initialement alloué.
Tout comme nous savons de la recherche dans le code source,
Valgrind nous informe que nous avons fui la mémoire
allouée par un appel à malloc à la ligne 8 de fuite.C
dans la fonction principale.
Très chouette.
>> Valgrind catégorise les fuites en utilisant ces termes:
Définitivement perdu - c'est segment de mémoire allouée
dans laquelle le programme n'a plus un pointeur.
Valgrind sait que vous aviez autrefois le pointeur, mais ont depuis perdu la trace de celui-ci.
Cette mémoire est certainement une fuite.
Indirectement perdu - c'est segment de mémoire allouée
à laquelle les seuls pointeurs à elle aussi est perdu.
Par exemple, si vous avez perdu votre pointeur sur le premier nœud d'une liste chaînée,
puis le premier nœud lui-même serait définitivement perdu,
tandis que tous les nœuds seraient indirectement perdu.
Peut-être perdu - c'est segment de mémoire allouée
dans laquelle Valgrind ne peut pas être sûr de savoir s'il existe un pointeur ou non.
Est toujours joignable segment de mémoire allouée
dans laquelle le programme a encore un pointeur à la sortie,
ce qui signifie généralement que quelques points de variables globales à elle.
Pour vérifier ces fuites, vous aurez également à inclure l'option
- Encore accessible = oui
dans votre invocation de Valgrind.
>> Ces différents cas peuvent nécessiter des stratégies différentes pour les nettoyer,
mais les fuites doivent être éliminées.
Malheureusement, la réparation des fuites peut être difficile à faire,
puisque les appels erronés à la libre peut faire exploser votre programme.
Par exemple, si nous regardons invalid_free.c,
nous voyons un exemple de libération de la mémoire mauvais.
Quel devrait être un simple appel à libérer la totalité du bloc
de la mémoire pointée par int_block,
est plutôt devenu une tentative pour libérer chaque section int entreprises
de la mémoire individuellement.
Ce sera une rupture catastrophique.
Boom! Qu'est-ce une erreur.
Ce n'est certainement pas bon.
Si vous êtes coincé avec ce genre d'erreur, cependant, et vous ne savez pas où chercher,
se replier sur votre nouveau meilleur ami.
Vous l'avez deviné - Valgrind.
Valgrind, comme toujours, sait exactement ce qui se passe.
Les chiffres alloc et libre ne correspondent pas.
Nous avons obtenu 1 alloc et 4 libère.
Et Valgrind nous dit aussi où le premier appel mauvais gratuit -
l'une qui a déclenché la blowup - vient -
la ligne 16.
Comme vous le voyez, les appels mauvaises pour libérer sont vraiment mauvais,
nous vous recommandons de laisser votre fuite programme
pendant que vous travaillez sur l'obtention de la fonctionnalité correcte.
Commencer à chercher des fuites seulement après que votre programme fonctionne correctement,
sans autres erreurs.
>> Et c'est tout ce que nous avons pour cette vidéo.
Maintenant, qu'est-ce que tu attends?
Aller courir Valgrind sur vos programmes en ce moment.
Mon nom est Nate Hardison. C'est CS50. [CS50.TV]