Déboguage
Outils et options de compilation pour éliminer certaines erreurs de programmation
1. Débogueurs
2. Déboguage
Introduction
Les débogueurs sont des auxiliaires précieux pour éliminer les erreurs dans les programmes.
Au premier abord, déboguer un programme consiste à l'arrêter sous certaines conditions pour examiner l'état de la pile d'appels et les valeurs stockées dans les variables.
On arrête l'exécution en insérant des points d'arrêt (breakpoints). Ces points d'arrêt peuvent être inconditionnels (ils engendrent toujours un arrêt du programme lorsqu'ils sont rencontrés) ou conditionnels (ils arrêtent le programme seulement si une condition est vérifiée, condition spécifiée par l'utilisateur).
Ils permettent aussi d'analyser les fichiers core générés lors d'un plantage. Pour cela on autorise la création de tels fichiers avec la commande ulimit coredumpsize.
L'utilisation d'un débogueur nécessite au moins l'ajout des l'options -qnooptimize et -qdbg à la compilation des fichiers sources. La première option inhibe les optimisations et la seconde entraine l'insertion d'informations dans l'exécutable qui permettent au débogueur de suivre les variables locales, les numéros des lignes, etc ...
Le déboguage d'un code est préliminaire et incompatible à toute optimisation.
Pour une liste détaillée des options conseillées pour le déboguage, consultez la page des compilateurs XLF ou XLC.
dbx est un débogueur symbolique textuel.
pdbx est un débogueur parallèle, une extension du débogueur standard, dbx.
dbx
dbx est un débogueur symbolique textuel.Il permet de déterminer l'origine d'un plantage en étudiant le fichier core pour analyser la pile d'appel du programme :
dbx mon_prog coreOn utilise la commande where au prompt qui apparait. En retour s'affiche la file d'appels des fonctions employées ainsi que le nom des fichiers sources et les numéros des lignes ou ont eu lieu ces appels :
(dbx) wheredbx travaille de manière optimale sur un code compilé avec les options -qnooptimize et -qdbg. Cela a pour effet d'inhiber les optimisations et de rajouter des informations de déboguage dans le code objet. Ces informations permettent à dbx de voir les variables locales et de déterminer les numéros des lignes du source. Autrement dbx ne pourra fournir que peu d'informations.
pdbx
Le débogueur parallèle est une extension du débogueur standard, dbx. Il permet de définir un "groupe de tâches parallèles" et d'utiliser les fonctions habituelles de déboguage sur une tâche, sur un ensemble de tâches prénommées (all, slaves, master) ou sur un groupe défini par l'utilisateur.
Il fonctionne en mode NORMAL, où l'utilisateur donne le nom du programme à déboguer, ou en mode ATTACH, où l'utilisateur décide en cours d'exécution d'un programme de lancer le débogueur sur une tâche précise.
Il supporte le déboguage de threads : variables, stack, breakpoints, tracepoints et code source.
Options de déboguage
La liste suivante fournit quelques conseils d'écriture de code pour avoir un code portable, lisible et réutilisable :- faire un code aéré avec des commentaires ;
- indenter les lignes du code ;
- mettre un sous-programme ou un bloc common par fichier ;
- déclarer explicitement l'ensemble des variables (locales et globales), des fonctions externes appelées, avec leur nom générique ;
- utiliser les types génériques pour les variables (REAL, INTEGER), voir les conseils pratiques (type de données des flottants) ;
- construire des blocs common par type de variables et par thème : CHARACTER et LOGICAL à part ;
- déclarer explicitement les dimensions des tableaux en Fortran (pas de * fourre-tout) et transmettre ces dimensions comme arguments lors des appels ;
- utiliser les clauses INTENT pour les programmes en Fortran 90.
- aucune optimisation de code : option -qnooptimize ;
- enrichir la table des symboles pour permettre la localisation de certaines erreurs ou/et l'utilisation efficace d'un débogueur symbolique (par exemple dbx) : option -qdbg ;
- recherche des variables non initialisées avant utilisation : option -qinitauto=ff, cette option initialise à NaN toutes les variables locales et automatiques mais ne détecte rien, pour cela il faut utiliser l'option -qflttrap (ci-dessous) ;
- recherche des débordements de tableau : option -qcheck ;
- vérification des séquences d'appels de sous-programmes : option -qextchk ;
- capture des signaux des exceptions : option -qsigtrap (empêche la création de fichier core) ;
- traçage des exceptions flottantes (opérations non conformes sur les nombres réels) : option -qflttrap=ov:und:zero:inv:en ;
- affichage de tous les messages de la compilation : option -qflag=i:i; on peut éliminer certains messages (warning) qui inondent les sorties à l'aide de l'option -qsuppress=nnnn-mmm. nnnn-mmm est le numéro du message et on peut préciser un seul numéro, soit plusieurs, intercalés avec des double-points ":" .
Résumé :
Une séquence d'options pour le déboguage pourrait donc être :
-qnooptimize -qcheck -qdbg -qextchk -qflttrap=:ov:und:zero:inv:en -qfullpath -qinitauto=FF
Exceptions flottantes
Les exceptions flottantes sont des résultats incorrects qui entrainent un surcoût cpu pour le traitement de l'opération. Ce surcoût peut être très important selon l'ampleur du phénomène. Cela peut se produire même si le code ne plante pas ou si les résultats semblent corrects (présence d'underflows).Il y a cinq types d'exception : underflow, overflow, divide-by-zero, invalid operation, inexact operation. Il y a aussi une sixième exception concernant les entiers : l'integer overflow.
De manière générale, la présence d'exceptions flottantes correspond à des erreurs dans le programme et il est impératif de les corriger avant de passer à l'exploitation du code. La rubrique précédente propose des outils pour détecter ces exceptions.
- underflow
- overflow
- divide-by-zero
- invalid operation
- inexact operation
- integer overflow
- tableau récapitulatif
| Calcul | résultat par défaut | type d'exception |
|---|---|---|
| grand * (- grand) | - inf | overflow, inexact |
| inf / inf | NaNQ | invalid, inexact |
| nombre / 0.0 | + inf | division par zéro |
| - nombre / 0.0 | - inf | division par zéro |
| 0.0 / 0.0 | NaNQ | invalid |
| petit * petit | 0.0 | underflow, inexact |
| inf * 0.0 | NaNQ | invalid |
| petit / grand | nombre subnormal | underflow, inexact |
| inf / 0.0 | inf | oveflow, inexact |
| 0.0 / inf | 0.0 | underflow, inexact |
| 2.0 / 3.0 | 2/3 (arrondi) | inexact |
- représentation des nombres
Par exemple, sous AIX les intervalles de valeurs pour les différents types de données sont :
| Type des données | base 2 | base 10 |
|---|---|---|
| integer(*4) | -2^31 < I < 2^31 | -10^9 < I < 10^9 |
| integer(*8) | -2^63 < I < 2^63 | -10^18 < I < 10^18 |
| real(*4) (i.e. real) | 2^(-125) < |R| < 2^128 | 10^(-38) < |R| < 10^38 |
| real(*8) (i.e. double precision) | 2^(-1021) < |R| < 2^1024 | 10^(-308) < |R| < 10^308 |
Sur un IBM p690, les underflows surviennent par exemple lorsque |R| < 10^(-38) en simple précision (soit 4 octets) ou 10^(-308) en double précision (soit 8 octets) et les overflows lorsque |R| > 10^38 en simple précision ou 10^308 en double précision.
- précision des nombres
Un nombre réel a comme particularité que son développement décimal peut être infini. Hors on ne peut pas garder toutes ses décimales : on ne peut en garder qu'un nombre fini, on fait donc un arrondi en tronquant son développement. En général, la simple précision correspond à environ 6-7 décimales, la double précision à environ 14-15 décimales et la quadruple à environ 30-31; à l'exception (liste non exhaustive) des machines Cray où la simple précision se situe vers 14-15 décimales et la double précision vers 30-31 décimales.
Lorsque l'on fait un calcul il est important de connaitre la précision employée, cela permet de relativiser le nombre de décimales significatives des résultats et de déterminer si une "petite" valeur a un sens ou bien s'il peut s'agir d'une erreur d'arrondi ou de troncature.