URL: https://linuxfr.org/users/krunch/journaux/printf-debugging-considered-harmful Title: printf debugging considered harmful Authors: Krunch Date: 2006年09月05日T19:09:05+02:00 Tags: Score: -1 Après avoir vainement cherché un « printf debugging considered harmful » j'en suis arrivé à la conclusion qu'il n'existait pas et j'ai donc décidé de l'écrire moi même. J'appelle « printf debugging » le fait d'ajouter du code temporaire dans le seul but de débugger du code existant. En C, le code ajouté étant généralement un appel à printf(3) permettant par exemple de retrouver quelle partie du code a été exécutée avant le segfault dont on cherche la cause. Cette technique est particulièrement populaire chez les programmeurs débutants qui n'ont pas envie d'apprendre à se servir d'un debugger « parce qu'ils s'en passent très bien » (moi aussi j'ai dit ça à une époque). Dans ce document je tente de montrer que le printf debugging pose un certains nombre de problèmes que n'ont pas d'autres méthodes. **$ while true; do vi && make && ./plop ; done** Le premier problème le plus évident qu'on rencontre quand on pratique le printf debugging est qu'il est nécessaire de recompiler et réexécuter le code concerné plusieurs fois. En effet, on trouve rarement le bug en rajoutant un seul printf, d'où plusieurs cycles d'édition, compilation, exécution qui font perdre un temps considérable. Le deuxième problème qui apparaît lorsque l'on a ajouté des printf un peu partout est de les retrouver tous pour les enlever une fois le bug corrigé. Ca semble trivial mais ça prend aussi du temps et il n'est pas rare d'en oublier l'un ou l'autre caché au fin fond d'une branche d'exécution qui sera prise trop rarement pour être remarqué rapidement. Une alternative permettant d'éviter ce problème est d'utiliser un système de logging permanent (dés)activable plus ou moins dynamiquement (via une variable d'environnement, un argument ou une option de compilation par exemple) associé à un système d'assertions. De plus ça incite à écrire des messages compréhensible plutôt qu'un « toto » qui n'aura plus de signification pour personne une fois la session de debugging finie. Il existe de nombreux systèmes de logging plus ou moins complexes mais pour commencer autant s'en tenir à un simple macro qui affiche ou non son argument selon que l'on veuille afficher les logs ou non [1]. **« C'est Heinsenberg qui est sur l'autoroute... »** [2] Un autre problème plus rare mais bien plus pervers est que l'ajout de code peut modifier ou masquer le bug. C'est ce qu'on appelle un heinsenbug : il devient inobservable quand on essaie d'en déterminer la nature mais réapparaît aussitôt le printf retiré. Ce genre de bug à tendance à se retrouver dans les programmes concurrents mais peut aussi être causé par de subtiles problèmes dans la gestion de la mémoire et sans doute d'autres choses. L'utilisation d'un debugger classique permet de retrouver un certains nombre de ces heinsenbugs mais pas tous. D'autres problèmes qui peuvent sembler plus triviaux sont liés au printf debugging. Il est par exemple impossible de debugger printf lui même avec cette méthode et les systèmes de cache la rendent souvent inutile si on n'en tient pas compte. En conclusion, le printf debugging peut être utile dans certains cas mais on rencontre vite ses limites qui peuvent être dépassées avec un usage judicieux d'un système de logging, d'assertions et d'un debugger. À lire aussi : « Debugging 101 » [3]. Cet article déconseille l'utilisation générale de debuggers « classiques » en faveur du prinf debugging mais je persiste à penser que l'utilisation d'un système de logging et d'un debuggers plus évolués [4] reste souvent préférable. Et je suis tout à fait d'accord avec l'utilisation du « design by contract » tel qu'expliqué (ainsi qu'avec la plupart du reste de l'article). [1] Personnellement en C99 pour des petits projets j'utilise ceci :
#ifndef NDEBUG # define debug(...) fprintf(stderr, __VA_ARGS__) #else # define debug(...) #endif
Quand on développe, on compile normalement et quand on passe en « production », on définit le macro NDEBUG qui éliminera aussi les assert(3). Le macro debug() s'utilise de la même manière que printf(3) mais on le laissera dans le code final. [2] Heinsenberg est sur l'autoroute au volant de sa voiture quand il se fait interpeller par un agent de police. « Vous savez à quelle vitesse vous rouliez ? » « Non, mais je sais où je suis » Si vous n'avez pas compris, vous devriez chercher de la documentation sur le principe d'incertitude d'Heisenberg. Je déconseille cette blague dans les soirées comportant moins de 50% de physiciens/chimistes/ingénieurs. [3] [http://www.hacknot.info/hacknot/action/showEntry?eid=85](http://www.hacknot.info/hacknot/action/showEntry?eid=85) [4] L'« Omniscient Debugger » par exemple permet de retourner en arrière dans l'exécution du code. [http://www.lambdacs.com/debugger/debugger.html](http://www.lambdacs.com/debugger/debugger.html) [http://video.google.com/videoplay?docid=3897010229726822034](http://video.google.com/videoplay?docid=3897010229726822034)

AltStyle によって変換されたページ (->オリジナル) /