Journal printf debugging considered harmful

Posté par  (site web personnel) .
Étiquettes : aucune
-1
5
sept.
2006

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
  • # exceptions

    Posté par  . É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

    Posté par  (site web personnel) . Évalué à 2.

    Quelqu"un peut expliquer le heisenberg ? ^^"
    • [^] # Re: re

      Posté par  (site web personnel) . É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  . É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  . É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  . Évalué à 6.

      Fallait regarder Numb3rs saison 1 épisode 2 ;-)
    • [^] # Re: re

      Posté par  . É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
      • [^] # Commentaire supprimé

        Posté par  . Évalué à 3.

        Ce commentaire a été supprimé par l’équipe de modération.

        • [^] # Re: re

          Posté par  (site web personnel) . Évalué à 2.

          Moi je les trouves plutot complémentaire ces deux explications...
          • [^] # Re: re

            Posté par  . É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.
            • [^] # Re: re

              Posté par  . É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  (site web personnel) . É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 ?

                pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

                • [^] # Re: re

                  Posté par  . Évalué à 1.

                  A pinailleur, pinailleur et demi :)
              • [^] # Re: re

                Posté par  . É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.
                • [^] # Re: re

                  Posté par  (site web personnel) . É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  . É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).
                    • [^] # Re: re

                      Posté par  (site web personnel) . Évalué à 2.

                      Ah bah merci, ça à l'air très interessant tout ça, je vais tacher de me documenté un peu tiens...
  • # Comment profiter pleinement des possibilités de vim.

    Posté par  . Évalué à 0.

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

    Fais-tu vraiment ça ?
  • # Commentaire supprimé

    Posté par  . Évalué à 2.

    Ce commentaire a été supprimé par l’équipe de modération.

    • [^] # Re: glib, je t'aime

      Posté par  (site web personnel) . É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(...)

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: glib, je t'aime

        Posté par  . É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  . É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  (site web personnel) . É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.

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: Claimed truth considered harmful

      Posté par  . É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 ;)
  • # J'ai eu ma période ...

    Posté par  . É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  . É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  . É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  (site web personnel) . É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.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: ouais

        Posté par  . É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  . É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  . É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  (site web personnel) . É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 :-)

            Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN

            • [^] # Re: ouais

              Posté par  . É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  . É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.
            • [^] # Re: ouais

              Posté par  . É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  (site web personnel) . É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  . É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  (site web personnel) . Évalué à 2.

                  Les debuggers qui trouvent les bugs tout seuls ça existe aussi mais je sais pas si c'est très efficace.
                  http://www.st.cs.uni-sb.de/dd/

                  pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: ouais

      Posté par  (site web personnel) . É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.

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: ouais

      Posté par  (site web personnel) . É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  (site web personnel) . É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  . É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  . Évalué à 10.

  • # Au final...

    Posté par  (site web personnel) . É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  . É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  . É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  (site web personnel) . É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  . É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  (site web personnel) . É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.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

        • [^] # Re: Au final...

          Posté par  (site web personnel) . Évalué à 2.

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

          pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

  • # Tool

    Posté par  (site web personnel) . É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  (site web personnel) . É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.

    Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN

    • [^] # Re: Mouaif...

      Posté par  (site web personnel) . É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.

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: Mouaif...

        Posté par  (site web personnel) . É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  . É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.
    • [^] # Re: Of GDB considered Harmful

      Posté par  . É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 ?
      • [^] # Commentaire supprimé

        Posté par  . Évalué à 10.

        Ce commentaire a été supprimé par l’équipe de modération.

  • # Utilisation d'un débugger

    Posté par  (site web personnel) . É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  . Évalué à 1.

      STFW

      Sur google en français, cherche :
      gdb howto
      gdb tutorial
    • [^] # Re: Utilisation d'un débugger

      Posté par  (site web personnel) . É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...
      • [^] # Re: Utilisation d'un débugger

        Posté par  . É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  (site web personnel) . É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  (site web personnel, Mastodon) . É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  (site web personnel) . É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 ?

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

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

        Posté par  . É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.
        • [^] # Re: printf, pour débutant ?

          Posté par  . É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  (site web personnel, Mastodon) . É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  (site web personnel) . É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).

            pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

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

            Posté par  . É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  . É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  . É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  (site web personnel) . É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  . É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  (site web personnel) . É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  . É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  . É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  (site web personnel) . É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  . É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  . É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  (site web personnel) . É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  . É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  . É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  . É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  (site web personnel) . É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  . É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  (site web personnel) . É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  . É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  (site web personnel) . É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  . É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  . É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  . É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  . Évalué à 3.

      C'est moi ou tu as oublié de parler de performances ?
      • [^] # Re: Heisenbug

        Posté par  (site web personnel) . É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 lent à cause de l'impossibilité d'inliner le code ainsi que l'utilsation de VFT (Virtual Function Table, table de pointeurs sur fonctions) qui vide tous les caches du processeur.

        Bon de toutes façon, en terme de performances, C n'en n'a plus pour longtemps.

        « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

        • [^] # Re: Heisenbug

          Posté par  . Évalué à 10.

          Ha ha encore du prosélytisme pour un langage donc le compilateur n'est pas libre et les détenteurs des brevets sont incapable de le vendre correctement ?

          C'est marrant mais j'ai eu une pensée aussi pour lissac en rédigeant mon commentaire.

          Note a ceux qui n'ont pas tout suivit : ca ce passe ici http://isaacos.loria.fr/ , quand on en a entendu parlé, ca a l'air très bien, mais vous achèteriez, vous ? - bon j'ai l'air sarcastique, mais j'ai une profonde amertume de voir des gens conserver leur bijou jalousement pour en tirer des sous, mais être totalement incapable d'allécher correctement un éventuel investisseur. Autant faire du libre que de laisser le truc pourrir sur place.

          Bref pour en revenir sur le sujet, des gens qui optimisent en assembleur (gardé par des directive de compilation et avec le code portable pour les autre plates formes), ca existe encore, et je dirais que c'est encore heureux, dans bien des cas ca évite de tout transformer en une immense charette (bon c'est pas vraiment le cas du libre, mais on en est pas au tout mutlimédia non plus : j'en prendrais mplayer en témoin.

          Les langages haut niveau ne font pas tout, et il suffit de constater que quasiment toutes les applications destinées a un usage quotidien et rédigées en haut niveau n'ont que très peu de succès ... pas par hasard : lent, gouffre a mémoire.

          Les language a haut niveau n'ont pour moi que deux application principales : les application que l'ont utilise une fois de temps en temps pour une tache précise, ou pour un serveur dynamique. Après pour les autres applications (celles que l'ont utilise 95% du temps), et les systèmes sous jacent (sans descendre jusqu'au noyau bien sûr), bref 99% du code exécuté sur un machine ... a moins de transformer le tout en charrette.

          Bon et puisqu'on est dans le troll a plein dedans : Un des gros intérêts de l'objet est justement la liaison dynamique (vive les classes abstraites ! vive l'interface-peut-importe-l'implémentation !). Sans compter que 95% des développeurs ne veulent pas a apprendre de nouveau concepts (déjà passer du C++ vers Java/C#, houlala c'est dur, ca n'a rien a voir). Il y a le temps avant de détrôner le C/C++.
          J'en remet une couche : bien des bug en haut niveau restent difficiles a détecter alors que ca se règle par un bon vieux segfault en C/C++ et c'est pas forcément si mal.
          • [^] # Re: Heisenbug

            Posté par  . Évalué à 3.

            Je ne vois pas le rapport entre Lisaac et Eiffel. Il est vrai que je n'ai pas creusé plus en profondeur les liens (boulot, toussa) mais j'ai déjà fait du Eiffel avec un compilateur libre et si ma mémoire est bonne, une super suite de développement Eiffel a été libéré il n'y a pas longtemps.

            Ma mémoire est bonne : http://linuxfr.org/2006/04/06/20628.html

            Bref, ton troll ne tient même pas la route.

            Je ne rentre pas en détails sur tes conclusions douteuses quand au caractère lent des applications développé avec des langages de haut niveau : la rapidité d'un programme en informatique est une question tout à fait accessoire. C'est *très* important, mais beaucoup moins que le reste (fiabilité, stabilité, coût de développement, coût de maintenance...) et surtout, la performance dépend à peu près uniquement de la programmation, et certainement pas du langage utilisé.

            Alors oui, il y a une différence de rapidité entre un programme Java qui tourne dans une VM pourrie et le même code en assembleur, mais dans la vie de tous les jours et pour des classes de langage comparable (langages compilés, interprétés, dans une VM), c'est un faux problème.
            • [^] # Re: Heisenbug

              Posté par  . Évalué à 4.

              "Je ne vois pas le rapport entre Lisaac et Eiffel."

              Normal, il n'y en a aucun.

              L'histoire c'est que Ontologia nous a beaucoup parlé de lisaac, comment c'était bien et pouvais être aussi rapide que le C ou le C++, et que donc il faut remplacer le C et le C++ par lissac. Donc quand je vois `Bon de toutes façon, en terme de performances, C n'en n'a plus pour longtemps.' je suis sûr que Ontologia ne pensait pas a Eiffel mais bien a lisaac en disant ca.

              Sinon pour le reste, je dirait juste qu'une application n'est pas toujours le gros du boulot, et moi je pense beaucoup aux diverses bibliothèques graphiques et compagnie où quelque soit ton algorithme, comme ceux pour faire de l'imagerie (je compte les toolkits, les compositeurs bien a la mode la dedans) ou du multimédia qui ne demandent que de la rapidité de calcul brute. Si on pouvais atteindre des performances équivalente, ca se saurait - tout ca pour des histoires de copies mémoires et autre choses masquées quand on est trop haut. Or le multimédia est de plus en plus présent.

              Dernier point, quand on mélange du haut et bas niveau, il faut faire des bindings. Faire cela correctement, sans bugs, et en tirant parti des propriétés du langage cible est un travail long (quantité de fonctions a gérer) et difficile (bien le penser, être rigoureux).

              Désolé pour l'incompréhension.
              • [^] # Re: Heisenbug

                Posté par  (site web personnel) . Évalué à 4.

                Normal, il n'y en a aucun.

                Erreur, il y en a beaucoup.

                Eiffel et Lisaac sont des langages permettant l'héritage multiple.
                Ces deux langages utilisent aussi les types paramétriques.

                Eiffel est certes un langage objet à classe et Lisaac un langage objet à prototype.

                Les similarités entre SmartEiffel sont dus à leur développement. L'auteur de Lisaac a fait sa thèse dans le laboratoire de Dominique Colnet le créateur et mainteneur de SmartEiffel. C'est D. Colnet qui a fait découvrir à Benoit Sonntag (l'auteur de Lisaac) le langage Self qui a été le déclique. C'est parce qu'il travaillai dans ce laboratoire qui était en grande partie dédié à "comment compiler un langage objet avec de bonnes performances que Lisaac est né".
                A noter que la librairie de SmartEiffel et de Lisaac sont les mêmes, puisque la première a été traduite pour le second langage.

                D'ailleurs Colnet est co auteur de la plupart des publications qui ont été diffusées au début.

                Effectivement, je pensais à Lisaac, car une nouvelle version du compilateur est en train d'être écrite qui semble très prometteuse. Lisaac 1 était 2% plus lent que C, reste à descendre en dessous.

                Effectivement, je te rejins sur le reste. Plus je programme, plus je me rend compte que l'architecture et la structure de la lib, de l'OS, la qualité du compilateur, etc... sont déterminants.

                « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

                • [^] # Re: Heisenbug

                  Posté par  . Évalué à 2.

                  Pour le rapport c'était juste pour dire pourquoi je parlais de lisaac alors que ce n'était pas forcément très clair.

                  Sinon merci pour ton commentaire positif (j'ai du mal a l'être :) ) qui m'a fait penser "chouette un thread assez frontal qui fini bien !".
          • [^] # Re: Heisenbug

            Posté par  (site web personnel) . Évalué à 2.

            Les langages haut niveau ne font pas tout, et il suffit de constater que quasiment toutes les applications destinées a un usage quotidien et rédigées en haut niveau n'ont que très peu de succès ... pas par hasard : lent, gouffre a mémoire.

            Va le dire au développeurs ut utilisateurs de MLDonkey par exemple... (Ok, tu as dit "quasiment", mais tu as surtout mit "toutes" en gras) et il y en a pas d'autres.

            Le problème c'est qu'il faut savoir ou utiliser chaque langages. L'assembleur n'est utile que dans des cas bien précis ou le compilateur n'est pas capable de générer du code optimal et ou les performances sont critiques. Cela inclut par exemple les boucle interne de décodage de flux video quand des instruction SIMD complexes sont disponibles.

            Mais les compilateurs font d'énormes progrès et il y a de moins en moins de code pour lesquels une version assembleur apporte un gain vraiment interessant. Et c'est pour cela que les langage haut niveau ratrapent progressivement les langages bas niveau. Parce que pour pouvoir faire des optimisations aussi complexes, il faut disposer d'informations sémantiques qu'il est impossible de fournir au compilateur en C.
            • [^] # Re: Heisenbug

              Posté par  . Évalué à 3.

              MLDonkey ...

              J'ai eu une expérience extrêment douloureuse avec mldonkey, et, c'est vraiment pas pour me faire mousser, mais je le trouvais justement lent (surtout gourmand en mémoire) et bugué (il partait en boucle infinie ... plutôt ballot pour un démon).
              Bon certes, c'était il y a presque 3 ans, ils ont sans doute fait des progrès.

              Mais aMule qui était loin d'être mature a l'époque, était bien plus léger et réactif, et bien plus stable pour ma part. Et c'est codé en C++/wxWidgets. Et j'étais rempli de bonheur.

              Enfin pour continuer sur mon expérience personnelle, aucune application que j'utilise quotidiennement sont codées en autre chose que du C ou du C++. J'ai essayé pourtant, mais a chaque fois je suis déçu : "autant de RAM pour ca" ou autre.

              Un autre truc : l'avantage des applications C/C++ c'est que dans ma distribution, elles ne me cassent pas les pieds pour telle ou telle obscure dépendance, c'est un binaire dans un coin, pas de surprises, on est toujours chez soit.
          • [^] # Re: Heisenbug

            Posté par  (site web personnel) . Évalué à 4.

            Note a ceux qui n'ont pas tout suivit : ca ce passe ici http://isaacos.loria.fr/ , quand on en a entendu parlé, ca a l'air très bien, mais vous achèteriez, vous ? - bon j'ai l'air sarcastique, mais j'ai une profonde amertume de voir des gens conserver leur bijou jalousement pour en tirer des sous, mais être totalement incapable d'allécher correctement un éventuel investisseur. Autant faire du libre que de laisser le truc pourrir sur place.

            Je suis totalement d'accord avec toi.

            Mais l'Inria a semble t-il un statut batard : organisme publique avec des chercheurs voulant faire de la recherche pur (j'ai vu des chercheurs en venir quasiment aux mains avec un chargé des valorisations industrielles) et des bonhommes cherchant à faire du fric avec ce qui y est produit.

            Ajoute à cela la lourdeur consternante qui sied à toute administration française, l'aveuglement avec.

            Tu obtiens un sac de noeud improductif (à part dans le domaine de la recherche) et inconséquent.

            « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: Heisenbug

        Posté par  . Évalué à 1.

        Oui, mais son programme fonctionne !!!
    • [^] # Re: Heisenbug

      Posté par  . Évalué à 10.

      Bon, je t'ai pertinenté, mais quand même, j'ai envie de répondre un peu à certains trucs :

      « 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. »

      Si si, c'est de la programmation en C. Utiliser un ramasse-miette externe n'est jamais une mauvaise idée si c'est la seule chose qui te gêne dans le langage.

      « 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. »

      Le C n'est pas un langage « difficile ». Sa syntaxe est l'une des plus simples qui existe, je trouve : il y a peu de concepts à retenir, et le plus difficile d'entre eux, le pointeur, est une notion qu'il est nécessaire de connaître si on veut faire de l'informatique professionnellement - enfin je trouve.

      Par contre, pour paraphraser certains grands intervenants de fr.comp.lang.c : « C n'est pas un langage de débutant », et « C is a sharp tool ».

      Là où par contre je suis tout à fait d'accord : utiliser le langage C est la plupart du temps inutile, car d'autres langages permettent de faire la même chose plus rapidement, avec beaucoup moins de bugs.

      Le C est un langage à destination des programmeurs qui savent ce qu'ils font. Il est très important d'en avoir déjà fait à mon avis pour savoir ce qui peut clocher dans des langages de plus haut niveau, mais pour autant, à moins de programmer pour de la haute performance ou du « bas-niveau », utiliser le langage C dans les programmes de tous les jours relève souvent du masochisme.
      • [^] # Re: Heisenbug

        Posté par  . Évalué à 3.

        utiliser le langage C dans les programmes de tous les jours relève souvent du masochisme


        mais aussi d'un certain élitisme. J'ai l'impression que faire du C, c'est coule, que ça donne une grosse quéquette parce que c'est dur à faire. Je pense que beaucoup programme en C juste pour le plaisir de se mesurer à des stars du code.

        En conséquence de quoi, le C reste utilisé en production (là où il est adapté bien sûr, mais aussi à plein d'endroit où il n'est qu'une source d'erreurs et de retards...) parce que personne n'ose dire qu'il n'y a que 2000 personnes dans le monde capables de faire du C proprement et qu'aucune ne travaille dans l'entreprise concernée :)
        • [^] # Re: Heisenbug

          Posté par  . Évalué à 2.

          Pas d'accord, un des avantages du C c'est justement qu'il y a plein de gens qui savent l'ecrire, qu'il y a plein de librairies dispo, que l'interfacage est clairement defini, qu'il permet d'ecrire du code rapide, ...

          Alors maintenant oui, C ca demande plus de connaissances sur les concepts de bases que d'autres langages et ca induit certains risques niveau erreurs de programmation, mais c'est un langage _extremement_ utile. C# et Java c'est sympa, mais ca peut pas repondre a tout loin de la.

          Enfin bon, de mon point de vue, le top c'est quand meme C++, les avantages de l'objet allies a la vitesse et flexibilite de C (et voila, c'est comme ca qu'on lance un troll sur les langages !)
  • # Principe d'incertitude

    Posté par  . Évalué à 1.

    On devrait plutôt parler des inégalités d'Heisenberg, ou au pire du principe d'indétermination.

    Quote Wikipedia :
    Ainsi, la dénomination « principe d'incertitude » n'a plus de valeur autre qu'historique et ne devrait plus être mentionnée.
  • # Would you like to know more?

    Posté par  (site web personnel) . Évalué à 3.

    http://samizdat.mines.edu/howto/HowToBeAProgrammer.html#id27(...)

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

Suivre le flux des commentaires

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