VT2020-Valgrind-Fiche

From air
Jump to navigation Jump to search
Logo de Valgrind

Valgrind est un outil libre est multi-plateforme, qui a pour but d'analyser la gestion de la mémoire et des threads d'un code.

Julian Seward est la personne à l'origine de Valgrind et en 2006 Google lui a remis le Google-O'Reilly Open Source Award pour son travail sur Valgrind. Puis au cours des années plusieurs développeurs ont contribué au projet, tel que Cerion Armour-Brown, Jeremy Fitzhardinge, Tom Hughes, Nicholas Nethercote, Paul Mackerras, Dirk Mueller, Bart Van Assche, Josef Weidendorfer, et Robert Walsh.

La petite histoire de Valgrind est que le nom de Valgrind vient de la mythologie nordique. Valgrind fait ainsi référence à l'entrée principale du Valhalla. Initialement, le projet devait s'appeler Heimdall, mais ce nom était déjà attribué.

Fonctionnement

Schéma de fonctionnement de Valgrind

Valgrind a un comportement se rapprochant d'une machine virtuelle qui utilise la technique de compilation Just In Time (JIT).

Valgrind n'exécute pas directement le code sur le processeur. Valgrind va dans un premier temps traduire le code dans une représentation intermédiaire. Cette dernière est plus simple que du code machine et surtout indépendante du processeur. Une fois la présentation intermédiaire prête, un ou plusieurs des outils choisis vont modifier la représentation intermédiaire, afin de réaliser l'analyse du code. La dernière étape consiste à traduire la représentation intermédiaire en code machine pour l'exécuter sur le processeur.

Durant les étapes de traduction, Valgrind va fournir des points d'entrée pour GDB afin de pouvoir débuguer le code en cours d'analyse.

Cette façon de faire a bien évidemment des inconvénients. Le principale de ces inconvénients est le temps d'exécution. Du fait de la compilation JIT, le temps d'exéctution est plus long que si nous exécutions le programme sans Valgrind. Selon les outils sélectionnés pour l'analyse la vitesse d'exécution peut être 3 à 50 fois plus lente.

Compilation Just In Time (JIT)

La compilation JIT (ou aussi appelée compilation dynamique) est apparue dans un article de J. McCarthy de 1960 en utilisant LISP. Le principe de la compilation JIT est le suivant : compiler le code en même temps qu'il s'exécute. Ce principe permet au compilateur d'avoir accès à des informations d'exécution, qu'il n'aurait pas eu avec une compilation statique.

En théorie, la compilation dynamique va compiler les parties du code nécessaires à son démarrage et va compiler le reste du code quand s'il sera appelé. C'est-à-dire que la fonction main de votre programme C sera compilé pour démarrer le programme. Maintenant votre fonction main contient un appel à la fonction foo. Au moment de cet appel le code de la fonction foo sera alors compilé.

Cette méthode de compilation a pour avantage de produire un code machine optimisé en fonction de l'architecture du CPU. Mais aussi de pouvoir recompiler directement du code machine optimisé au moment ou le code source est modifié. La compilation JIT a pour inconvénients d'avoir des retads de démarrage et la compilation peut apporter une surcharge pour le CPU et la mémoire. C'est pourquoi en pratique seuls les bloques de code couramment utilisés sont compilés dynamiquement.

La Java Virtual Machine (JVM) est un compilateur JIT.

Exemple d'utilisation de Valgrind

Comme nous venons de le voir Valgrind est un outil permettant la détection des fuites mémoire. Une fuite mémoire c'est lorsque votre code alloue une zone mémoire et qu'après avoir utilisé cette dernière, votre code ne libère pas ladite zone. Ainsi vous pouvez détecter les fuites mémoire de votre programme et par conséquence rendre votre programme moins gourmand.

Comparaison entre C++ et Java

C++ et Java sont deux langages de programmation objet. Lorsque vous êtes un new ... pour instancier un objet, votre programme va allouer une zone mémoire pour cet objet.

La différence entre les deux langages est la suivante : lorsque vous n'utilisez plus l'objet ou que la variable contenant sa référence (pointeur) vaut null, Java va automatiquement supprimer l'objet de la mémoire et libérer la zone occupée, via le Garbage Collector. En C++, c'est à vous de faire le ménage : il vous faudra créer une méthode dite destructeur et l'appeler. Dans le destructeur que vous devez écrire, il vous faudra libérer la mémoire occupée par les attributs de votre objet via la fonction free

Utilisation

Outils fournis par Valgrind

  • Memcheck : Outil qui permet la détection des erreurs de gestion de mémoire.
  • Cachegrind : Outil qui permet d'obtenir le profile de caches en simulant les caches I1, D1 et L2. Cet outil permet donc de détecter les erreurs de cache dans le code
  • Callgrind : Est une extension de Cachegrind. Cette extension permet d'avoir les informations de Cachegrind, mais avec des informations supplémentaires sur les callgraphs.
  • Massif : Permet de faire le profile du tas. L'outil prend régulièrement des captures du tas afin de représenter son utilisation au cous du temps.
  • Helgrind : Outil de débugage de threads. Cet outil détecte les accès concurrents.
  • DRD : Outil permettant la détection des erreurs dans les programmes C/C++ multithreaded
  • DHAT : Outil permettant de savoir comment les programmes utilisent leur tas

Installation

$ sudo apt install valgrind

Analyse de la mémoire

$ valgrind --track-origins=yes --leak-check=full --verbose --show-leak-kinds=all ./valgrind_test

Signification des paramètres :

  • --leak-check=full : Pour afficher chaque fuite mémoire en détail.
  • --show-leak-kinds=all : Pour afficher tous les types de fuite mémoire (definite, indirect, possible, * reachable).
  • --track-origins=yes : Permet d'avoir la trace des variables non initialisées.

Sources

Veille Technologique