Outils CSC4526 (cmake, IDE, SonarLint, GoogleTest, débogueur, analyse de fuites mémoire)

Michel SIMATIC

15 mai 2024

1 Introduction

Ce document donne des précisions sur l’utilisation de différents outils utilisés durant CSC4526 :

2 cmake

Tous les canevas de projet C++ fournis dans le cadre de CSC4526 s’appuient sur cmake. cmake permet de configurer l’environnement du projet, ce qui dispensera de configurer l’IDE.

Commencez par lire ci-dessous les instructions communes à tous les IDE/OS, puis lisez la section correspondant à votre IDE/OS.

2.1 Instructions communes à tous les IDE/OS

Cliquez pour déplier/plier les détails

2.2 Clion (Linux)

Cliquez pour déplier/plier les détails

cd chemin_absolu_vers_votreRépertoireProjet
rm -Rf cmake-build-debug
mkdir build
cd build
cmake .. # N'oubliez pas le " .." après la commande cmake
#
# Si cmake affiche l'une des messages suivants:
#    - "cmake not found"
#    - "Error during cmake"
#    - "No CMAKE_CXX_COMPILER"
#    - "Could NOT find X11"
# Alors tapez les 2 commandes suivantes:
#
sudo apt -y install git cmake g++ libx11-dev libxrandr-dev libxi-dev libxcursor-dev libudev-dev mesa-common-dev libfreetype6-dev libopenal-dev libvorbis-dev libflac-dev
# Au final, vous devriez obtenir le message "-- Build files have been written to: absolute_path_to_your_project". Cela signifie que toutes les installations préalables pour Linux sont OK.
#
# Il vous reste à nettoyer votre répertoire de votre test cmake:
cd ..
rm -Rf build
Fenêtre SFML works!

2.3 CLion (MacOS)

2.3.1 Démarche préliminaire pour les utilisateurs·trices de Mac avec processeur M1 (ARM)

Cliquez pour déplier/plier les détails

Dans un terminal, tapez la commande which brew pour vous assurer d’avoir la version de Homebrew adaptée à l’architecture ARM de votre processeur : Si la version de Homebrew est adaptée à ARM, le chemin affiché devrait commencer par /opt/homebrew/. Si le chemin affiché commence par /usr/local/, votre version de Homebrew est adaptée à un processeur Intel (ce qui vous posera des soucis lorsque CLion fera des éditions de liens) : Il vous faut installer un Homebrew adapté à ARM (cf. procédure un peu modifiée de la 1ère réponse ici) :

cd /opt
sudo mkdir -p homebrew
sudo chown -R $(whoami) homebrew
curl -L https://github.com/Homebrew/brew/tarball/master |\
    tar xz --strip 1 -C homebrew
#+end_src
- Modifiez *PATH*
#+begin_src bash
PATH=/opt/homebrew/bin:$PATH
hash -d brew
brew bundle install --file /path/to/Brewfile

2.3.2 Démarche pour tous les utilisateurs·trices de MacOS

Cliquez pour déplier/plier les détails

Fenêtre SFML works!

2.4 Visual Studio (Windows)

Cliquez pour déplier/plier les détails

Fenêtre SFML works!

Pour information, cette procédure crée les répertoires .vs et out dans le répertoire de votre projet.

3 IDE CLion ou Visual Studio : Ajout de fichiers source dans un projet généré par cmake

Pour les premiers canevas de projet que vous utiliserez dans CSC4526, vous aurez juste à modifier des fichiers source déjà fournis dans le canevas.

Puis, vous serez amené à ajouter des fichiers sources, ce qui requiert une procédure particulière décrite dans cette section.

3.1 CLion (Linux ou MacOS)

Cliquez pour déplier/plier les détails

Sous CLion, vous pouvez utiliser les menus de création de .h et de .cpp. Sachez toutefois que CLion modifiera src/CMakeLists.txt. pour y ajouter le nom des fichiers ajoutés. Si vous le souhaitez vous pouvez manuellement supprimer ces ajouts inutiles.

3.2 Visual Studio (Windows)

Cliquez pour déplier/plier les détails

Dans le cas où votre projet a été créé avec menu Fichier > Ouvrir > CMake… :

4 SonarLint

Cliquez pour déplier/plier les détails

4.1 Session CSC4526 2023-2024 : Résoudre un bug lié à la combinaison Visual Studio version 17.9.6 / cmake / SonarLint

Pour la session CSC4526 2023-2024, un bug lié à la combinaison Visual Studio version 17.9.6 / cmake / SonarLint est apparu quelques jours avant le début de ce cours. Il empêche que SonarLint affiche ses messages dans Visual Studio.

4.1.1 Constatation du bug

Voyons tout d’abord si vous êtes concerné·e par ce bug :

4.1.2 Correction du bug

Cet article indique la démarche pour résoudre ce bug :

5 Google Test au sein de votre IDE

Google Test est un environnement facilitant l’écriture et le passage de tests unitaires. Cette section présente l’utilisation de Google Test selon l’IDE que vous utilisez. Nous supposons ici que vous avez déjà appliqué cmake sur un canevas de projet contenant des tests unitaires.

5.1 Avec CLion (Linux ou MacOS)

Cliquez pour déplier/plier les détails

5.2 Avec Visual Studio (Windows) et son explorateur de tests

Cliquez pour déplier/plier les détails

6 Débogueur

Cette section retranscrit le contenu des vidéos de tutoriel sur le débogage :

Ces vidéos utilisent le fichier ExempleDeDebugDeProgramme.cpp

6.1 Initiation au débogage de programme avec CLion

Cliquez pour déplier/plier les détails

Les manipulations ont été effectuées en s’appuyant sur le scénario Visual Studio ci-dessous. Pour information, CLion ne permet pas de mettre en place un arrêt quand la valeur d’une variable change (il faut taper manuellement la commande gdb watch dans la console gdb fournie par CLion).

6.2 Initiation au débogage de programme avec Visual Studio

Cliquez pour déplier/plier les détails

6.2.1 Introduction

Dans ce tutoriel vidéo, je vous propose une initiation aux principales fonctions de débogage offertes par Visual Studio. Vous pourrez ainsi gagner en efficacité et en rapidité dans la mise au point de vos programmes, qu’ils soient écrits en C++ ou dans d’autres langages. En effet, les notions présentées ici sont génériques à tous les langages de programmation.

Notez que, pour ce tutoriel, nous nous appuierons sur le fichier ExempleDeDebugDeProgramme.cpp que je vous invite à télécharger maintenant, si vous ne l’avez pas déjà fait.

6.2.2 Mise en place de l’environnement

  1. Création d’un projet console
  2. Recopie du code pour debugging

6.2.3 Déboguer un plantage qui ne semble pas dans notre code

  1. Menu Déboguer > Exécuter sans débogage
  2. La fenêtre affiche Expression: vector subscript out of range
  3. Vu que la fenêtre dit Please retry to debug the application, clic sur Recommencer
  4. Expliquer affichage “Windows recherche une solution au problème” (pour info, MacOS a le même comportement). Le pb ne vient pas de votre OS ou de votre IDE (VisualStudio), mais bien de votre application !
  5. Cliquer sur “Déboguer” : patienter (environ 20 secondes).
  6. Choisir l’instance courante : vous arrivez dans le menu de debug.
  7. Nous pourrions commencer le débogage. Mais, pour vous montrer qu’on peut s’économiser le temps d’attente qu’on vient de vivre, voyons ce qui se passe si on lance notre programme directement en débogage.
  8. Faisons le ménage et démarrons maintenant en mode debug, par exemple en cliquant sur le bouton “Débogueur Windows Local” ou bien en appuyant sur la touche F5
  9. La fenêtre affiche toujours Expression: vector subscript out of range.
  10. Vu que la fenêtre dit Please retry to debug the application, clic sur Recommencer
  11. On arrive directement dans VisualStudio, dans le code du module vector qui affiche vector subscript out of range
  12. Dans le cadre en bas à droite, sélectionner l’onglet “Pile des appels” : la flèche montre où est arrêté votre programme.
  13. Double-cliquez sur la ligne juste en dessous : vous êtes à votre ligne de code qui a appelé vector et qui a généré le souci.
  14. Sélectionnez v.size : VS vous affiche 4.
  15. Sélectionnez v et cliquez sur la flèche pour voir le contenu de v : il y a 4 éléments. NB : on peut aussi voir le contenu de v dans le cadre des variables locales en bas à gauche.
  16. OK, nous comprenons le bug : ajoutons le -1 et cliquons sur le bouton “Redémarrer”

6.2.4 Pas à pas

  1. Nous obtenons une erreur, plus loin : division par zéro. Effectivement c vaut 0 (qui ne fait pas bon ménage avec l’opérateur modulo).
  2. Pour comprendre comment c s’est retrouvé avec la valeur 0, mettons un point d’arrêt au niveau de la définition de c, puis exécutons pas à pas le programme.
  3. Arrêter le programme
  4. Clic droit, Exécuter jusqu’au curseur
  5. Bouton “Pas à pas principal” (y compris au-dessus de l’appel à unCalcul()) ==> Zut, la valeur de c a changé.
  6. Exécuter jusqu’au curseur de l’appel à unCalcul()
  7. Bouton “Pas à pas détaillé” ==> Vous rentrez dans unCalcul()
  8. Faire “Aperçu de la définition” ==> On voit l’origine du problème.

6.2.5 Pose d’un point d’arrêt conditionnel

  1. Insérer un point d’arrêt au niveau du return resultat
  2. Dans le cadre en bas à droite, clic sur l’onglet “Points d’arrêt”
  3. Clic-droit sur le point d’arrêt, Paramètres
  4. Clic sur Conditions, resultat == 0
  5. Redémarrez
  6. Désactivez ce point d’arrêt

6.2.6 Arrêter l’exécution quand la valeur d’une variable change

  1. Exécutez jusqu’à ce que la variable c soit définie.
  2. Dans le cadre des variables locales, clic-droit sur c et “Arrêtez quand la valeur change”. Notez que la variable a change aussi de couleur (vu que c est une référence vers a).
  3. Cliquez sur “Continuer” ==> Arrêt quand a (et donc c) change.
  4. Hélas, si vous cliquez sur “Redémarrez”, le programme ne s’arrête pas sur le changement de c : il faut programmer un nouveau point d’arrêt pour cette variable.

6.2.7 Nettoyage de tous les points d’arrêt

  1. Menu Déboguer > Supprimer tous les points d’arrêt

6.2.8 Conclusion

Nous voici arrivés à la fin de cette initiation au débogage sous Visual Studio : n’hésitez pas à faire vos propres expériences pour vous familiariser avec cet outil et ainsi gagner en efficacité et en rapidité dans la mise au point de vos programmes.

7 Analyse de fuites mémoire

Pour analyser les fuites mémoire dans du code C++, nous nous appuyons sur des outils dédiés.

Pour tester ces outils, vous pouvez vous appuyer sur votre propre projet ou bien créer un projet console et substituer le code automatiquement généré par le contenu de codeQuiFuit.cpp.

7.1 Linux

7.1.1 CLion sans installation d’un autre outil

Pour éviter l’installation d’un autre outil, nous nous appuyons sur le sanitizer address.

Pour ce faire, modifiez le CMakeLists.txt à la racine de votre projet en lui ajoutant les lignes :

        .......
        set (BUILD_SHARED_LIBS FALSE)

        set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # This cmake_policy avoids warning by cmake when we use pugixml

        cmake_policy(SET CMP0135 NEW) # This cmake_policy avoids warning by cmake when we fetch contents based on URL

        ###### DEBUT lignes a rajouter
        if(MSVC)
          add_compile_options(/fsanitize=address)
        else()
          add_compile_options(-fsanitize=address)
          add_link_options(-fsanitize=address)
        endif()
        ###### FIN lignes a rajouter

        set(CMAKE_CXX_STANDARD 20)
        .......

Dans votre projet, prenez en compte les changements de CMakeLists.txt, puis régénérez complètement votre projet.

Si vous exécutez, comme jusqu’à présent, unitTests dans le cadre de l’environnement GoogleTest, vos tests sont toujours tous corrects. Cependant, en bas à gauche de votre écran, à côté de l’onglet Console, il y a un onglet Sanitizers qui vous signale des problèmes repérés dans votre code :

Onglet Sanitizer dans CLion

Pour information, si vous choisissez d’exécuter la cible All CTests, les tests qui causent la fuite mémoire sont signalés comme ne passant pas (alors que, pourtant, les différents EXPECT ou ASSERT sont corrects). L’onglet Sanitizers permet de comprendre les problèmes.

7.1.2 Clion combiné à l’outil Valgrind

  1. Installez Valgrind. Par exemple, sous Ubuntu : sudo apt install valgrind
  2. Dans CLion, menu Run > Run ‘unitTests’ with Valgrind Memcheck : la fenêtre Console indique que valgrind a été lancé.
  3. Au bout de quelques secondes, la fenêtre en bas à droite de l’écran indique que les “Tests Results” sont OK. En fait, ceci ne concerne que les tests unitaires qui sont tous OK. SI vous cliquez sur l’onglet “Valgrind” (à côté de l’onglet “Console” actuellement affiché), vous voyez un message Leak-DefinitelyLost 3 Warnings. Si vous dépliez ce message, vous accédez au détail de ces fuites mémoire. En dépliant l’une de ces fuites, vous avez accès à la pile d’appel qui a amené à l’allocation mémoire sans libération associée. En double-cliquant sur l’une des lignes de la pile d’appel (par exemple, dans la figure 1, sur la ligne Group::Group(pugi::xml_node) Group.cpp:15)), vous accédez au code de la ligne qui a fait le new.
CLion affichant le résultat de l’exécution de Valgrind

7.2 MacOS

7.2.1 Clion

Suivez la démarche proposée dans la section “CLion sans installation d’un autre outil” de Linux.

7.2.2 Outil standard Leaks

Sous MacOS, vous pouvez aussi vous appuyer sur l’outil leaks fourni en standard (cf. cet article).

Dans un terminal, compiler votre programme avec l’option -g (NB: Clion compile votre programme directement avec cette option). Par exemple:

cc -g -Wall -Werror nomFichier.c -o nomFichier
leaks --atExit --list -- ./nomFichier
# ==> Détection des leaks sans voir leur emplacement précis dans le code
export MallocStackLogging=1
# Rien ne se passe
leaks --atExit --list -- ./nomFichier
# ==> Affichage des emplacements précis dans le code

7.3 Visual Studio / Windows

7.3.1 Votez pour que le sanitizer Address permette la détection de fuites mémoire sous Windows

Windows implémente le sanitizer Address pour son compilateur MSVC (cf. tous ces exemples de détection), mais, actuellement, cette implémentation ne détecte pas les fuites mémoire !

Si vous le souhaitez, votez ici pour demander l’ajout de cette fonctionnalité !

En attendant, consultez la section suivante pour détecter les fuites mémoire sous Windows.

7.3.2 Visual Studio Profiler

Par rapport à Valgrind sous Linux, le Visual Studio Profiler est un peu plus compliqué à mettre en oeuvre, mais présente l’avantage de ne pas du tout ralentir l’application profilée.

Cet outil étant disponible en standard dans Visual Studio, il n’y a aucune installation à faire.

Commencez par poser un point d’arrêt au niveau de l’appel à la fonction que vous souhaitez tester. Par exemple, dans le cas d’une application CSC4526, l’appel à la fonction myMain().

Si vous souhaitez voir si vos tests unitaires révèlent des fuites mémoire :

  1. Posez un point d’arrêt au niveau de la première ligne de votre premier test unitaire,
  2. Cliquez sur le bouton “Débogueur Windows Local”
  3. Quand vous êtes arrêté au niveau de votre ligne, dans la fenêtre en bas à droite, sélectionnez l’onglet “Pile des appels”
  4. Recherchez dans cette pile la ligne unitTests.exe!RUN_ALL_TESTS() et double-cliquez dessus : le code de gtest.h s’affiche.
  5. Posez un point d’arrêt sur la ligne return ::testing::UnitTest::GetInstance()->Run();
  6. Dans la fenêtre en bas à droite, sélectionnez l’onglet “Points d’arrêt” et désactivez ou effacez le point d’arrêt de la première ligne de votre test unitaire.
  7. Cliquez sur l’icône “Redémarrez votre session de debug” (Ctrl+Maj+F5)
  8. Dans la fenêtre “Outils de diagnostic”
  9. Mettez un point d’arrêt sur la ligne qui suit l’appel à la fonction dont vous souhaitez tester les fuites.
  10. Cliquez sur le bouton “Continuer”
  11. Cliquez sur le bouton “Prendre un instantané” : une deuxième ligne d’instantané apparaît dans la fenêtre “Outils de diagnostic” avec des flèches rouge.
  12. Cliquez sur le lien à gauche de la première flèche rouge : un onglet “Instantané n°2” apparaît.
  13. Pour chaque type d’objet correspondant à votre code (par exemple, unitTests.exe!Circle[])
Visual Studio affichant le résultat de la différence d’instantanés

7.3.3 Dr Memory

L’outil Dr. Memory est sensé être un équivalent de Valgrind sous Windows. Mais il ne fonctionne pas correctement. Nous le mentionnons ici seulement pour information.