Journal Des outils d'audit de code Java

Posté par  (site web personnel) .
Étiquettes :
10
17
fév.
2010
Le langage Java n'est pas un langage qui s'exprime avec juste un éditeur de texte et un compilateur... Les IDE et outils d'aide au développement sont légion et pour cause, il serait dangereux de s'en passer. C'est particulièrement vrai pour les outils d'audit de code dont voici deux exemples.

UCDetector (prononcer "You See Detector") est un plugin Eclipse qui comme son nom l'indique se propose de détecter le code mort (Un-needed Code Detector) dans les sources Java.

Premier avantage : son format de plugin Eclipse en fait un outil très rapide à déployer. Sous Eclipse, ajoutez le site http://ucdetector.sourceforge.net/update dans le menu "Aide > Installer de nouveaux logiciels" et vous n'êtes plus qu'à trois clics d'avoir un outil d'audit de code fonctionnel. UCDetector propose quelques options via le menu "Fenêtre > Préférences > UCDetector" même s'il n'y a pas de quoi s'y perdre.

La possibilité d'ignorer certains fichiers, méthodes, champs, de restreindre les résultats ou de les paramétrer est probablement la plus utilisée. Outre l'affichage dans Eclipse, l'export aux formats XML et HTML restent des fonctionnalités standard mais appréciables. La pertinence des rapports générés est bonne pour peu que l'on sache faire le tri dans la liste des remarques générées. Pour affiner les résultats ou décupler les possibilités de l'outil il sera ensuite nécessaire de mettre les mains dans le cambouis pour créer soi-même ses marqueurs en fonction de ses besoins.

PMD est un concurrent possible d'UCDetector mais cette fois-ci l'installation n'est pas triviale et pour cause : PMD est une application complète en ligne de commande. Même si beaucoup de plugins sont disponibles et rendront son utilisation quotidienne aisée sous Eclipse, NetBeans etc., la configuration de ses règles via des fichiers XML semble infinie et nécessitera donc un peu d'étude. Mais la puissance du logiciel lorsqu'il est bien configuré mérite très largement ces efforts. Pour les vérifications de base, certaines configurations initiales sont proposées : "basic, imports, unusedcode" mais on comprend vite que la puissance de l'outil se trouve dans la personnalisation de ses règles pour les besoins particuliers de tel ou tel projet. PMC propose aussi d'exporter vers des fichiers et peut même être intégré à des outils de build comme Maven et Ant.

Même s'ils proposent les mêmes services, UCDetector et PMC n'intéresseront pas les mêmes populations. UCDetector conviendra à un développeur souhaitant un filet de protection relativement simple mais toujours pertinent. Son intégration dans Eclipse est très bonne et rapide. Le fait qu'il y ait peu de configuration rend l'outil très "plug'n play" ce qui est utile quand on souhaite le mettre en place au milieu d'une phase de développement. PMD quant à lui est plus long à installer et à configurer mais il fait vite sentir la puissance qui en résulte. Il n'est pas le genre d'outil qu'on installe sans une étude préalable de sa configuration au risque de se retrouver avec des rapports peu exploitables. PMD se positionne plus comme l'outil à mettre en place automatiquement sur un ensemble de projets par l'architecte à l'attention de ses développeurs. Et si l'outil est effectivement puissant, il vaut mieux réfléchir à deux fois plutôt que de perdre les développeurs sous une avalanche de messages peu pertinents voire inexploitables et incompréhensibles !
  • # jrat

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

    On m'a dit beaucoup de bien de jrat
    http://jrat.sourceforge.net/

    ウィズコロナ

  • # Sonar

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

    http://sonar.codehaus.org

    Installer en 5 minutes montre en main.
    Et pas besoin d'alourdir une installation d'eclipse déjà trop lourde....
    • [^] # Re: Sonar

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

      Assez d'accord. Pour moi les outils de reporting ont mieux leur place sur un serveur d'intégration continue.
      Sinon, il y a des plugins maven très efficace pour une utilisation locale.
      Mais bon, c'est plus le code des autres qu'il est intéressant d'auditer, n'est-ce pas :-D
    • [^] # Re: Sonar

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

      c'est clair, cet outil est une tuerie.

      Mais si qqu'un se sentait le courage d'intégrer UCDetector comme plugin sonar, il aurait toute ma gratitude.

      Parce qu'il trouve des choses vraiment utiles, pour le code mort notamment, il est capable de trouver du code uniquement utilisé par les tests !
  • # Moui

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

    [remarque]
    Premier avantage : son format de plugin Eclipse
    Moui, bah moi les plugins eclipse j'en ai ras la couette !

    A chaque fois qu'ils sortent, ils ne sont compatible qu'avec la dernière version d'eclipse (si le logiciel est maintenu) ou alors avec la version de l'an 1000.

    Au final on se retrouve avec un eclipse différent par plugin, cherchez l'erreur...


    Au taf j'ai du lutter pour faire tenir dans un seul eclipse tous mes plugins (flex, j2ee, svn, uml, etc), et j'archive systématiquement les fichiers installés pour pouvoir restaurer un environnement identique en deux coup de cuillère à pot.

    [/remarque]
    • [^] # Re: Moui

      Posté par  . Évalué à 9.

      Mais quand est-ce que le monde va lâcher eclipse et essayer netbeans, qui lui au moins ne nécessite pas 50 plugins supplémentaires pour gérer SVN , maven, hudson, javascript, ruby, jira... Netbeans n'utilise pas de fichier de projet inutilisables comme les .projects d'eclipse, qui se mettent joyeusement à planter dès que le vent tourne. Non, à la place on a du ant un peu verbeux, mais ça fait plein de trucs et au moins ça peut s'utiliser en mode console.
      Au passage je décerne la palme du plugin naze à m2eclipse, qui a bien mis du temps avant d'être utilisable sereinement.
      Netbeans, c'est meilleur, mangez-en.
      • [^] # Re: Moui

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

        Non, à la place on a du ant un peu verbeux, mais ça fait plein de trucs et au moins ça peut s'utiliser en mode console.

        Ou alors netbeans utilise directement les pom.xml de maven. C'est l'IDE qui s'adapte à l'outil utilisé et pas le contraire.
      • [^] # Re: Moui

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

        Ah mais qu'on soit bien d'accord, je préfère netbeans à Eclipse. A la maison j'utilise que ça d'ailleurs. Mais FlexBuilder est un plugin eclipse, donc pas le choix...
    • [^] # Re: Moui

      Posté par  . Évalué à 0.

      Meme si je ne peux qu'abonder dans ton sens (j'ai toujours peur d'instalelr un plugin eclipse et que ca me foute la grouille dans mon install), mais pour j2ee et svn, faut pas pousser quand meme, c'est dispo pour toutes les versions.

      Flex builder est decent avec un support 3.2/3/4
      Il peut s'installer dans un galilleo windows 32bits via un hack: l'installer dans un ganymede, puis migrer l'install vers le galilleo.
      Oui, c'est pete couille, adobe est assez chiant avec son installeur a la mord moi le noeud. Et c'est encore pire depuis qu'ils ont droppe leur updateur, si tu veux updater ton flex framework, faut le faire a la main.
      Et c'est du windows only cette manip, pas de flex builder pour galileo mac, ce qui est bien dommage vu que c'est cette release qui est implementee en cocoa.

      Le probleme vient pas d'eclipse, mais du support 64 bits de flex.

      Vivement Flex4. Un jour peut etre, quand adobe se decidera a le releaser...
  • # Commentaire supprimé

    Posté par  . Évalué à 5.

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

    • [^] # Re: Le compilateur ne fait pas ça ?

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

      M' enfin, les compilateurs qui font de l' optimisation globale, il n' y en a pas beaucoup.
      Par exemple, gcc ne peux pas savoir qu'une fonction non statique n'est jamais appelée dans ton application: c'est uniquement au moment de l'édition de liens qu'on peut s'en rendre compte.
      Comme en java, la liaison est dynamique, et l'unité de compilation est la classe, ces outils permettent de lever des points "douteux" que le compilateur ne doit pas résoudre seul (sinon, le compilateur est faux).
      • [^] # Commentaire supprimé

        Posté par  . Évalué à 5.

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

        • [^] # Re: Le compilateur ne fait pas ça ?

          Posté par  . Évalué à 2.

          >> M' enfin, les compilateurs qui font de l' optimisation globale, il n' y en a pas beaucoup.

          > Il y a au moins icc et llvm, pour java aucune idée. Mais dans tous les cas, il est beaucoup
          > mieux de l'inclure dans le compilo.

          Je sais pas pour LLVM, mais pour icc, si tu pensais à IPO (interprocess optimization), si ta base de code est un tant soit peu très grosse, les temps de compil' explosent avec un risque non-négligeable de « internal error » (plus de mémoire, /tmp plein, ou bien certaines limites internes atteintes). IPO est très bien pour des outils dont la base de code est de taille raisonnable. Il y a un autre inconvénient à IPO : les .o générés ne sont pas des .o standards, et c'est gênant pour interagir avec d'autres compilateurs quand on en a besoin.
      • [^] # Re: Le compilateur ne fait pas ça ?

        Posté par  . Évalué à 3.

        C'est pas un peu l'un des intérêts de LLVM ?

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Le compilateur ne fait pas ça ?

      Posté par  . Évalué à 1.

      Effectivement le compilo Java emet des avertissement quand il détecte du code mort, cela dit c'est un problème indécidable donc j'image que les dif. logiciels sont plus ou moins performants. Je ne crois pas que java aille bcp plus loin que de voir les if(false)
    • [^] # Re: Le compilateur ne fait pas ça ?

      Posté par  . Évalué à 2.

      sisi, ca le fait, dans une certaine mesure.
      code mort, genre if(false) bla il va le trouver.
      Fonction privees jamais appellees itou.
      if(bla) return truc else machin va te generer un warning "else inutile"
      assignement inutile (x = x), if (a=b) au lieu de a == b, bref il peut trouver un paquet de trucs, mais ca reste du tres local, confine a une classe ou une methode.

      maintenant, une fonction publique jamais appelee par ton code ne peut pas etre consideree comme du code mort par le compilo, faut un contexte pour savoir si ce code est considere mort.

      De plus, ces outils font bien plus que ca, ils te donnent des warnings sur la facon d'implementer certaines choses (genre les boulets qui mettent toutes les variables locales final), des trucs qui sortent de tes conventions de codage, bref des trucs qui sortent du scope d'un compilo, les bugs potentiels, bref c'est de l'analyse statique de code, bien plus pousse que ce que fait un compilo.

      Et aussi, ca te donne des rapports, tu sors le truc en html ou ce que tu veux et c'est accessible en dehors de ton ide, genre au big boss qui peut ensuite fouetter le dev qui a la plus de warnings.
    • [^] # Re: Le compilateur ne fait pas ça ?

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

      Ca a été un peu dit plus haut, mais en gros ce n'est pas possible car Java fait de la compilation séparée.

      Java, comme la plupart des langages objet fonctionne sur le principe de "Open World Assumption" : le code compilé peut recevoir du code par la suite, au runtime (plugin, etc...)

      Ce que tu propose implique de partir sur du CWA ( Closed World Asumption), qui consiste à travailler sur la fermeture transitive du graphe du code où là tu peux virer le code mort. Les compilateurs basés là dessus sont rares (SmartEiffel et Lisaac à ma connaissance)

      Gcc le fait en partie car sur du code procédural comme du C, c'est faisable. Sur de l'objet, c'est beaucoup plus compliqué, car le typage devenant dynamique, tu te retrouves avec des sites d'appels dans tous les sens et ta complexité explose (np complet).

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

      • [^] # Commentaire supprimé

        Posté par  . Évalué à 1.

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

        • [^] # Re: Le compilateur ne fait pas ça ?

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

          Tu demandes si le compilo Java ne peut pas faire de l'analyse globale, je te répond non et explique pourquoi.
          Tu me répond que je me trompe parce que LLVM peut le faire.

          Je sais que LLVM peut le faire, mais ce n'est pas répondre à ta question.

          LLVM travaille sur un code assez bas niveau en analyse plus ou moins globale, effectivement c'est possible.

          C'est plus une question de choix d'approche et de leur conséquence que de "qui a la plus grosse, et est pas assez fort pour le faire"

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

        • [^] # Re: Le compilateur ne fait pas ça ?

          Posté par  (Mastodon) . Évalué à 2.

          Le nombre de points d'entrée dans le code via le monde est nécessairement fini et cela peut-être pris en compte.

          Du point de vue du compilateur Java, les points d'entrée, ça peut être n'importe quelle méthode qui n'est pas déclarée "private". Donc oui, c'est fini, mais en pratique, ça ne nous avance pas tellement, d'où l'intérêt d'avoir des outils un peu plus évolués.
          • [^] # Re: Le compilateur ne fait pas ça ?

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

            C'est bien pire que ça, grâce à l'introspection et la reflection, même une méthode jamais appelée "en dur" dans le code peut l'être à l'exécution, selon les actions de l'utilisateur. Il est donc impossible de savoir si une méthode sera réellement appelée un jour ou non.
            • [^] # Re: Le compilateur ne fait pas ça ?

              Posté par  . Évalué à 1.

              yep
              c'est comme ca que tu finit avec un attribut prive, lui mettre un getter et un setter prive pour hibernate, qu'il faut donc tagger avec @SuppressWarning("unused") et documenter pourquoi le getter/setter est la alors que visiblement, il ne sert a rien.
              • [^] # Re: Le compilateur ne fait pas ça ?

                Posté par  . Évalué à 1.

                Je ne connais pas vraiment Hibernate (seulement de nom), mais tu es en train de dire qu'il peut (via l'introspection ?) accéder à des méthodes privées ?
                Ça ne va pas à l'encontre de l'encapsulation, ça ?
                Et qu'est-ce qui justifie que ces méthodes soient privées ? Pourquoi ne pas mettre les getter/setter publics, comme c'est généralement le cas ? Pour empêcher toute modification de l'objet par autre chose qu'Hibernate ?
                • [^] # Re: Le compilateur ne fait pas ça ?

                  Posté par  . Évalué à 2.

                  Oui, hibernate joue avec la visibilite des methodes pour pouvoir acceder a des getter/setter private/protected, idem pour les constructeurs sans argument protected. C'est techniquement qq chose d'assez trivial a faire, c'est juste lourd a mettre en place, l'api d'instrospection Java n'etant pas des plus agreable a utiliser.

                  Et oui, ca va plus ou moins a l'encontre de l'encapsulation, mais pas vraiment en fait. On va dire que c'est un moindre mal en fait vu ce qu'hibernate apporte.
                  Faut pas voir hibernate comme une couche au meme niveau que tes objets ou comme un consommateur de tes objets, mais une couche qui se met en dessous, entre tes objets et ta DB.
                  C'est donc ok d'acceder au contenu de l'objet en outrepassant les regles de l'objet dans ce cas. Faut juste garder a l'esprit comment hibernate marche pour eviter certains pieges.
                  Ca tord donc un peu le concept objet ou l'objet est maitre chez lui, mais vu ce que ca t'apporte quand t'as de gros graphes d'objets et une DB assez touffue, ben c'est vraiment un moindre mal.

                  Sinon, ce qui justifie que ces methodes soient privees, c'est assez simple en fait.
                  Au niveau objet, ton attribut est prive pour les raisons usuelles: aucune raison de l'exposer, il est modifie par des methodes publique plus haut niveau.
                  Typiquement, une collection tu ne veux pas que tes consommateur puisse la modifier directement, mais passent par des methodes add/remove, tu vas avoir:
                  private List maCollection = new ArrayList();
                  public void addMachin(machin)
                  {
                  du code de synchro ou des sanity tests
                  maCollection.add(machin)
                  encore du code
                  }

                  public void removeMachin(machin)
                  {
                  comme plus haut
                  }

                  Maintenant, tu veux que ta collection soit persistee dans la DB.
                  Hibernate se basant sur les getters/setters pour trouver les attributs a persister (je sais il peut le faire sur les attributs aussi, mais c'est assez vilain a regarder et dur a lire), la notion habituelle de java bean, ben tu vas creer un getter setter pour cet attribut.
                  Mais comme t'as aucune envie que qq1 ait un acces direct a ta collection, ben tu vas les mettre prives.

                  Le meme cas de figure peut aussi se presenter avec des methodes protected (accessible au package donc). Mais comme hibernate n'est pas du tout du meme package, ben il va devoir trifouiller la visibilite.
  • # Intégration continue

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

    Une chaîne d'intégration continue, avec un outil tel que Hudson, permettra aussi de générer des rapports d'analyse de code (lisibilité, maintenabilité, bugs potentiels, code mort, etc.), et bien plus encore. Bien sûr, sa mise en œuvre est un peu plus lourde, mais on s'y retrouve vite.
  • # Maven+Continuum+checktyle+findbugs+junit+cobertura

    Posté par  . Évalué à 2.

    Nous utilisons :
    * Maven pour le build
    * + Continuum pour faire les builds en continu
    * + plugin maven checkstyle et findbugs pour l'audit de code
    * + junit pour nos tests unitaires et cobertura pour examiner la couverture de nos tests

    Le tout fonctionne super sur un relativement gros projet (+ de 100 modules maven, ~400000 java loc)
  • # La nuit du code mort vivant

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

    >> UCDetector conviendra à un développeur souhaitant un filet de protection relativement simple mais toujours pertinent.

    C'est clair, du code mort, faut s'en protéger, c'est super dangereux. En plus c'est dur à tuer : tout le monde n'arrive pas à lui mettre un coup dans l'entête.

    Et sinon, le code mort quasiment n'existe pas, car tu as l'introspection, et la possibilité de créer dynamiquement le nom d'une méthode à appeler.
    • [^] # Re: La nuit du code mort vivant

      Posté par  . Évalué à 2.

      Il reste quand même la possibilité, au sein d'une même méthode, d'avoir un "else" qui ne peut pas être appelé. Cela vient généralement d'une condition foireuse au niveau du "if", et c'est le signe d'un vrai problème.

      Idem avec un "while" dans lequel on ne pourrait pas rentrer, un "do" dont on ne pourrait pas sortir, etc...
      • [^] # Re: La nuit du code mort vivant

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

        Oui, c'est pour ça que j'avais écrit qu'il n'existe « quasiment » pas.

        Mais une branche pas appelée, c'est pas un « problème » je trouve. C'est quelques bits ou octets gâchés, certes, mais ça n'influe pas sur l'exécution du programme.
        C'est un signe comme quoi le programme n'a pas été proprement spécifié, et contient de la redondance…

        Néanmoins, j'avoue n'avoir pas pensé à la bête erreur d'une boucle sans fin qui rend caduque tout le reste du programme. Mais là, j'ai beau être un fanatique des méthodes formelles, je crois qu'un minimum de test devrait régler ces problèmes.
        • [^] # Re: La nuit du code mort vivant

          Posté par  . Évalué à 3.

          Techniquement, du code mort non détecté peut empêcher le compilateur de
          1/ Simplifier encore plus le code
          2/ L'inliner parce que ces « quelques kio de plus » font que le code d'une méthode/fonction dépasse le seuil interne à partir duquel inliner est intéressant pour le compilo
          3/ optimiser encore plus avant le code utile.
        • [^] # Re: La nuit du code mort vivant

          Posté par  . Évalué à 1.

          ben une branche pas appellee, c'est un peu un probleme quand meme.
          Le dev l'a pas ecrite parce qu'il trouve trouve rigolo et artistique d'ecrire du code qui n'est jamais execute.

          S'il l'a ecrit ce code, c'est parce qu'il pense qu'il va etre execute. Idem s'il est repasse derriere, a change la condition mais n'a pas enleve le else.
          Ya un decalage entre ce que pense le dev et ce que fait le code.
          C'est pas forcement dramatique et peut tres rester a vie dans le programme sans gener personne, mais ca reste un pb.

          Ca rend aussi la maintenance par une autre personne qui a ecrit le code mort plus dure: faut tout se taper, realiser que le code est bel et bien mort et ensuite determiner si c'est un bug ou juste une betise de l'auteur/patcheur.
          Ca prend du temps pour pas grand chose au final.
          • [^] # Re: La nuit du code mort vivant

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

            C'est surtout intéressant pour de la lib : tu ne vas embarquer que le code de la lib utilisé par le programme.

            Le code mort dans un classe c'est intéressant, mais assez limité, et ça rejoint ce que tu dis, le programmeur est quand même censé le voir ! :-)

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

Suivre le flux des commentaires

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