Accueil > Réflexions > L’art de débugger

L’art de débugger

vendredi 23 février 2007, par Nicolas

Tout développeur est confronté au problème de la correction de bogues ou de comportements aberrants, que ce soit dans du code qu’il a écrit ou non.

Une telle correction peut en général se découper en plusieurs phases, parfois mêlées ou confondues.

La première phase est de comprendre le comportement anormal. Cela peut parfois être très simple (« l’application plante »), parfois un rapport de bogue écrit par un utilisateur n’a pas assez de détails (« le troisième alinea a une mauvaise forme »). Développeur et utilisateur ne parlent pas toujours le même langage, donc la communication est parfois difficile.

La deuxième phase est de savoir reproduire le bogue. Très souvent, elle va avec la première, car pour comprendre le comportement anormal le plus simple est de le reproduire et de constater de visu ce qui ne va pas.

La troisième phase est de savoir quel est le comportement attendu. Là, ça peut être plus compliqué à savoir. Si l’application a un cahier des charges, ce n’est pas forcément difficile, mais il existe parfois des mésententes entre la demande que le rédacteur voulait exprimer et celle que le développeur lit, sans même qu’il y ait de mauvaise foi de part et d’autre. Encore une fois, rédacteur et développeur ne parlent pas forcément le même langage, donc il faut clarifier au maximum.

Parfois, l’application n’a pas spécifiquement de cahier des charges. C’est un problème récurrent par exemple dans les projets Open Source, où des fonctionnalités sont développées puis le codeur s’éloigne du projet, sans avoir forcément tout documenté. Je développe pour un jeu nommé Crossfire, jeu de rôle assez standard. Parfois, un comportement est anormal (un objet ne se comporte pas comme il le faut), la cause en est connue (il manque un paramétrage pour l’objet), mais personne ne sait quelle est la correction adéquate, le paramétrage que le créateur de l’objet voulait donner. D’où discussion éventuelle entre les développeurs et les joueurs pour décider ce qu’il faut faire. Sachant que parfois la discussion n’est pas très active, et le premier à corriger décide de facto de la façon de corriger.

La quatrième phase est de comprendre ce qui cause le problème. Et là, il y a des fois des surprises. Sur Crossfire, il existe depuis plusieurs années un code de climat permettant de rendre les cartes un peu plus dynamiques et variables selon la saison dans le jeu. Ce code était anormal depuis plusieurs mois voire années, des arbres apparaissaient sur l’eau, du sol disparaissait, bref plein d’anomalies. Son code a été tracé, examiné, en vain. Les erreurs étaient en fait dans d’autres parties du code, sans aucun rapport. La première coupable était une fonction insérant des objets à un endroit de la carte, qui ne se comportait pas comme elle le devait. Comme seul le climat utilisait certains paramètres, l’anomalie n’avait pas été remarquée. Le second coupable était une fonction de concaténation de noms de chemins. Elle utilisait un buffer statique qu’elle retournait à son appelant avec un chemin [1]. Or le climat appelait plusieurs fonctions différentes, dont certaines appelant cette fonction de concaténation et changeant sa valeur. Au final, le climat travaillait à un moment sur une carte et à un moment sur une autre en pensant être sur la première. Bilan, des éléments supprimés ou insérés à des endroits incohérents car tests et insertion étaient sur deux cartes différentes.

La cinquième phase est la correction du bogue. Dans le cas cité auparavant, le buffer statique a été remplacé par un paramètre buffer que la fonction remplit, éliminant le cas d’écriture parasite [2]. Bien évidemment, il faut vérifier que la correction n’introduit pas de nouveaux bogues...

La sixième phase, enfin, est la validation par l’utilisateur de la validité de la correction. À ce niveau, il est parfois nécessaire de recommencer à la première phase, si la modification n’est pas satisfaisante.

Une septième phase, en général oubliée, est de documenter les modifications, tout au moins le comportement voulu par l’utilisateur ou décidé. Ceci permet de s’assurer que des développeurs futurs auront les informations nécessaires à la compréhension du code.

Un élément amusant est la répartition temporelle entre ces différentes phases. Le développeur, bien naturellemment, préfère souvent développer plutôt que discuter pour savoir le comportement attendu, surtout quand celui-ci prête à discussion. Les quatrième et cinquième phases sont donc souvent ses favorites. Mais, souvent, ce sont les autres phases qui demandent le plus de temps. Le pire des cas est celui où la cause du problème est comprise, la façon de corriger aussi (et en plus la correction est simple et sans effet de bord), mais il faut attendre de savoir comment corriger.


[1autrement dit, si l’appelant garde une référence vers le résultat retourné, le contenu ainsi référencé peut changer sans préavis. Ce qui bien sûr pose un souci.

[2chaque appelant fournit son emplacement privé où la fonction écrit le résultat, donc plus d’interférence.