• Aucun résultat trouvé

1.2 Réalisation de l’outil

1.2.3 Une application parallèle

Afin de pouvoir traiter le volume de code des applications actuelles qui dépasse parfois le million de lignes de code, et parce que l’évolution des architectures de processeurs est orientée vers le multi-cœur pour des raisons de physique incontournable ; notre outil a été conçu pour fonctionner de manière parallèle par l’exécution de plusieurs tâches indépendantes simultané- ment. Ces tâches s’organisent en un réseau de processus de Kahn (KPN) [Kah74] qui s’adapte au projet à analyser ainsi qu’aux règles à appliquer. Issus du lambda calcul, les réseaux de processus de Kahn permettent un résultat déterministe avec des processus déterministes et un ordonnancement non déterministe. Utiliser ce formalisme confère à notre système les proprié- tés suivantes :

– Monotonicité : si les processus qui composent le réseau ont une durée d’exécution finie, et le graphe représentant le réseau est un graphe acyclique orienté, l’ensemble du réseau possède une durée d’exécution finie.

– Parallélisation : il est possible d’exécuter les processus de manière parallèle sans risque d’inter-blocage9.

– Déterminisme : pour des entrées de données équivalentes, le résultat est toujours iden- tique à la fin de l’exécution.

Deux types descopes10sont possibles pour une tâche, en fonction de la nature des traite- ments qu’elle doit effectuer. Chaque tâche qui fonctionne avec un scope fichier est dupliquée pour que chaque fichier qui compose l’application cible ait une instance de la tâche qui s’y applique. Les tâches possédant un scope projet ne sont instanciées qu’une fois. Pour éviter des problèmes de synchronisation, une tâche ne démarre que lorsque tous les éléments dont elle a besoin sont disponibles ; c’est à dire tokens11en provenance d’autres tâches et fichiers utilisés à l’aide de sémaphores. Le câblage entre les tâches qui possèdent un scope fichier et celles qui possèdent un scope projet est assuré par des nœuds de routage :

– Un multiplexeur pour regrouper les tokens fichiers vers une tâche projet, – Un hub qui va dupliquer les tokens d’une tâche projet vers les tâches fichiers,

– Un switch qui permet de trier les données contenues dans les tokens pour les adresser à l’instance de script correspondante. Effectivement, les contenus des tokens sont en fait la plupart du temps des nœuds d’AST qui correspondent à des morceaux de fichiers. Une tâche projet va produire un token contenant des morceaux de plusieurs fichiers différents. Les taches fichiers ne traitent (et ne verrouillent) qu’un fichier. Il faut donc leur fournir uniquement les données issues du fichier qui leur a été attribué.

Les tâches sont ordonnancées selon un ordre défini par les dépendances intrinsèques aux règles pour lesquelles elle sont instanciées. De plus chaque règle possède un ordre de priorité. Par 9. Lorsque deux processus doivent travailler sur une même ressource, il convient d’ajouter un système à base de sémaphores. Dans notre outil, les ressources sont le code des applications ; il a donc été prévu deux niveaux de verrous, un niveau fichier (un seul fichier par processus) et un niveau projet (l’ensemble des fichiers pour un processus) qui seront sollicités en fonction de la nature des scripts.

10. cible

exemple, les transformations qui concernent la mise en forme doivent s’appliquer en dernier pour ne pas être altérées par d’autres modifications.

1.2.4 Ergonomie

Une règle est l’expression d’une bonne pratique à détecter, et éventuellement à corriger lorsque cela est possible. Pour permettre une réutilisabilité des scripts et un développement fa- cile, une tâche complexe est préférablement décomposée en plusieurs sous-tâches remplissant une fonction simple. Une règle se compose donc de plusieurs scripts communicants qui sont eux même écrits en Java12. Chaque script possède des métadonnées pour décrire son mode de fonctionnement au sein du réseau de processus. Notamment il y est mentionné sa dépen- dance envers d’autres scripts et ses possibilités de configuration. Ces métadonnées apparaissent sous forme d’annotations Java afin d’être accessibles à l’exécution. La structure d’une règle se construit par conséquent à l’exécution par résolution de pré requis entre les scripts.

D’autres métadonnées qui n’apparaissent pas dans le source du script y sont également atta- chées à celui-ci au sein de l’atelier de développement de règles pour gérer l’état d’avancement, les tests, les versions, l’auteur,. . .

L’ensemble de la base de règles développées est appelé le méta-référentiel. Un référentiel est un sous-ensemble du méta-référentiel. Il se compose de règles choisies pour appliquer une norme de codage, une politique de sécurité,. . ., en fonction des besoins de l’application à traiter. Cet ensemble de règles est trié en arborescence en fonction de caractéristiques, de fonctions ou de cibles communes. Un référentiel possède également une configuration, qui se compose de la liste de l’ensemble des règles activées et de leurs configurations respectives.

Afin de pouvoir arriver rapidement à un volume de règles d’analyse statique conséquent, un atelier de développement de règles ergonomique sous forme de plugin pour IDE a été réalisé. Des mécanismes automatisés ont été implémentés tels que :

– Les tests de validation des règles et de ses sous-composants les scripts.

– Les tests de non régression du méta référentiel. Le résultat de l’exécution d’un jeu de test comportant les modèles recherchés est comparé avec le résultat attendu.

– Le packaging de référentiels et la génération de documentations à partir des métadonnées des scripts, des règles et des référentiels.

L’édition des règles, ainsi que la création de tous les éléments entrant en jeu dans la constitution d’un référentiel sont assistés graphiquement. La sauvegarde en base des informations et le versionning sont assurés sur un serveur distant.

Le principe d’application des règles est illustré en figure 1.1 pour son fonctionnement en mode batch. Il convient pour les processus d’intégration continue (cf annexe A.1) de type Maven ou Ant. Un fonctionnement sous forme de plugin pour IDE a également été prévu.

12. nous avons choisi également le Java pour développer notre outil, ainsi, l’utilisateur pourra plus facilement créer ses propres scripts puisqu’il s’agit du même langage que son application.

FIGURE1.1 – Utilisation de notre outil d’audit et de transformation.

1.3

Conclusion

L’ensemble des propriétés mises en évidence dans la première partie qui font souvent défaut aux outils existants ont été intégrées avec succès à notre prototype. La parallèlisation de l’exé- cution est assurée par la mise en œuvre d’un réseau de processus de Kahn. Chaque traitement se décompose en threads qui s’échangent les résultats de leurs calculs. Les traitements sont ordonnancés entre eux en respectant les priorités des règles qu’ils permettent d’appliquer13. La solution que nous avons adoptée pour parvenir à agréger tous les composants d’une appli- cation Web a été d’encapsuler les contenus non Java dans des classes Java fictives afin de les soumettre au même système d’abstraction et de manipulation du code. La création simplifiée et la configuration des règles permettent à l’utilisateur de maîtriser les traitements appliqués au code. Enfin, la complète automatisation de l’outil et un mode de fonctionnement en ligne de commande permet son intégration dans un environnement de développement agile sur un ser- veur de compilation. Notre système de scripts est un système ouvert qui permet de configurer et d’alimenter facilement la base de règles. Les apports de ces caractéristiques et les nouveaux types d’analyses qui peuvent être effectués sont illustrés par des résultats dans le chapitre sui- vant.

Résultats

Notre outil permet d’appliquer un ensemble de règles pour auditer et corriger une applica- tion Web. Notre approche nous a permis de dépasser le potentiel d’analyse et de transformation des outils existants pour les applications Web. Ceci passe par la rédaction de règles innovantes et spécifiques dont certaines sont détaillées ici. Nous avons montré que cette nouvelle couver- ture fonctionnelle donne accès à des problèmes encore non contrôlés. Des exemples d’implé- mentation et d’utilisation de ces règles et de leur application sont présentés dans ce chapitre. Les résultats obtenus sont également montrés.

Cette section propose des exemples de problèmes courants lors du développement d’ap- plications Web Java. Ces cas ont été étudiés afin de développer et d’appliquer les règles de détection et de correction qui y correspondent.

2.1

Fonctionnalités classiques

2.1.1 Checkstyle-PMD-finbugs

Une série de 400 règles simples issues des outils les plus populaires1 a été implémentée pour notre outil. Environ la moitié propose une transformation du code qui permet de corriger le problème détecté.

Les performances constatées2pour un audit réalisé sur un projet de taille moyenne (1600 fichiers, représentant 250kloc), avec une machine possédant 2 giga octets de mémoire et un processeur Intel Core2Duo 1,8 Ghz :

– Pic de mémoire utilisée : 1.4 go, – Temps d’exécution : < 45 min.

1. Les principaux sont PMD, FindBugs, Checkstyle.

2. On ne peut pas comparer ces chiffres aux temps d’exécution de PMD, Findbugs et Checkstyle directement car ici le temps de correction d’un très grand nombre d’erreurs (plusieurs milliers) est inclu. Il est évident que pour ces outils, la correction manuelle nécessite bien plus de temps même pour un utilisateur expérimenté.

2.1.2 Renommage

Les fonctions de renommage sont proposées depuis longtemps par la majeure partie des outils de développements. Cependant, ils ne peuvent appliquer la transformation de centaines, voire de milliers d’éléments de manière complètement automatique. De plus, leur champ d’ac- tion est très souvent limité au code Java. Enfin, le temps d’application (parce que manuelle et non parallélisable) sur des gros projets est parfois rédhibitoire.

Comme test, nous avons choisi d’appliquer une convention de nommage configurable sur le projet Open Source Xerces Java parser v2.10. Le refactoring impacte 32035 symboles et 882 types parmi 674 fichiers Java. Les symboles accédés par des dépendances (sous forme de byte- code principalement) sont préservés. Cette convention consiste à préfixer en fonction du type. Le formatage de la casse est également prévu pour les champs, les variables et les paramètres. Pour les classes, méthodes et packages, nous appliquons la convention Sun. L’analyse et le refactoring de l’ensemble du projet prennent 25 minutes et 2 secondes sans intervention exté- rieure avec une machine possédant 2 giga octets de mémoire et un processeur intel Core2Duo 1,8Ghz.