Derniers journaux de Krunch :

Journal : printf debugging considered harmful

Posté par Krunch (Jabber id, page perso, ) le 05 septembre 2006
0

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

[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://video.google.com/videoplay?docid=3897010229726822034

> Lire le journal (118 commentaires, moyenne: 3,3).  

Cette discussion est archivée, il n'est plus possible de laisser des commentaires.

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.

exceptions

Posté par Vincent BRZUSCZAK (page perso, ) le 05/09/2006 à 18:12. (lien). Évalué à 2.

Une bonne solution pour faire du "débugging préventif" est d'utiliser les exceptions (quand le language le permet (en C++/C# en tout cas)), ca permet sans entrer dans le débuggeur, et sans devoir se tracasser de la reproductibilité d'un bug de savoir ou se trouve la segfault ou de savoir ou qqch se passe mal.

C'est pratique, mais un débutant ne s'intéresse pas toujours au sujet.

  • [^]Re: exceptions

    Posté par Troy McClure (page perso, ) le 05/09/2006 à 18:26. (lien). Évalué à 2.

    en gros tu parles des assert() du C ? Si c'est ça je suis totalement d'accord, j'utilise principalement ça comme garde-fous. Et je debuggue au printf oui monsieur, gdb je ne le sors en gros que pour avoir une backtrace en cas de segfault ou de division par zero et autres évenements deplaisants.

    D'ailleurs l'auteur du journal a oublié de parler du debugger qu'il utilise. gdb en ligne de commande sous HPUX ou le debugger de visual studio sous windows vista ?

re

Posté par Sylvain (Jabber id, page perso, ) le 05/09/2006 à 18:33. (lien). Évalué à 2.

Quelqu"un peut expliquer le heisenberg ? ^^"

  • [^]Re: re

    Posté par Sylvain (Jabber id, page perso, ) le 05/09/2006 à 18:36. (lien). Évalué à 3.

    jai trouvé ca :
    Ce principe, énoncé en 1927 par le physicien allemand Karl Werner Heisenberg, nous indique les limites sur la précision de mesure que l’on puisse obtenir sur l’information d’un système donné.

    Le monde quantique échappe à toutes nos tentatives de le délimiter dans une zone précise de l’espace : lorsqu’on essaie de mesurer la position d’une particule avec une grande précision, l’information sur sa vitesse est incertaine.

    Et inversement, lorsqu’on veut connaître sa vitesse avec une précision accrue, sa position devient floue... Il y a une limite infranchissable à la connaissance que l’on puisse obtenir sur l’information d’un système; cette limite est connue sous le nom du principe d’incertitude.

    Mais attention : cette imprécision n’est pas due à l’imperfection des appareils de mesure, c’est une réalité intrinsèque du monde atomique. Bien évidemment dans notre monde ce principe d’incertitude ne s’applique pas. On peut par exemple connaître à la fois et avec une grande précision la vitesse et la position d’une voiture.


    -----------------------------
    Donc ca serait la complexité de determiner la vitesse ET la position ?

    • [^]Re: re

      Posté par Wawet76 (page perso, ) le 05/09/2006 à 19:59. (lien). Évalué à 2.

      Je ne suis pas un pro de la physique, mais je pense qu'il faut plutôt dire que c'est intrinsèque aux théories quantiques actuelles plutôt qu'à la réalité du monde atomique. Ou alors, intrinsèque à la réalité présumée du monde atomique.

    • [^]Re: re

      Posté par fabien () le 05/09/2006 à 22:02. (lien). Évalué à 4.

      en plus light :
      le fait d'observer un objet (de l'echelle de l'atome) influe sur lui-même,

      en moin light : c-a-d que le produits des incertitudes (des mesures) est de l'ordre de la constante de planck.

  • [^]Re: re

    Posté par account () le 06/09/2006 à 07:46. (lien). Évalué à 6.

    Fallait regarder Numb3rs saison 1 épisode 2 ;-)

  • [^]Re: re

    Posté par _Mekare_ () le 06/09/2006 à 21:13. (lien). Évalué à 2.

    En fait lorsque tu détectes un objet c'est parce qu'il réfléchit la lumière. C'est à dire que des photons le touchent et ricochent vers toi.

    Imagine que tu veux déterminer la position et la vitesse d'un atome. De la même façon tu vas l'"éclairer". Sauf que ton photon est aussi une particule (enfin y a la dualité mais bon simplifions). Donc que va faire ton photon en ricochant sur ton atome ? Et bien il va le dévier et donc modifier sa vitesse. Ainsi tu sauras où il est mais tu ne pourras plus déterminer sa vitesse (en tout cas plus celle qu'il avait avant ta mesure).

    Mekare

    • [^]Re: re

      Posté par L () le 06/09/2006 à 23:08. (lien). Évalué à 3.

      Ton explication est largement plus sombre que la définition Wikipédia et je ne pense pas que ce sont tes photons qui vont mieux l'éclairer /o\

      • [^]Re: re

        Posté par Mathieu Stumpf (Jabber id, page perso, ) le 09/09/2006 à 10:23. (lien). Évalué à 2.

        Moi je les trouves plutot complémentaire ces deux explications...

        • [^]Re: re

          Posté par Jerome Herman () le 10/09/2006 à 00:59. (lien). Évalué à 1.

          Moi je les trouves plutot complémentaire ces deux explications...

          Mouais, faut le dire vite.
          Le photon qui "ricoche" c'ets quand même une approximation plus que grossiére. Un photon ca va tout droit. Eventuellement c'est absorbé puis un autre photon est réémis dans une autre direction mais c'est pas vraiment le même photon.

          --
          Kha
          root est un privilège, pas un droit !
          • [^]Re: re

            Posté par Sebastien Binet () le 10/09/2006 à 03:42. (lien). Évalué à 1.

            Le photon qui "ricoche" c'ets quand même une approximation plus que grossiére. Un photon ca va tout droit.

            Ceci est egalement une approximation grossiere :). Un photon ne va pas tout droit. Il suit les geodesiques de l'espace temps (c'est beau... mais surement inexact, ma relat' G est "un peu" rouillee).
            Du coup, un "rayon lumineux" est devie aux environs d'un champ de matiere intense (genre une galaxie, un trou noir,...) qui modifie la courbure de l'espace-temps.
            C'est ce qui est appele "lentilles gravitationnelles" ou "anneaux d'Einstein".

            Une rapide recherche donne:
            http://www.techno-science.net/?onglet=news&news=2035
            http://commons.wikimedia.org/wiki/Image:Einstein_rings_zoom.(...)
            http://www.futura-sciences.com/news-hubble-observe-lentilles(...)

            • [^]Re: re

              Posté par Krunch (Jabber id, page perso, ) le 10/09/2006 à 10:56. (lien). Évalué à 3.

              Un photon ne va pas tout droit. Il suit les geodesiques de l'espace temps
              Donc par rapport au référentiel de l'espace-temps il va tout droit non ?

              --
              Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
              pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
              • [^]Re: re

                Posté par Sebastien Binet () le 10/09/2006 à 21:16. (lien). Évalué à 1.

                A pinailleur, pinailleur et demi :)

            • [^]Re: re

              Posté par Jerome Herman () le 11/09/2006 à 08:46. (lien). Évalué à 2.

              Si si le photon va tout droit.
              De toute façon vu qu'il n'existe qu'en deux dimension il aurait du mal à tourner.
              Après çà si un observateur a l'impression que le photon tourne c'est que l'espace est courbe (pour le temps le photon s'en fout, ca ne fait pas partie des deux dimension qu'il connait).

              A noter qu'apparament ca marche aussi à proximité des neutrinos, ce qui évite d'avoir un déranger une galaxie pour rien.

              --
              Kha
              root est un privilège, pas un droit !
              • [^]Re: re

                Posté par Mathieu Stumpf (Jabber id, page perso, ) le 11/09/2006 à 09:18. (lien). Évalué à 2.

                Bah comment il fait pour se déplacer dans un espace à deux dimensions ton photon? J'ai jamais eu de cours sur la relativité tout ça, mais pour qu'il y ai un déplacement y faut au moins deux instants t différents non? Donc le photon y s'en fout pas tant que ça je pense.

                Ou alors les deux dimensions dont tu parles, est la première une dimensions spatial (un ligne) et l'autre le temps. Sinon je vois pas comment il peut y avoir un déplacement. Non?

                • [^]Re: re

                  Posté par Jerome Herman () le 11/09/2006 à 11:00. (lien). Évalué à 3.

                  Bah comment il fait pour se déplacer dans un espace à deux dimensions ton photon?

                  Le photon est un phénomène purement spatial, le temps ne s'écoule pas pour le photon. Le fait que le photon se "déplace" est lié à l'observateur.
                  En fait plus on se déplace vite, moins le temps s'écoule par rapport à un observateur fixe. C'est le principe de la théorie de la relativité.

                  Le photon ne connait que sa direction, son sens et son amplitude (sa longeur d'onde).

                  --
                  Kha
                  root est un privilège, pas un droit !

Comment profiter pleinement des possibilités de vim.

Posté par apom () le 05/09/2006 à 19:11. (lien). Évalué à 0.

$ while true; do vi && make && ./plop ; done

Fais-tu vraiment ça ?

  • [^]Re: Comment profiter pleinement des possibilités de vim.

    Posté par Krunch (Jabber id, page perso, ) le 05/09/2006 à 19:17. (lien). Évalué à 9.

    Non t'as raison j'ai oublié de passer le fichier en argument à vi.

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.

glib, je t'aime

Posté par Étienne Bersac (Jabber id, page perso, ) le 05/09/2006 à 19:18. (lien). Évalué à 2.

J'utilise g_debug et gdb suivant le cas. Le seul truc que j'aimerai bien, c'est un g_debug qui affiche __FUNCTION__:__LINE__ ou __FILE__:__LINE__. Mais bon, on a pas tout.

Ah oui, avec apport, j'ai un backtrace dès que le programme plante, sans avoir à relancer le programme dans gdb :).

Étienne.

--
E Ultreïa !
  • [^]Re: glib, je t'aime

    Posté par Krunch (Jabber id, page perso, ) le 05/09/2006 à 20:53. (lien). Évalué à 6.

    La libc GNU permet aussi d'avoir un backtrace depuis le programme lui même (sans fork()er gdb par contre). Personnellement je trouve ça assez gadget. Pourquoi rajouter du code si on peut le faire avec un programme externe (et quelques lignes dans le Makefile si on est vraiment fainéant) ?

    http://www.gnu.org/software/libc/manual/html_node/Backtraces(...)

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
    • [^]Re: glib, je t'aime

      Posté par galactikboulay () le 06/09/2006 à 06:10. (lien). Évalué à 1.

      Ah c'est génial, je te pertinente allègrement pour ce lien. Je m'étais souvent fait la remarque que ça serait vraiment pratique d'avoir ce genre d'info, je ne pensais pas que c'était possible directement et aussi simplement.

Claimed truth considered harmful

Posté par calandoa () le 05/09/2006 à 19:30. (lien). Évalué à 10.

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.

Je dirais plutôt que le fait que ce texte n'existe pas est une observation. Une conclusion serait par exemple que tu es le seul bon programmeur sur le net. Une autre serait qu'utiliser des printfs pour debuger un programme est considéré comme tout à fait correct par la majorité des programmeurs.

J'appuierai d'ailleurs cette deuxième conclusion en ajoutant que, même si tes remarques sont pertinantes, utiliser un debugger apporte aussi son lot d'inconvénients et n'est pas forcement un meilleur choix.

D'ailleurs, pour trouver un bug, même les solutions les pires sont acceptables si les meilleures n'ont rien donné...

  • [^]Re: Claimed truth considered harmful

    Posté par Krunch (Jabber id, page perso, ) le 05/09/2006 à 22:20. (lien). Évalué à 0.

    Je dirais plutôt que le fait que ce texte n'existe pas est une observation. Une conclusion serait par exemple que tu es le seul bon programmeur sur le net. Une autre serait qu'utiliser des printfs pour debuger un programme est considéré comme tout à fait correct par la majorité des programmeurs.
    C'est pas parce qu'un tel article existerait déjà que celui qui l'aurait écrit serait un bon programmeur. Il y a plein d'articles très mauvais écrits par des mauvais programmeurs. Que cet article ci soit bon ou non, c'est au lecteur de juger mais si l'article existait déjà je n'aurais pas eu à l'écrire. L'intérêt de l'avoir écrit c'est que maintenant qu'il existe, j'aurai juste à ressortir le lien quand le sujet sortira dans une conversation plutôt que d'expliquer mon point de vue pour la 42ème fois.

    utiliser un debugger apporte aussi son lot d'inconvénients et n'est pas forcement un meilleur choix.

    D'ailleurs, pour trouver un bug, même les solutions les pires sont acceptables si les meilleures n'ont rien donné...
    C'est bien comme ça que je l'entend. Il n'y a pas de solution miracle. Mon journal tente de montrer au programmeur débutant qui ne connait que le printf debugging les limites de cette méthode.

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
  • [^]Re: Claimed truth considered harmful

    Posté par Christophe Fergeau () le 07/09/2006 à 09:58. (lien). Évalué à 2.

    Il me semble que Linus est vachement plus fan du printf(k) debugging que du traçage à coup de gdb si mes souvenirs sont bons ;)

    • [^]Re: Claimed truth considered harmful

      Posté par Jerome Herman () le 07/09/2006 à 11:07. (lien). Évalué à 4.

      Il me semble que Linus est vachement plus fan du printf(k) debugging que du traçage à coup de gdb si mes souvenirs sont bons ;)

      D'un autre coté il a pas trop le choix. GDB en kernelspace ca le fait pas trop...

      --
      Kha
      root est un privilège, pas un droit !
      • [^]Re: Claimed truth considered harmful

        Posté par woopla () le 09/09/2006 à 20:31. (lien). Évalué à 1.

        GDB non, mais il existe kgdb : http://kgdb.linsyssoft.com

        --
        Love is like a trampoline, first it's like "SWEET!!" then it's like *BLAMM!*

J'ai eu ma période ...

Posté par √λιi () le 05/09/2006 à 19:32. (lien). Évalué à 10.

Mais parfois on a pas trop le choix : Pendant un stage, je devais faire une palette pour interface builder sous MacOSX (un extension de l'éditeur d'UI en gros). Le hic c'est que je codais ca sur un iMac G3 avec 392Mo de RAM, sous Tiger ... et pour débugger ma palette, il fallait débugger Interface Builder en entier avec XCode, les connaisseurs souriront en disant "le pauvre". Du coup la méthode la plus rapide pour certains bug était le printf (enfin NSLog() ici plutôt :) ) logging, mais grace a mon expérience de débutant en C passée, j'avais quelques bonne techniques, triviales, mais je vais les partager tout de même. Comme tu le dis une bonne vieille macros avec des __VA_ARGS__ roxor, mais très vite ca ne suffit plus, et on ne peut pas profiter facilement des macros __FUNCTION__, __FILE__, __LINE__ a moins de se les taper a chaque fois. donc voilà quoi :

#  define debug(...) fprintf(stderr,"%s : %s, line %i : ",\
                            __FILE__, __FUNCTION__, __LINE__);\
                            fprintf(stderr, __VA_ARGS__)
 
Bon après tu peux y aller encore plus comme un bourrin et afficher direcetement ton code (bien plus rapide que de saisir un rapide descriptif donné a ta macro, le dévellopeur moyen se souvient de ce qu'il a écrit).
#  define debug(CODE, ...) fprintf(stderr,"%s::%s, line %i : ",\
             __FILE__, __FUNCTION__, __LINE__);\
             fprintf(stderr, "\"%s\" - ", #CODE);\
             fprintf(stderr, __VA_ARGS__);\
             CODE
 
Exemple : debug(appelFonction(param), "param : %s", param); te donnera comme sortie a l'exécution :
main.c::mafonction, line 23 : "appelFonction(param)" - param : valparam
 
après tu peux faire des truc sympa comme du avant/après :
#  define debug(CODE, ...) fprintf(stderr,"%s::%s, line %i : ",\
             __FILE__, __FUNCTION__, __LINE__);\
             fprintf (stderr, "\nbefore : ");\
             fprintf(stderr, __VA_ARGS__);\
             CODE;\
             fprintf (stderr, "\nafter : ");\
             fprintf(stderr, __VA_ARGS__)
 
et bien sûr tu peux toujours faire:
#if defined (DEBUG)
#  define debug(CODE, ...)   fprintf(stderr,"%s::%s, line %i : ",\
             __FILE__, __FUNCTION__, __LINE__);\
             fprintf (stderr, "\nbefore : ");\
             fprintf(stderr, __VA_ARGS__);\
             CODE;\
             fprintf (stderr, "\nafter : ");\
             fprintf(stderr, __VA_ARGS__)
#else
#  define debug(CODE, ...)   CODE
#endif
 
Bon après y'a moyen de se faire haïr par tout les relecteurs, donc bien penser a tout nettoyer :)

ouais

Posté par boklm (page perso, ) le 05/09/2006 à 19:37. (lien). Évalué à 9.

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).

Programmeurs débutants du genre Linus Torvalds :)

http://linuxmafia.com/faq/Kernel/linus-im-a-bastard-speech.h(...)

  • [^]Re: ouais

    Posté par Gregplus () le 05/09/2006 à 19:52. (lien). Évalué à 4.

    Le passage intéressant :


    I happen to believe that not having a kernel debugger forces people to
    think about their problem on a different level than with a debugger. I
    think that without a debugger, you don't get into that mindset where you
    know how it behaves, and then you fix it from there. Without a debugger,
    you tend to think about problems another way. You want to understand
    things on a different _level_.


    Il dit en gros que lorque l'on utilise un debugger, on a tendance à fixer les bugs un peu vite sans forcément tout bien voir comme il faut.

    En tout cas moi j'ai toujours été étonné du nombre de développeurs qui ne savent pas utiliser un debugger ou n'en voient pas l'intéret, je dirais que ça représente au moins 80% des gens avec qui j'ai bossé.

    • [^]Re: ouais

      Posté par Krunch (Jabber id, page perso, ) le 05/09/2006 à 20:11. (lien). Évalué à 5.

      Un peu comme le « Avoid debuggers » de hacknot.info :

      I've noticed that habitual use of symbolic debuggers also tends to discourage serious reflection on the problem. It becomes a knee-jerk response to fire up the debugger the instant a bug is encountered and start stepping through code, waiting for the debugger to reveal where the fault is.
      Un debugger n'est pas une solution miracle non plus, c'est qu'un outil qui peut aider quand on l'utilise intelligemment (j'ai aussi l'impression que « single-stepper » est rarement un bon moyen de trouver un bug). Dans le mail de Torvalds je ne vois pas non plus où il dit que le printf debugging c'est supair.

      --
      Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
      pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
    • [^]Re: ouais

      Posté par peau chat () le 06/09/2006 à 08:02. (lien). Évalué à 5.

      En tout cas moi j'ai toujours été étonné du nombre de développeurs qui ne savent pas utiliser un debugger ou n'en voient pas l'intéret, je dirais que ça représente au moins 80% des gens avec qui j'ai bossé.


      Ben faut dire que quand tu cherches un bug dans une application multithreadée avec tâches concurrentes, des communications entre objets par signaux/slots et des communications interprocessus genre avec un DCOP serveur ou un kioslave, le débugger ça fait plus perdre de temps qu'autre chose.

      Et si ton bug est un race condition, l'utilisation d'un debugger peut aussi induire des Heisenbugs...

      • [^]Re: ouais

        Posté par Gabriel Linder () le 06/09/2006 à 10:25. (lien). Évalué à 4.

        Un bon outil pour détecter les race conditions : Valgrind (plugin Helgrind, malheureusement il ne fonctionne plus depuis la version 3.1.0, mais les devs bossent dessus). Il exécute le programme sans avoir besoin de compiler ou linker avec des libs spéciales (en fait, il émule un CPU et détecte les accès en mémoire non protégés), c'est pratique mais c'est très lent. Le plugin Memcheck est aussi un indispensable pour les programmeurs C/C++ ;)

        Si vous en connaissez d'autres, je suis preneur :)

      • [^]Re: ouais

        Posté par lasher () le 06/09/2006 à 10:42. (lien). Évalué à 4.

        « Ben faut dire que quand tu cherches un bug dans une application multithreadée avec tâches concurrentes, des communications entre objets par signaux/slots et des communications interprocessus genre avec un DCOP serveur ou un kioslave, le débugger ça fait plus perdre de temps qu'autre chose. »

        Sauf qu'en fait, dès que tu passes à du multi-process/multi-thread, l'utilisation de printf sur la sortie standard est plutôt déconseillée, vu que tu ne maîtrises pas l'ordre d'affichage des messages et que tu peux même en avoir certains qui passent à la trape.

        • [^]Re: ouais

          Posté par Laurent Pointal (page perso, ) le 06/09/2006 à 11:21. (lien). Évalué à 3.

          Ben là, tu reroutes vers un système de logging (avec son exclusion mutuelle à lui) qui assure que les logs sont bien stockés qq part en cas de crash - et bien sûr, tu perturbes un peu le fonctionnement de l'appli :-)

          --
          (pub: Livres à prix réduit sur http://www.sollire.com/ - la boutique de mes petites soeurs)
          • [^]Re: ouais

            Posté par Laurent A. () le 07/09/2006 à 20:58. (lien). Évalué à 1.

            Et sur cette même fonction de log, tu peux aussi provoquer un deadlock !

            Ça paraît difficile à première vue, et ça l'ait, néanmoins je suis tombé récemment sur ce cas parce que le programme attrapait des signaux et déroutait l'exécution du thread receveur. Le thread receveur avait fait un lock() juste avant d'être dérouté, ce qui provoquait donc un blocage lorsqu'un autre thread voulait enregistrer quelque chose dans le log...

            La solution consistait à bloquer les signaux pour tous les threads, sauf un.

        • [^]Re: ouais

          Posté par Jerome Herman () le 06/09/2006 à 11:41. (lien). Évalué à 6.

          vu que tu ne maîtrises pas l'ordre d'affichage des messages

          Les rares fois ou j'ai debugé du multithreadé avec des printf sur la sortie standard, j'étais bien content de ne pas maitriser l'ordre d'affichage. En fait c'était même un peut le but. C'est là qu'on arrivait à de jolis use-case avec un affichage qui permettait de savori quel thread avait fait quoi avant/après tel autre thread.
          Le gros défaut de la sortie standard c'est qu'ecrire dessus c'est non atomique, il peut donc se passer des choses entre le début et la fin de l'écriture (interrupt, segfault d'un autre thread qui plie le process etc.)

          Gros problème pour faire du debug avec exclusivement des commandes atomiques il faut se lever de bonne heure (que ce soit avec printf, GDB ou des buffers extérieurs).
          On peut tricher avec un process extérieur et des IPC mais généralement on obtient des résultats assez variables.
          L'idéal c'est un simulateur qu'on peut mettre en pause, retour arrière, avance rapide quand on veut. Mais là il vaut mieux avoir de la ram et de la puissance CPU à disposition, parceque ca risque de ramer (surtout si vous avez des dizaines et des dizaines de threads).

          Pour finir il est important de bien se rendre compte d'un chose : il ets impossible d'écrire un debuggueur parfait. Etre capable d'écrire un programme qui va trouver toutes les failles dans n'importe quel code est équivalent à écrire un programme capable de detecter si n'importe quel code va se terminer. Pour debugguer rien en remplace donc le cerveau. Certaines méthodes vont marcher, d'autres pas du tout mais il est assez difficile de savoir à priori lesquelles.

          --
          Kha
          root est un privilège, pas un droit !
          • [^]Re: ouais

            Posté par Thomas Douillard () le 06/09/2006 à 14:58. (lien). Évalué à 3.

            Pour finir il est important de bien se rendre compte d'un chose : il ets impossible d'écrire un debuggueur parfait. Etre capable d'écrire un programme qui va trouver toutes les failles dans n'importe quel code est équivalent à écrire un programme capable de detecter si n'importe quel code va se terminer.

            C'est bien pire que ça, un debugger parfait devrait "deviner" ce que tu veux faire pour savoir si tu le fais correctement ;)
            Les "prouveurs" automatique se basent sur une spécification formelle pour vérifier la correction du code.


            Sinon, une autre solution pour éviter certaines failles est d'utiliser des langages qui en réduisent le nombre, en garantissant certaines choses (l'utilisation obligatoire d'utiliser des iterateurs sur un tableau pour éviter les dépacements de capacité pour éviter les débordements, ce genre de chose). D'utiliser des langages qui peuvent garantir certaines propriétés en somme. Inutile de chercher des erreurs que le langage ne permet pas de faire, surtout si trouver la totalité de ce genre d'erreurs est indécidable ^^

            A nuancer cependant, c'est pas parce que c'est pas décidable dans le cas général qu'on construit systématiquement des programmes pour lesquels il est impossible de décider : les preuves d'indécidabilités sont souvent construites sur des cas extrêmes spécialement tordus et construits pour.

            • [^]Re: ouais

              Posté par Matthieu Moy (page perso, ) le 07/09/2006 à 09:38. (lien). Évalué à 3.

              Euh, oui, sauf que là, tu confonds un déboggueur et un prouveur.

              Le déboggueur, il ne trouve pas les bugs à ta place, il te donne une execution instrumentée et/ou pas à pas de ton programme. C'est de la pure technique, rien à voir avec les problèmes de décidabilité théoriques.

              • [^]Re: ouais

                Posté par Thomas Douillard () le 07/09/2006 à 10:00. (lien). Évalué à 2.

                Complètement exact, mais à ma décharge, jérome parlait d'un debugger qui trouvait les failles, ce qui n'est pas son rôle.

                • [^]Re: ouais

                  Posté par Matthieu Moy (page perso, ) le 07/09/2006 à 11:54. (lien). Évalué à 2.

                  J'avais pas fait gaffe, je répondais à ta citation en fait ;-).

  • [^]Re: ouais

    Posté par Krunch (Jabber id, page perso, ) le 05/09/2006 à 20:17. (lien). Évalué à 4.

    Ben il dit bien qu'il sait se servir de gdb et qu'il l'utilise tout le temps. Juste qu'il l'utilise pas n'importe comment.

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
  • [^]Re: ouais

    Posté par patrick_g (page perso, ) le 05/09/2006 à 22:28. (lien). Évalué à 10.

    >> Programmeurs débutants du genre Linus Torvalds :)

    Ce post de Linus est vraiment d'anthologie ! On sent bien le mec de caractère. J'ose même pas imaginer ce qui adviendrait si il était enfermé dans un cagibi avec Theo de Raadt....

valgrind

Posté par gc (page perso, ) le 05/09/2006 à 19:55. (lien). Évalué à 10.

Tout simplement c'est super con parce l'essentiel des plantages sont dus à des accès mémoire incorrects et qu'il suffit de fait « make -g && valgrind ./a.out » pour avoir directement la ligne incriminée, au lieu d'une bête dichotomie à coup de printf.

Après, inutile d'en faire une tartine ou de chercher d'autres raisons.

  • [^]Re: valgrind

    Posté par Gabriel Linder () le 06/09/2006 à 10:27. (lien). Évalué à 3.

    A noter que Valgrind ne se limite pas à un vérificateur de mémoire : il y a plein de plugins bien sympas, on peut même coder ses propres plugins (voir mon post un peu plus haut).

"Considered Harmful" Essays Considered Harmful

Posté par Erwan (page perso, ) le 06/09/2006 à 00:42. (lien). Évalué à 10.

http://meyerweb.com/eric/comment/chech.html

Au final...

Posté par Mathieu Pillard (page perso, ) le 06/09/2006 à 03:58. (lien). Évalué à 7.

Au final, tu ne donnes pas vraiment d'argument sur pourquoi c'est mal: tu indiques juste que toi tu n'aimes pas ca... C'est juste une méthode de debugging comme une autre; elle est peut etre tres limitée, mais elle marche pour certains, y compris ceux qui connaissent gdb sur le bout des doigts mais qui dans certains cas ne veulent pas lancer tout le debuggueur juste pour des petits trucs...

Note au passage a propos des printf et du debugging: Ryan C. Gordon, porteur sous GNU/Linux d'une tonne de trucs (des jeux, mais pas seulement: vous lui devez google earth), ex de chez feu Lokigames, a indiqué dans un podcast récent comment il s'y prend pour porter un truc, généralement. Ce n'est bien sur qu'un début et ce sont des instructions basiques, mais j'ai trouvé ca pas mal: Il essaye de compiler le truc avec un makefile générique. A chaque endroit ou ca merde, il met un printf("FIXME"); a la place du code et continue jusqu'a ce que ca compile... Ensuite il corrige le code :-)

  • [^]Re: Au final...

    Posté par fouyaya () le 06/09/2006 à 05:47. (lien). Évalué à 3.

    Bonjour,es

    Les études sont loin maintenant, mais j'ai le souvenir que le debug à coup de printf cémal si tu n'tilises pas le fflush() à outrance. De mémoire (je dis peut être une bétise) printf() met en mémoire ce que tu lui demande d'écrire, puis de temps à autre (pas "immédiatement") cette mémoire est _flushée_ ver la sortie (fd : standard, fichier, tty...).
    Du coup, mieux vaut écire une macro PRINTF faisant appel à write() pour faire du debug. Tant qu'on y est, on peut ajouter à notre macro un argument qui affichera notre debug en fonction d'une variable DEBUG...

    P.S. moi j'aimais bien le debug à coup de PRINTF, ca permettait de commenter le code tout en générant des logs compréhensibles pour les autres développeurs. Le gdb, c'est bien, mais il est parfois difficile de comprendre pourquoi un tel à écrit son code d'une manière bien précise...

    • [^]Re: Au final...

      Posté par galactikboulay () le 06/09/2006 à 06:12. (lien). Évalué à 5.

      Le comportement par défaut de printf() est de flusher lorsqu'il rencontre un "\n" (saut de ligne).

      • [^]Re: Au final...

        Posté par Jean-Philippe Garcia Ballester (Jabber id, page perso, ) le 06/09/2006 à 10:11. (lien). Évalué à 2.

        À ma connaissance, c'est plutôt ton système qui fluh si la sortie est un terminal (je crois qu'il le fait aussi pour les socket et les pipes, non ?)
        Si la sortie de printf est un fichier, le flush n'est pas automatique.

        • [^]Re: Au final...

          Posté par lasher () le 06/09/2006 à 10:56. (lien). Évalué à 2.

          « À ma connaissance, c'est plutôt ton système qui fluh si la sortie est un terminal (je crois qu'il le fait aussi pour les socket et les pipes, non ?) »
          Non non, il a raison, c'est le '\n' qui fait que le vidage du tampon est effectué pour printf().

          • [^]Re: Au final...

            Posté par Jean-Philippe Garcia Ballester (Jabber id, page perso, ) le 06/09/2006 à 11:26. (lien). Évalué à 3.

            Oui oui, c'est bien le '\n', mais ce n'est pas printf qui fait le flush, et ce flush n'est pas fait si la sortie de printf est un fichier.

    • [^]Re: Au final...

      Posté par Krunch (Jabber id, page perso, ) le 06/09/2006 à 09:10. (lien). Évalué à 1.

      C'est ce dont je parle en disant :

      les systèmes de cache la rendent souvent inutile si on n'en tient pas compte
      Si on veut se passer de fflush(3) et de write(1) (qui est moins portable), on peut faire un coup de setbuf(stdout, NULL) au début ou utiliser stderr qui n'est pas bufferisé par défaut.

      moi j'aimais bien le debug à coup de PRINTF, ca permettait de commenter le code tout en générant des logs compréhensibles pour les autres développeurs
      Relis ma définition :
      J'appelle « printf debugging » le fait d'ajouter du code temporaire dans le seul but de débugger du code existant.
      Ce que tu fais j'appelle ça du logging.

      Par ailleurs, pour répondre à galactikboulay, le fait que printf(3) flush à chaque "\n" n'est sans doute pas très standard.

      --
      Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
      pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
      • [^]Re: Au final...

        Posté par Krunch (Jabber id, page perso, ) le 06/09/2006 à 09:11. (lien). Évalué à 2.

        s/write(1)/write(2)/ bien sûr.

        --
        Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
        pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.

Tool

Posté par Pooly (page perso, ) le 06/09/2006 à 06:49. (lien). Évalué à 8.

Un printf/fprintf est un outil comme un autre, s'en séparer c'est réduire la panoplie d'outils à ta disposition pour débugger. Autant un débugger ou valgrind c'est bien, autant parfois, un petit printf bien placé peut te faire gagner du temps.

Mouaif...

Posté par Laurent Pointal (page perso, ) le 06/09/2006 à 07:45. (lien). Évalué à 5.

Perso, pour du code multithreadé, je n'ai pas trouvé vraiment mieux que du printf-like[*] et analyse de logs.
Avec bien sûr de la compilation conditionnelle (non seulement globale avec _DEBUG et NDEBUG, mais aussi du DEBUG_MODULE au début des sources C, que je peux commenter/décommenter afin de désactiver/activer les lignes de déboguage d'un module).

L'argument du temps de compilation me semble, en 2006, avec les machines et les compilateurs que l'on a, un peu léger. Ca eu été vrai [**], ça l'est encore pour de très gros projets (OS, KDE & Co), mais faudrais pas exagérer.

Par contre, ça peut en effet avoir un gros inconvénient comme tu l'indiques: ça ajoute du temps d'exécution qui peut faire disparaître des bugs (typiquement quand on fait du pilotage d'instruments).

D'un autre côté, si en qq printf on n'a pas trouvé, en effet, passer au débugger, utiliser des points d'arrêt, de l'affichage du contenu de variables... tu as raison de vouloir montrer l'existence de ces outils, mais de là à considérer que le débogage via des traces est nocif, non.



[*] Des fonctions varargs qui ajoutent quelques infos, et qui soit renvoient vers un système de log, soit font du printf.

[**] Il y a une douzaine d'années j'avais un projet qui mettait une heure et quart à compiler, sur un 486DX33 sous Windows... on évitait autant que possible les "Rebuild All", mais la gestion des compilations sous l'environnement VC++ était suffisament foireuse pour qu'on soit régulièrement obligé de le faire. Quand le Pentium est arrivé, on est tombé à 1/4 d'heure de compil - le bonheur.
Et un peu auparavant, j'avais eu la joie de travailler en assembleur sur un Solar au CEA... compilation de plusieurs heures avec sortie du résultat sur une imprimante - là on dépouille vraiment le résultat.

--
(pub: Livres à prix réduit sur http://www.sollire.com/ - la boutique de mes petites soeurs)
  • [^]Re: Mouaif...

    Posté par Krunch (Jabber id, page perso, ) le 06/09/2006 à 09:25. (lien). Évalué à 1.

    C'est pas tellement la compilation en elle même qui fait perdre du temps mais plutôt le fait d'éditer pour rajouter/enlever les printf et parfois l'exécution.

    Pour le debug de code concurrent, c'est toujours bien chiant mais les printf ont tendance à heisenbugger assez bien aussi dans ce cas. J'ai plutôt l'impression que la solution est d'utiliser un debugger système du genre de dtrace (qui fait plus du logging que du debugging proprement dit d'ailleurs).

    Le problème c'est que bien souvent on printf debug rien que pour savoir si on est arrivé à un endroit dans le code alors qu'on peut aussi bien le faire plus rapidement avec un debugger sans avoir à modifier le code. S'il est vraiment intéressant de savoir régulièrement si un est passé par un endroit donné, alors autant laisser un debug() en permanence.

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
    • [^]Re: Mouaif...

      Posté par CrEv (page perso, ) le 06/09/2006 à 19:52. (lien). Évalué à 1.

      C'est pas tellement la compilation en elle même qui fait perdre du temps mais plutôt le fait d'éditer pour rajouter/enlever les printf et parfois l'exécution.


      C'est pour ça que c'est pratique de pouvoir faire de la compilation incrémentielle et donc de modifier le code en debug :
      - j'utilise le debugeur pour trouver mon probleme
      - je corrige sans arrêter l'exécution
      - je compile incrémentalement et je continue a exécuter pour verifier ma correction

      Evidemment ça ne marche pas avec tout mais c'est très pratique pour ne pas avoir à recompiler / relancer le prog (surtout quand il y a des connexion non automatiques à des bdd par ex, authentifications, ouverture de fichiers, ... tout ce qui demande de refaire les même opérations qui servent à rien et qui font perdre du temps)

Of GDB considered Harmful

Posté par Jerome Herman () le 06/09/2006 à 09:20. (lien). Évalué à 10.

Après avoir vainement cherché un « GDBdebugging 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.

Restons clair, si GDB est un excellent outil pour former les programmeurs débutants à la notion de pointeur et de libération de mémoire, il ne devrait jamais être utilisé pour quoi que ce soit d'autre. Donc passé deux ans à faire du C de façon intensive jetez le aux oubliettes et utilisez des appels intracode pour vous informer des problèmes rencontrés (printf est une bonne solution dans 90% des cas)

Les inconvennients majeurs de GDB :
- Un programme C/C++ compilé en mode "debug" ne se comporte pas (parfois pas du tout) comme le même programme compilé en mode standard. les différences principales sont :
* différence dans le link des bibliothèques. Le simple fait d'activer GDB dans un programme qui lie une bibliothèque compilée sans les options de debug peut faire apparaitre/disparaitre pas mal de bugs.
* différence dans la signature des pointeurs de fonctions. Si vous avez le moindre paramêtre système dans votre fonction, il y a 9 chances sur 10 pour que sa signature change après un passage de GDB. Cela peut également faire apparaitre/disparaitre un paquet de bugs. (des segfault souvent)
* remontée de tout un tas d'appels systèmes pour suivre le déroulement du programme qui n'existent pas dans les versions compilées en standard.

- Dans la plupart des programmes, GDB ne peut pas grand chose pour vous :
* Oubliez le mot clef "volatile" à moins de s'en servir exclusivement pour GDB. (Bon vous allez me dire que de toute façon vous ne connaissiez pas le keyword volatile)
* Oubliez les interruptions systèmes, notamment alarm, gettime et sleep. Certains messages passent, d'autres sont trappés par GDB directemetn sans jamais atteindre votre programme.
* GDB est totalement inutile en cas de programmation concurrente/parallèle. Il ne comprend rien au fichiers mappés en mémoire par un autre process et a du mal avec les messages IPC/ITC (com entre processes, com entre threads) un peu complexe.
* Si pour une raison quelconque, la mémoire allouée par un process est désallouée par un autre, c'est festival.
* Pour les raisons évoquée plus haut n'essayez même pas d'utiliser GDB en dehors du userland, vous allez vous faire mal à le kernel.

- GDB vous empêche de comprendre ce qui se passe vraiment.
* GDB ne comprend pas le code, il le trace. Si vous avez fait une erreur conceptuelle quelque part, GDB vous maintiendra dans l'illusion quand allouant un octet mémoire de plus ou en castant un peu plus violamment une variable ca marche.
* GDB ne comprend pas ce que vous cherchez à faire. A moins de truffer le code de volatile dédiés, GDB va vous aider à debugger les différents cas de figures un par un. A moins d'être très propre, très rigoureux et pas pressé par le temps, votre code (si il est imposant) ressemblera rapidement un un patchwork de verrues et de pansements. On reconnait le code des afficionados de GDB aux nombres de tests qu'il y a dans le code.
* GDB check, trace et valide le code. Il est totalement insensible au système et à l'environnement de la machine. Si votre programme plante suite à un appel système, GDB vous permettra de trouver ou dans le code il faut contourner l'appel système. Mais il ne sera d'aucune utilité sur le pourquoi, le comment et la méthode à adopter pour traiter/ignorer l'appel système.

En bref GDB c'est bien pour les petites applis monotache, mono-utilisateur, monoprocess qui n'ont pas besoin des fonctions systèmes ou d'appels complexes à des bibliothèques extérieures.

Pour le reste, utilisez votre cerveau.

--
Kha
root est un privilège, pas un droit !
  • [^]Re: Of GDB considered Harmful

    Posté par kadreg (page perso, ) le 06/09/2006 à 13:11. (lien). Évalué à 1.

    ou pour le reste, utilisez un vrai debugger, vu que tes remarques sont spécifiques à gdb.

    Il y a un autre debugger que gdb sous linux d'ailleurs ?

    • [^]Re: Of GDB considered Harmful

      Posté par L () le 06/09/2006 à 13:32. (lien). Évalué à 10.

      printf() ? /o\

Utilisation d'un débugger

Posté par Raphaël Gertz (page perso, ) le 06/09/2006 à 09:47. (lien). Évalué à 1.

Puisque tout le monde crache sur ce fameux printf pour débugger...

Quelqu'un aurait-il la gentillesse de mettre ici comment utiliser gdb et valgrind (enfin la sortie).

Parce que je connais :
gdb ./a.out_qui_segfault
(gdb) run
<stdout+stderr>
(gdb) bt

(gdb)

Mais pour le reste (afficher les variables, etc) j'ai jamais trouvé un tutoriel bien fait.

Et pour les fanas du RTFM, merci de vous le remballer, je vous demande pas une doc de merde comme j'en ai trouvé a la pelle inutilisable, mais une du niveau de http://fr.php.net/nom_fonction qui explique BIEN les choses et des exemples d'utilisation !

En fait c'est que j'aide au debugging de mandriva et que ça me soûle de pas pouvoir régler les problèmes moi-même faute d'arriver a retrouver le contexte de l'erreur (valeurs de variables, appel de fonction, etc...).

Merci d'avance.

  • [^]Re: Utilisation d'un débugger

    Posté par Nicolas Schoonbroodt (Jabber id, page perso, ) le 06/09/2006 à 09:54. (lien). Évalué à 1.

    STFW

    Sur google en français, cherche :
    gdb howto
    gdb tutorial

    --
    [ Répondre ] Ce commentaire est-il impertinent ou utile ?
  • [^]Re: Utilisation d'un débugger

    Posté par Colin Leroy (page perso, ) le 06/09/2006 à 09:58. (lien). Évalué à 4.

    break fichier.c:NUMLIGNE et
    break function arrêtent l'exécution
    delete X enlève le breakpoint X
    cond X expr_booleenne n'arrête l'exécution du breakpoint X seulement si expr_booleenne est vérifiée
    step continue un step
    cont resume l'exécution
    bt affiche la backtrace
    frame N saute au point N de la backtrace
    l affiche le code autour
    print variable affiche la valeur de la variable (adresse en cas de pointeur)
    print *variable affiche la "valeur du contenu du pointeur"
    print struc->stuff affiche le champ stuff de la structure struc
    set struc->stuff=0 affecte 0 à struc->stuff
    handle SIGBLAH {stop,nostop,print,noprint,pass,nopass} permet de contrôler ce que GDB fait des signaux

    et encore plein de trucs que je ne connais pas...

    --
    Claws Mail - it bites!
    • [^]Re: Utilisation d'un débugger

      Posté par Larry Cow () le 06/09/2006 à 10:59. (lien). Évalué à 2.

      ... sachant qu'on peut généralement résumer les commandes à leur plus simple expression.

      Genre 'p' au lieu de 'print', 'c' au lieu de 'cont', etc...

  • [^]Re: Utilisation d'un débugger

    Posté par Mildred (Jabber id, page perso, ) le 07/09/2006 à 22:10. (lien). Évalué à 3.

    Tu peux utiliser un truc en mode graphique comme kdbg ou ddd ... C'est ce que j'utilise lorsque j'ai besoin et c'est plus pratique je trouve que la ligne de commande.
    Après, il faut un grand écran.

    ddd affiche en plus une console gdb ... tu dois pouvoir apprendre les commandes comme ça.

    http://www.kdbg.org/
    http://www.gnu.org/software/ddd/

printf, pour débutant ?

Posté par Laurent J (page perso, ) le 06/09/2006 à 12:17. (lien). Évalué à 4.

Bon, certains commentaires l'ont déjà signalé, un debugger fait pas tout. Et je vais en rajouter.

1) gdb : j'ai pas vu de debugger aussi inutilisable que gdb, aussi lourd, et aussi lent. Surtout sur des gros programmes (même si c'est sur une toute petite partie que tu veux debogger).

Et je ne parle pas de toutes ces commandes qu'il faut apprendre, des front-ends limités ou non ergonomique, et qui par nature, souffrent des mêmes défauts de gdb lui même..

Si il y a bien quelque chose à féliciter à MS, c'est le deboggeur de Visual Studio. Tout est quasi instantannée, interface nickelle. Vivement le jour où on pourra virer gdb sous linux et avoir un déboggeur aussi efficace que celui de VS.

2)

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é.


Mauvais éditeur, changer éditeur. Et changer pour un éditeur qui contient une véritable fonction de recherche multi fichier.

3) Gdb est inutilisable dans un programme qui travaille sur un volume de donnée conséquent. Exemple : j'ai réalisé un validateur xml, basé sur relaxng. J'avais des bugs lors du parsing du schema de docbook, qui contient des milliers de patterns. Trés franchement, il est inutile de tenter d'utiliser le deboggeur : pas envie de repasser 3500 fois dans la même méthode pour arriver jusqu'au 3500ieme pattern qui provoque le bug.
La seule solution : le printf pour se générer un log. C'est ce que je fais sur des traitements "moyens".

Sinon j'utilise comme tu dis "un système de logging permanent (dés)activable plus ou moins dynamiquement ". En particulier dans mes devs Mozilla. Mozilla possède un système de log trés sympa (PrLog), que tu peux activer ou désactiver via une variable d'environnement (avec plusieurs niveaux d'activation). (et n'est pas compilé pour la production d'une version optimisée de l'application).

Bref, chaque solution (printf, loggeurs évolués, deboggueur) est utile selon les cas.

  • [^]Re: printf, pour débutant ?

    Posté par Krunch (Jabber id, page perso, ) le 06/09/2006 à 12:24. (lien). Évalué à 6.

    pas envie de repasser 3500 fois dans la même méthode pour arriver jusqu'au 3500ieme pattern qui provoque le bug
    J'ai pas vu le code/bug concerné mais tu sais que tu peux mettre des conditions sur les breakpoints ?

    --
    Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
    pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
    • [^]Re: printf, pour débutant ?

      Posté par Nicolas Schoonbroodt (Jabber id, page perso, ) le 06/09/2006 à 12:29. (lien). Évalué à 4.

      Si tu ne sais pas lequel provoque le bug (c-à-d si c'est justement ce que tu cherche) c'est moins simple. Je ne sais pas si c'est qu'il a voulu dire, mais c'est ce que j'ai cru comprendre en lisant.

      --
      [ Répondre ] Ce commentaire est-il impertinent ou utile ?
      • [^]Re: printf, pour débutant ?

        Posté par √λιi () le 06/09/2006 à 14:05. (lien). Évalué à 6.

        Si tu ne sais pas lequel provoque le bug
        Si tu sais, printf et gdb ne sont pas exclusif :
        procesing pattern 3501 ... passed
        procesing pattern 3502 ...
        segfault
        
        puis
        (gdb) break 523 if partternnum = 3502
        
        non de d'la !

      • [^]Re: printf, pour débutant ?

        Posté par Laurent J (page perso, ) le 06/09/2006 à 14:29. (lien). Évalué à 3.

        effectivement, quand on ne sait pas vraiment quelle "valeur en entrée" fait planter le truc... Le log est le seul moyen.

        Aprés effectivement, si on arrive à determiner des conditions d'apparitions du bug, on peut passer au deboggeur avec des breakpoint conditionnels. Par contre je ne savais pas que ça se faisait, les breakpoints conditionnels (dans kdevelop apparement, il ne permet pas de specifier ce genre de truc, et la ligne de commande de gdb, je ne supporte pas)

        • [^]Re: printf, pour débutant ?

          Posté par Krunch (Jabber id, page perso, ) le 06/09/2006 à 14:38. (lien). Évalué à 3.

          ddd supporte les breakpoints conditionnels apparement. C'est pas très joli mais c'est à la souris (avec possibilité d'entrer des commandes gdb à la main quand même).

          --
          Free Softwares Users Group Arlon (Sud Luxembourg, Belgique)
          pertinent, e adj. Approprié ; qui se rapporte exactement à ce dont il est question.
        • [^]Re: printf, pour débutant ?

          Posté par Thomas Douillard () le 06/09/2006 à 15:06. (lien). Évalué à 5.

          C'est possible dans eclipse avec CDT.

          Après c'est parfois difficile de déterminer la condition dans des cas tordus, je suis pas sûr qu'il soit possible de mettre un appel de méthode dans la condition par exemple.

          • [^]Re: printf, pour débutant ?

            Posté par peau chat () le 06/09/2006 à 15:44. (lien). Évalué à 4.

            En fin de compte, le plus important n'est pas de savoir COMMENT debugger le code, mais de trouver une solution pour debugger le cerveau du programmeur (i.e. bien souvent son propre cerveau).

            ;)

        • [^]Re: printf, pour débutant ?

          Posté par tene (page perso, ) le 10/09/2006 à 12:51. (lien). Évalué à 1.

          euh y'a pas moyen de dire à son gentil debugger de ... faire son break quand le segfault arrive?

          Y'a du remote debugging avec gdb, j'image aussi?

          • [^]Re: printf, pour débutant ?

            Posté par Matthieu Moy (page perso, ) le 11/09/2006 à 05:12. (lien). Évalué à 2.

            > euh y'a pas moyen de dire à son gentil debugger de ... faire son break quand le segfault arrive?

            Euh, c'est un peu le mode par défaut ...

            Mais bug != segfault.

            Et par ailleurs, en général, l'endroit intéressant, il est quelques instructions avant le segfault (genre « Grmbl, comment ça il est nul mon pointeur, je viens de l'initialiser ? Ah, en fait, non ! »).

            • [^]Re: printf, pour débutant ?

              Posté par Erwan (page perso, ) le 13/09/2006 à 19:29. (lien). Évalué à 3.

              Mais bug != segfault.

              C'est pour ca que ce que je fais, c'est mettre des asserts partout pour stopper le processus des que quelque chose d'anormal arrive (genre au debut d'une fonction, les parametres d'entree ne respectent pas certains criteres). Ca aide a debugger, car sinon on se retrouve a planter plus loin.

              Je met ca dans des #ifdef DEBUG, comme ca je desactive les asserts pour la version que je livre/publie. Si quelque chose va mal, il y aura peut-etre un comportement bizarre mais moins violent qu'un arret du processus pour l'utilisateur.

              • [^]Re: printf, pour débutant ?

                Posté par Mildred (Jabber id, page perso, ) le 14/09/2006 à 05:45. (lien). Évalué à 4.

                man assert :

                DESCRIPTION
                       Si  la  macro  NDEBUG  était  définie  lors de la dernière inclusion de
                       <assert.h>, la macro assert() ne génère aucun code, et  ne  fait  rien.
                       Sinon,  la  macro  assert()  affiche  un message d’erreur sur la sortie
                       standard, et termine l’exécution du  programme  en  cours  en  appelant
                       abort() si l’expression est fausse (égale à zéro).
                
                Donc pas besoin de #ifdef DEBUG

  • [^]Re: printf, pour débutant ?

    Posté par norbs () le 06/09/2006 à 23:07. (lien). Évalué à 1.

    > Mauvais éditeur, changer éditeur. Et changer pour un éditeur qui
    > contient une véritable fonction de recherche multi fichier.

    Serait-ce une attaque contre Emacs ???

Heisenbug...

Posté par peau chat () le 07/09/2006 à 05:42. (lien). Évalué à 3.

Voici un exemple d'Heisenbug qui ne vient pas du printf() :


[aegir@dell ~]$ cat t.c
#include <stdio.h>

int main()
{
int a=10;
printf("%d %d\n",(int)(a*.3+a*.7),(int)(10*.7+10*.3));
}
[aegir@dell ~]$ gcc -O2 t.c ; ./a.out
10 10
[aegir@dell ~]$ gcc -g3 t.c ; ./a.out
9 10
[aegir@dell ~]$


À part ça, GNU/Linux est un système parfait ;)

  • [^]Re: Heisenbug...

    Posté par Sylvain Rampacek (Jabber id, page perso, ) le 07/09/2006 à 09:53. (lien). Évalué à 2.

    visiblement, j'obtiens le même résultat avec :
    gcc -g3 t.c ; ./a.out
    et
    gcc t.c ; ./a.out
    donc je pense plutôt que ça vient d'un problème dans l'optimiseur, et pas les effets d'ajout des informations de débuggages

    • [^]Re: Heisenbug...

      Posté par peau chat () le 07/09/2006 à 10:39. (lien). Évalué à 2.

      Non, ça ne vient pas de l'optimiseur, c'est un coup de bol qu'avec -O2 on a le bon résultat (bon résultat mathématiquement parlant).

      J'ai juste voulu illustrer que, si on supprime la classique optimisation -O2 pour faire du débogage, et bien on peut voir apparaître de nouveaux bugs.

      Dans cet exemple, ce qui est "inattendu" et impévisible c'est :

      1) Le faux résultat (9 au lieu de 10)
      2) Que le faux résultat disparaisse avec l'option -O2.

      En fait, dans cet exemple, le seul truc qui se comprte de façon préfisible c'est le printf() ;-)

    • [^]Re: Heisenbug...

      Posté par peau chat () le 07/09/2006 à 10:57. (lien). Évalué à 3.

      En fait c'est bel et bien un bug linux, et le problème vient de là :


      hl054353@ingvsx3j:/usr/include$ grep "FPU_DEFAULT" /usr/include/x86_64-linux/fpu_control.h
      #define _FPU_DEFAULT 0x037f


      Donc si tu transformes le code de la manière suivante :


      void set_fpu(unsigned int mode)
      {
      asm ("fldcw %0" : : "m" (*&mode));
      }


      int main(int argc, char *argv[])
      {
      set_fpu (0x27F);

      int a = 10;
      printf("%d %d\n",
      (int)( a*.3 + a*.7),
      (int)(10*.3 + 10*.7));
      return 0;
      }


      là tu auras bien les bons résultats, comme sur n'importe quel microprocesseur, ou n'importe quel autre un*x sur x86.

  • [^]Re: Heisenbug...

    Posté par Sebastien Tanguy (page perso, ) le 07/09/2006 à 10:07. (lien). Évalué à 1.

    C'est vrai, la faute à Linux sans doute...


    $ uname -s ; gcc -O2 t.c ; ./a.out; gcc -g3 t.c; ./a.out
    FreeBSD
    10 10
    10 10


    Ou pas:

    % uname -s ; gcc -O2 t.c ; ./a.out; gcc -g3 t.c; ./a.out
    Linux
    10 10
    10 10

    • [^]Re: Heisenbug...

      Posté par peau chat () le 07/09/2006 à 10:34. (lien). Évalué à 2.

      Parce que ton CPU n'est pas un x86. Ou alors c'est un x86-64 et ton système est compilé en 64 bits.

    • [^]Re: Heisenbug...

      Posté par lasher () le 07/09/2006 à 10:39. (lien). Évalué à 2.

      Ca vient sans doute de gcc 4.x
      J'ai testé sur une debian testing, avec gcc 4.0 et 4.1 : bug ;
      Sur une red hat customisée : gcc 3.2.3, puis un gcc pré version 4 (gcc-ssa) : ça marche. Idem avec gcc 2.96.

      En fait, avec gcc 4, il faut lancer l'optimisation pour que ça marche correctement .

      • [^]Re: Heisenbug...

        Posté par peau chat () le 07/09/2006 à 10:43. (lien). Évalué à 2.

        Euh, c'est quoi que tu appelles "ça marche correctement" ?

        Normalement, dans tous les cas, ça doit afficher "10 10".

        Ca n'affiche "9 10" que si :

        1) Tu es sur cpu x86
        2) Tu es sous un Linux compilé en 32 bits.

        Sur un BSD sur x86 cela affichera toujours le résultat correct "10 10".

  • [^]Re: Heisenbug...

    Posté par Troy McClure (page perso, ) le 07/09/2006 à 10:32. (lien). Évalué à 0.

    probleme d'arrondi classique avec les floats, dans un cas ça donne 9.999999999 et dans l'autre 10.0000000001 , quand tu tronques les decimales paf c'est le désastre. Mais je te trouve gonflé d'accusé linux :)

    • [^]Re: Heisenbug...

      Posté par peau chat () le 07/09/2006 à 10:45. (lien). Évalué à 1.

      Ah ? Et tu trouves donc normal que 9,99999999 soit arrondi à 9 lors de la conversion en entier ?

      Arrondir, ce n'est pas tronquer les décimales.

      Enfin, en tout cas, d'après les normes IEEE ça ne devrait pas.

      • [+] [^]Re: Heisenbug...

        Posté par Troy McClure (page perso, ) le 07/09/2006 à 10:58. (lien). Évalué à -2.

        ah mais où est-ce que tu arrondis ? (int)(0.9) ça a toujours donné 0 , ça n'arrondi pas!

        • [^]Re: Heisenbug...

          Posté par peau chat () le 07/09/2006 à 11:14. (lien). Évalué à 1.

          Ce n'est pas le (int) qui fait l'arrondi, c'est l'expression mathématique.

          Et heureusement !

          int i = (int) (100 * 0.1);

          peut contenir 9 ???

          Ou alors encore mieux :

          int a = (int) (100 *2); // donne 200
          int b = (int) (100 *2.0); // donne 199

          ????

          Heureusement que non, sinon il y aurait beaucoup plus de bugs ;)

          D'après la norme IEEE754 :

          "Unbiased which rounds to the nearest value, if the number falls midway it is rounded to the nearest value with an even (zero) least significant bit. This mode is required to be default."

  • [^]Re: Heisenbug...

    Posté par Jean-Philippe Garcia Ballester (Jabber id, page perso, ) le 07/09/2006 à 10:52. (lien). Évalué à 2.

    Si mes souvenirs sont bons, c'est une histoire de taille différente entre registre et mémoire, et qu'un passage de l'un à l'autre tronque le flottant.

    • [^]Re: Heisenbug...

      Posté par peau chat () le 07/09/2006 à 11:01. (lien). Évalué à 3.

      Et comment expliquerais tu alors que sur le même microprocesseur, le mêmecode compilé avec le même mricroprocesseur donne "9 10" sous Linux et "10 10" sous BSD. ;)

      C'est un problème que Linux d'initialise pas le FPU comme il faut. C'est connu depuis longtemps.

      • [^]Re: Heisenbug...

        Posté par miod () le 15/09/2006 à 15:49. (lien). Évalué à 1.

        On ne peut pas dire que Linux n'initialise pas les paramètres IEEE754 "comme il faut". Aucune norme n'impose qu'un programme démarre dans un mode spécifique

        C'est au programmeur de paramétrer le mode d'arrondi dont il a besoin, s'il veut avoir des résultats reproductibles.

        De façon portable, cela se fait avec fpsetround().

Heisenbug

Posté par Duncan Idaho (Jabber id, page perso, ) le 07/09/2006 à 11:00. (lien). Évalué à 6.

J'ai programmé en C une fois dans ma vie et est été confronté, en débug-ant à coup de printf, à l'un de ces fameux heisenbug.

La solution que j'ai trouvé est simple : laisser la gestion de la mémoire, tâche fastidieuse et source de nombreuses erreurs, à un gestionnaire automatique, prévu pour ça. C'est-à-dire en gros, ne pas programmer en C. Je ne parle pas forcément de Java et de son garbage collector (que je n'aime pas pour d'autre raison) mais il est surprenant que, malgré de longues et brillantes recherches en théorie des langages, on se tape encore un langage aussi difficile à programmer que le C. Si l'on compare la programmation à la conduite sur route, le C est une voiture qui va partout même là ou il ne faut pas, alors qu'il existe des routes avec des garde-fous pour éviter de se viander dans les ravins, et même si l'on conduit bien, ça peut servir.

Alors vous allez me dire "ouais, mais le C c'est vachement bas niveau, on peut faire plein de truc avec", ce à quoi je vous répond : "certes, mais si tu ne fais pas de l'embarqué ou de la programmation système, le C c'est juste une grosse perte de temps".

Mon expérience m'a conduit à privilégier le Eiffel, langage brillant s'il en est (orienté objet bien sûr, contrats, pas de pointeurs à la con...) ou bien sûr de super langage de script comme python, bien que ma préférence aille plutôt vers le ruby, plus propre à mon goût.

Bref, la technologie permet depuis trèèès longtemps de s'affranchir de bizarreries comme les "Heisenbug", profitez-en.

  • [^]Re: Heisenbug

    Posté par √λιi () le 07/09/2006 à 12:12. (lien). Évalué à 3.

    C'est moi ou tu as oublié de parler de performances ?

    • [^]Re: Heisenbug

      Posté par Ontologia (page perso, ) le 07/09/2006 à 12:51. (lien). Évalué à 1.

      Euh Eiffel c'est pas mal quand même en terme de performances... Ya mieux, mais c'est un des meilleurs tout de même.
      Eiffel a été et est toujours un des seuls compilateur objet à supprimer la liaison dynamique qui rend le code très l