Tips & Tricks

7 conseils essentiels pour déboguer en C++

Read this post in other languages:
English, 한국어, 简体中文

Nous avons eu le plaisir de nous entretenir avec Greg Law, qui a partagé avec nous ses conseils pour déboguer du code C++.

Greg Law Greg (@gregthelaw) est le cofondateur et dirigeant d’Undo. Programmeur expérimenté, ses activités se partagent aujourd’hui entre les domaines du logiciel et des affaires. Il apprécie tout particulièrement de contribuer à la distribution de technologies logicielles innovantes. Greg a plus de 20 ans d’expérience, à la fois dans le milieu universitaire et au sein de startups du secteur logiciel.

Brian Kernighan a déclaré : « Tout le monde sait qu’il est deux fois plus difficile de déboguer un programme que de l’écrire. Alors si vous mettez toute votre intelligence au service de l’écriture de votre code, comment allez-vous pouvoir le déboguer ? ». Selon moi, cela ne signifie pas seulement qu’il faille « rester simple » et écrire du code clair, facile à lire et à comprendre. Cela signifie également que le débogage est un élément central de la programmation : vous ne pouvez pas exceller dans la programmation sans exceller dans le débogage. J’espère que mes conseils de débogage pour C++ vous aideront à être aussi talentueux et productif lors du débogage de votre code que lorsque vous l’avez écrit.

Essayer le débogage dans CLion

#1 Disposer d’un kit d’outils de débogage complet

Toutes choses égales par ailleurs, un développeur disposant de bons outils évitera et résoudra les problèmes plus rapidement qu’un développeur moins bien équipé. Voici selon moi les 4 catégories d’outils de débogage que tout programmeur devrait savoir comment et quand utiliser. J’ai inclus dans cette liste des outils payants et des outils gratuits open source :

Catégorie  Comment il vous aide Exemples d’outils
Débogueur interactif Mettre en pause l’exécution et comprendre « Que fait mon programme ? » GDB, strace
Time travel debugger Revenir en arrière et avancer dans le temps d’exécution pour comprendre comment votre programme est arrivé là où il est. UDB, rr, WinDbg
Vérificateurs de code dynamiques Analyser ou instrumenter votre code pour vérifier les débordements de mémoire tampon et autres défauts. Valgrind, ASan
Vérificateurs de code statiques Analyser votre code pour déterminer s’il y a des risques de survenance de défauts spécifiques. Analyseur Clang et Clang-Tidy, Coverity, Cppcheck, linters intégrés à l’IDE

Lors de notre intervention à la conférence ACCU, Dewang Li de Coverity et moi avons passé en revue ces catégories d’outils et expliqué comment ils fonctionnent sous le capot.

Conseil d’Anastasia Kazakova, Chef de produit CLion chez JetBrains, sur l’utilisation du débogueur dans CLion : non seulement CLion vous permet de déboguer votre code avec les backends GDB ou LLDB, mais il facilite également l’utilisation d’autres outils, notamment l’intégration de Valgrind et Sanitizers, ainsi que différentes options pour l’analyse statique de code.

#2 Points d’arrêt conditionnels

Un point d’arrêt vous permet d’arrêter l’exécution du programme à une ligne ou à une fonction spécifique dans votre code. Lorsque votre programme atteint un point d’arrêt, il attend vos instructions pour inspecter ou manipuler l’état de l’application, reprendre l’exécution, etc.
J’utilise les points d’arrêt conditionnels pour déboguer plus efficacement. Au lieu de faire une pause à chaque fois que le point d’arrêt est atteint (ce qui peut être fastidieux si le point d’arrêt est défini dans une boucle), je peux définir une condition pour un point d’arrêt qui arrête l’exécution si cette condition est remplie. Par exemple, si la variable « i » est normalement égale à zéro, je peux vouloir arrêter l’exécution du programme si « i » n’est pas égale à zéro :

(gdb) break my_func if i!=0

Vous pouvez écrire pratiquement n’importe quelle condition de votre choix dans le langage de programmation du programme que vous déboguez, ce qui rend les points d’arrêt conditionnels très puissants et efficaces. La condition peut inclure un appel de fonction, la valeur d’une variable ou le résultat de toute expression GDB.
Pour en savoir plus, regardez ce tutoriel vidéo dans lequel je présente les points d’arrêt conditionnels, ainsi que plusieurs autres types de points d’arrêt, et explique comment utiliser chacun d’eux pour le débogage.

Conseil d’Anastasia sur l’utilisation du débogueur dans CLion : Dans CLion, vous pouvez facilement spécifier une condition à contrôler à chaque fois qu’un point d’arrêt est atteint.

#3 Points de surveillance

Comme le point d’arrêt, un point de surveillance arrête l’exécution, mais il le fait chaque fois que la valeur d’une expression change, sans que le programmeur n’ait à prévoir précisément à quelle ligne de code cela peut se produire. Les points de surveillance sont extrêmement utiles pour déboguer en cas de problème de concurrence, par exemple lorsqu’on essaie de comprendre quel thread ou processus modifie une ressource partagée. L’expression peut être simple (la valeur d’une seule variable) ou complexe (de nombreuses variables combinées par des opérateurs). Quelques exemples :

  • Une référence à la valeur d’une seule variable.
  • Une adresse convertie en un type de données approprié. Par exemple, *(int *)0x12345678 surveillera une zone de 4 octets à l’adresse spécifiée (en supposant qu’un int occupe 4 octets).
  • Une expression arbitrairement complexe, telle que a*b + c/d. L’expression peut utiliser tous les opérateurs valides dans le langage natif du programme.

Voici un article de blog et une vidéo expliquant comment utiliser les différents types de points de surveillance.

Conseil d’Anastasia sur l’utilisation du débogueur dans CLion : Si vous vous demandez comment ajouter un point de surveillance dans CLion, consultez l’aide en ligne.

#4 Commandes de débogage définies par l’utilisateur en Python

Je vous recommande de personnaliser votre débogueur en fonction de votre projet et de votre équipe. Dans GDB, vous pouvez le faire en créant des commandes définies par l’utilisateur en Python. Vous pouvez effectuer toutes sortes d’actions intelligentes pour faciliter la détection de bugs épineux (et leur résolution). Il y a aussi beaucoup de moyens de personnaliser GDB en fonction de votre projet et de vos besoins pour le débogage.
Il serait dommage de ne pas tirer parti de Python et de vous priver de la possibilité d’augmenter la vitesse de votre débogage et de vous simplifier la vie ! C’est un petit investissement en temps qui en vaut vraiment la peine et qui sera rapidement rentabilisé.
Par exemple, vous pouvez automatiser une tâche telle que la vérification de la sortie du débogueur dans le contrôle de la source et la partager avec les membres de votre équipe. Dans cet article, je montre comment l’intégration de Python avec GDB peut être utilisée à cette fin.

Conseil d’Anastasia sur l’utilisation du débogueur dans CLion : Saviez-vous que pendant une session de débogage vous pouvez accéder à la console GDB/LLDB directement depuis CLion ? L’onglet montre le flux de sortie/erreur du débogueur et vous permet d’exécuter des commandes GDB/LLDB.

#5 Structures pretty-print

L’affichage des variables, des structures et des classes est une part importante du débogage. Par défaut, le débogueur peut ne pas afficher à l’écran la valeur d’une manière qui soit facile à comprendre pour le développeur.
Par exemple, si j’affiche la structure siginfo_t, la commande print renvoie toutes les données de la structure et développe les unions qu’elle utilise :
Désordonné et difficile à lire !
Heureusement, GDB peut être étendu avec les fonctions « pretty-printer ». Lorsque GDB imprime une valeur, il vérifie s’il y a un pretty-printer enregistré pour cette valeur. Si c’est le cas, GDB l’utilise pour afficher la valeur. Sinon, la valeur s’affiche comme de manière habituelle.
La création de la fonction pretty-printer requiert un peu de codage en amont, mais elle vous évitera de rester trop longtemps les yeux rivés à votre écran par la suite. Voici une vidéo dans laquelle je présente une façon rapide de configurer un pretty-print de structure dans GDB.

Conseil d’Anastasia sur l’utilisation du débogueur dans CLion : Si vous ajoutez des pretty-printers personnalisés à GDB, CLion les utilisera par défaut.

#6 Time Travel Debugging

Il arrive souvent que les développeurs aient besoin de savoir ce que leurs programmes ont réellement fait par rapport à ce qu’ils étaient censés faire. C’est pourquoi le débogage implique généralement de reproduire un bug de nombreuses fois, en collectant progressivement de plus en plus d’informations jusqu’à ce quel’on puisse trouve la cause.
Le débogage dans le temps élimine toutes ces conjectures et ces tâtonnements : le débogueur peut vous dire directement ce qui vient de se passer.
Des débogueurs gratuits comme GDB ont une fonction Time Travel Debugging intégrée. Cela fonctionne assez bien, mais vous devez être prêt·e à sacrifier une grande partie des performances. Les débogueurs payants conçus pour permettre le débogage dans le temps, comme UDB, offrent des performances beaucoup plus rapides.
Le processus est identique à celui d’un débogage classique, si ce n’est que vous pouvez enregistrer l’exécution d’un programme en cours d’exécution, puis reculer ou avancer dans le temps. Les points d’arrêt et les points de surveillance fonctionnent en sens inverse. Cela permet par exemple de passer directement à un point précédent dans le programme où une variable spécifique change. Les points de surveillance inversés peuvent être incroyablement puissants. J’ai vu plusieurs cas dans lesquels des bugs ayant échappé à un développeur pendant des mois, voire des années, ont été résolus en quelques heures grâce aux points de surveillance inversés.
Dans cette vidéo, je présente le débogage inversé dans GDB. Jetez-y un œil : il vous montre étape par étape (en sens inverse) comment faire le suivi d’un défaut dans mon programme.

Conseil d’Anastasia sur l’utilisation du débogueur dans CLion : Undo a créé un plugin CLion pour le Débogueur Time Travel d’UDB. Consultez notre précédent article en collaboration avec l’équipe d’Undo pour en savoir plus.

#7 Commande find pour rechercher une séquence d’octets

Parfois durant débogage vous pouvez avoir besoin de trouver une séquence d’octets en particulier dans l’espace mémoire du programme. Peut-être voulez-vous voir tous les pointeurs vers un objet spécifique. Ainsi, chacun des huit octets de la mémoire qui correspond à la séquence d’octets constitue une adresse que vous voulez identifier.

La commande find de GDB vous fournit un autre type d’inspection de votre programme. Toutes les valeurs de recherche sont interprétées dans le langage de programmation du programme. Par exemple, le langage source de hello.c est le C/C++. Ainsi, si nous recherchons la chaîne de caractères « Hello, world ! », elle inclut le '\0' de fin.

GDB fournit également des informations sur les mappages de mémoire des processus du programme, ce qui vous aide à concentrer la recherche dans certains segments de la mémoire du programme. L’adresse de chaque correspondance trouvée et le nombre de correspondances sont renvoyés.

Vous voulez d’autres conseils de débogage en C++ ?

J’ai partagé sept conseils pour améliorer la productivité du débogage en C++. J’espère qu’ils vous seront utiles. Parfois, il suffit de quelques petits ajustements dans notre routine de débogage pour faire une grande différence. N’hésitez pas à partager ces conseils ! J’aimerais également connaître vos astuces. Vous pouvez me contacter sur Twitter ou sur LinkedIn.
Voici quelques ressources sur le débogage qui pourront vous être utiles :

  • GDBWatchpoint : Abonnez-vous à mon blog sur undo.io
  • Obtenir GDB : Débogueur C/C++ gratuit
  • UDB : Débogueur Time Travel (essai gratuit de 30 jours)

Et quelques ressources supplémentaires concernant CLion, l’IDE de JetBrains :

Merci à Greg d’avoir partagé ces conseils !

Et vous, quelle est votre astuce de débogage préférée ? N’hésitez pas à nous le dire dans les commentaires !

Auteur de l’article original en anglais :

 

Delphine Massenhove

Anastasia Kazakova