Projet Lande

previous up next contents
Précédent : Logiciels Remonter : Projet LANDE, Conception et validation Suivant : Actions industrielles



Résultats nouveaux

Nous utilisons la dichotomie aval/amont pour présenter nos résultats récents: la partie <<aval>> regroupe nos travaux sur l'analyse (dynamique ou statique) de programmes et le test de logiciels; la partie <<amont>> traite des langages déclaratifs et des architectures logicielles.

Actions <<aval>>

Nous présentons d'abord des résultats généraux sur l'analyse de programmes (modules [*] et [*]) avant de détailler un certain nombre de recherches portant sur des analyses particulières: la vérification de propriétés de sécurité (module [*]) et la compilation de $\lambda$Prolog (module [*]). Nous décrivons ensuite nos travaux récents sur le débogage statique (module [*]), le débogage dynamique (modules [*] et [*]) et la génération de jeux de test (module [*]).

Construction systématique d'analyses génériques



Participants : Valérie Gouranton , Daniel Le Métayer


Mots-clés : Analyse de programmes, Sémantique naturelle, Slicing


 

Résumé : Certaines analyses, comme le slicing (cf. module [*]) ne sont pas restreintes à un langage de programmation particulier. Plutôt que d'en fournir une nouvelle définition pour chaque langage, nous avons montré qu'il est possible de définir une telle analyse de manière générique et de l'instancier pour obtenir des analyses particulières. Nous avons proposé pour ce faire un format de sémantique naturelle et nous avons appliqué cette technique à l'analyse de slicing. Nous avons pu ainsi dériver, par simple instanciation d'une définition générique, des analyses dynamiques et statiques pour un langage impératif, un langage fonctionnel et un langage de programmation logique.

De nombreux travaux ont été effectués sur les fondements de l'analyse sémantique, la correction et la précision des analyseurs. On peut regretter cependant le peu d'attention accordé jusqu'à présent à la conception d'outils d'analyse génériques. Il est ainsi très difficile de factoriser les efforts en matière de réalisation d'analyseurs. Nous avons abordé ce problème en considérant la construction d'analyseurs sous l'angle de la dérivation de programmes à partir de spécifications [11,21]. Le programme en l'occurrence est l'analyseur lui-même et sa spécification est composée de deux parties:

1.
La sémantique opérationnelle du langage de programmation décrite sous forme de sémantique naturelle.
2.
La propriété recherchée exprimée sous forme de récurrences sur les arbres de preuves de la sémantique naturelle.
Les deux composants peuvent en fait s'exprimer sous forme de fonctions et la dérivation consiste en une série de transformations (pliage/dépliage) permettant d'obtenir un programme récursif autonome qui constitue l'analyseur.

L'intérêt de cette démarche est qu'elle permet d'exprimer dans un même cadre des analyses variées sur différents langages. Nous en avons fait la démonstration pour des analyses de programmes impératifs (durée de vie des variables), logiques (clôture des termes) et fonctionnels (nécessité, globalisation) [11]. Certaines de ces analyses sont spécifiques à un langage de programmation: on peut citer dans cette catégorie l'analyse de clôture pour la programmation logique. D'autres, comme le slicing (cf. module [*]) ont un intérêt plus général. Plutôt que d'en fournir une nouvelle définition pour chaque langage, nous avons montré qu'il est possible de définir une telle analyse de manière générique et de l'instancier pour obtenir des analyses particulières. Nous avons proposé pour ce faire un format de sémantique naturelle et nous avons appliqué cette technique à l'analyse de slicing. Nous avons pu ainsi dériver, par simple instanciation d'une définition générique, des analyses dynamiques et statiques pour un langage impératif, un langage fonctionnel et un langage de programmation logique [11]. Nous nous intéressons maintenant à l'application de cette méthode à d'autres analyses d'intérêt général comme l'analyse de partage et à sa mise en oeuvre dans un outil d'aide à la conception d'analyseurs.

Spécification et implantation d'analyses à partir de règles d'inférence



Participant : Thomas Jensen


Mots-clés : Typage non-standard, Logique de programmes, Modularité


 

Résumé : Nous avons proposé une méthode de spécification d'analyses, où une analyse est définie comme une logique de programmes par des règles d'inférence. Le cadre peut accommoder plusieurs analyses d'une précision variable en ajoutant des connecteurs logiques (conjonction, disjonction, etc.). En nous appuyant sur des résultats récents concernant l'inférence de types d'intersection et de types polymorphes, nous avons conçu un algorithme qui permet de déduire pour chaque programme une propriété principale à partir de laquelle toute autre propriété démontrable par l'analyse peut être déterminée. Un avantage de cet algorithme est qu'il traite des fragments de programmes aussi bien que des programmes entiers. Il s'agit donc d'une démarche qui, à plus long terme, peut mener à un cadre général pour l'analyse modulaire.

Nous avons proposé une méthode de spécification d'analyses, où une analyse est définie comme une logique de programmes par des règles d'inférence. Le cadre peut accommoder plusieurs analyses d'une précision variable en ajoutant des connecteurs logiques (conjonction, disjonction, etc.). Nous avons focalisé nos recherches sur une logique avec propriétés disjonctives pour un langage avec procédures d'ordre supérieur et types récursifs (listes, arbres, etc.). Cette activité a abouti à un cadre qui réunit et étend plusieurs techniques d'analyse existantes [16].

Exprimer une analyse par des règles d'inférence comme un système de typage non-standard présente l'avantage de la rendre plus compréhensible mais permet également de s'appuyer sur des algorithmes de typage connus. Dans des travaux antérieurs, nous avons conçu une méthode efficace pour vérifier qu'un programme satisfait une propriété donnée. Se pose alors la question de savoir s'il est possible, étant donné seulement le programme, de trouver les propriétés satisfaites par ce programme, c'est-à-dire inférer les propriétés. En nous appuyant sur des résultats récents concernant l'inférence de types d'intersection et de types polymorphes, nous avons conçu un algorithme qui permet de déduire pour chaque programme une propriété principale à partir de laquelle toute autre propriété démontrable par l'analyse peut être déterminée. Un avantage de cet algorithme est qu'il traite des fragments de programmes aussi bien que des programmes entiers. Il s'agit donc d'une démarche qui, à plus long terme, peut mener à un cadre général pour l'analyse modulaire [24].

Vérification de propriétés de sécurité de programmes Java



Participants : Thomas Jensen , Daniel Le Métayer , Tommy Thorn


Mots-clés : Téléchargement, Sécurité, Chargement dynamique, Java


 

Résumé : Nous nous sommes attaqués à la formalisation de la sémantique du langage Java en nous focalisant sur les règles de visibilité (des classes et de leurs membres) et leur évolution lors du chargement dynamique de classes. Il s'agit en effet de caractéristiques particulières de Java qui ont un impact direct sur la sécurité et dont les définitions informelles ne sont pas exemptes d'ambiguïtés ou d'insuffisances. Cette formalisation en terme de systèmes d'inférence nous a permis de décrire de manière rigoureuse l'origine d'une erreur de sécurité qui avait été découverte empiriquement par des chercheurs d'ATT.

L'un des intérêts majeurs du langage Java est la possibilité de télécharger du code et de l'exécuter de manière transparente. Cette pratique soulève de sérieux problèmes en matière de sécurité des informations (confidentialité, intégrité notamment) et Java y répond, entre autres, par une plus grande sûreté de programmation (typage, vérification de code importé, héritage simple, etc.), un gestionnaire de sécurité et des recommandations en matière de politique de sécurité [18]. Le langage lui-même est supposé sûr mais cette affirmation est toujours sujette à débats. Il ressort clairement des déclarations contradictoires à ce sujet que Java intègre un certain nombre de dispositions complexes qui rendent indispensable la formalisation du langage (pour le moins de certains de ses aspects critiques). Nous nous sommes attaqués à cette formalisation en nous focalisant sur les règles de visibilité (des classes et de leurs membres) et leur évolution lors du chargement dynamique de classes [34]. Il s'agit en effet de caractéristiques particulières de Java qui ont un impact direct sur la sécurité et dont les définitions informelles ne sont pas exemptes d'ambiguïtés ou d'insuffisances. Cette formalisation en terme de systèmes d'inférence nous a permis de décrire de manière rigoureuse l'origine d'une erreur de sécurité qui avait été découverte empiriquement par des chercheurs d'ATT. Notre objectif actuel est de définir de la même manière des politiques de sécurité afin de pouvoir étudier leur effet (c'est à dire les exigences qu'elles imposent) sur les composants d'une application Java (comme le chargeur de classes).

Analyse statique de programmes $\lambda$Prolog



Participant : Olivier Ridoux


Mots-clés : Analyse statique, $\lambda$Prolog


 

Résumé : Nous étudions l'analyse statique de $\lambda$Prolog par la technique de la compilation abstraite où un programme source est traduit en un autre programme dont les résultats sont interprétés comme le résultat de l'analyse du programme source. L'extension à $\lambda$Prolog des techniques éprouvées dans le cas de Prolog nécessite entre autres le traitement des $\lambda$-termes simplement typés.

Nous avons choisi pour l'analyse statique de $\lambda$Prolog la technique de la compilation abstraite où un programme source est traduit en un autre programme dont les résultats sont interprétés comme le résultat de l'analyse du programme source. Cette méthode a déjà été employée pour Prolog et l'appliquer à $\lambda$Prolog nécessite des extensions dans trois directions:

1.
La prise en compte de la structure des formules de $\lambda$Prolog (formules de Harrop au lieu de formules de Horn).
2.
Le traitement de la structure des termes de $\lambda$Prolog ($\lambda$-termes simplement typés au lieu de termes de premier ordre).
3.
L'application à l'analyse de propriétés nouvelles dont l'utilisation permettrait d'optimiser l'implantation du langage (état de $\beta$-normalisation, combinateurs, etc.).

Le domaine de calcul choisi pour les programmes abstraits est celui des booléens. Dans ce cadre, nous étudions actuellement une solution au deuxième problème qui consiste à coder par un vecteur booléen une fonction de transfert de la propriété à analyser et à calculer pour chaque terme d'ordre supérieur sa contribution à la propriété et sa fonction de transfert. Ce travail fait l'objet d'une thèse à l'École des Mines de Nantes (Frédéric Malésieux) co-encadrée par Olivier Ridoux et Patrice Boizumault.

Débogage statique



Participants : Pascal Fradet , Ronan Gaugne , Daniel Le Métayer , Florimond Ployette


Mots-clés : Pointeur, Déréférence, Structure de données, Mise au point, Débogage statique


 

Résumé : Nous avons proposé une analyse statique de programmes pour la détection d'accès incorrects à la mémoire (déréférences de pointeurs invalides). Cet analyseur est très précis: il est capable par exemple de décrire exactement le partage induit par des structures complexes comme les listes circulaires. Dédié à l'aide à la mise au point de programmes, il permet un certain nombre d'interactions avec l'utilisateur. Celles-ci se font à l'aide d'un langage d'annotations qui s'intègre naturellement au formalisme de l'analyseur tout en restant très proche du langage de programmation afin de faciliter sa compréhension par le programmeur.

L'utilisation incorrecte de pointeurs est une des sources d'erreurs les plus répandues dans les langages impératifs. Par conséquent, tout vérificateur de code capable de détecter ce type d'erreurs à la compilation est le bienvenu. Nous avons proposé une analyse statique de programmes pour la détection d'accès incorrects à la mémoire (déréférences de pointeurs invalides) [10,32]. Un pointeur peut être invalide parce qu'il n'a pas été initialisé ou parce qu'il désigne une cellule de la mémoire qui a été désallouée. D'un point de vue formel, l'analyseur repose sur une sémantique opérationnelle structurelle du langage, dont nous dérivons une axiomatisation des propriétés d'accessibilité. Cette axiomatisation est ensuite raffinée en un analyseur qui est également décrit sous forme de système d'inférence. La correction de l'analyseur a été démontrée et sa complexité établie: elle est polynomiale en fonction du nombre de variables du programme et exponentielle en fonction de la profondeur d'imbrication des boucles. D'un point de vue pratique, notre analyseur se distingue de la plupart des tentatives précédentes par son traitement précis des structures de données récursives et par son intégration dans un système interactif. La précision de l'analyse et la possibilité d'interaction conditionnent en effet l'utilisation d'un tel analyseur dans un environnement d'aide à la mise au point de programmes. Notre analyseur est capable en particulier de décrire exactement le partage induit par des structures complexes comme les listes circulaires. Les interactions avec l'utilisateur se font à l'aide d'un langage d'annotations qui s'intègre naturellement au formalisme de l'analyseur tout en restant très proche du langage de programmation afin de faciliter sa compréhension par le programmeur. Les annotations correspondent soit à des hypothèses ajoutées par l'utilisateur, soit à des propriétés à vérifier par l'analyseur. Les hypothèses peuvent être utilisées pour représenter des connaissances sur le contexte d'exécution du code (ou d'appel d'une procédure) ou pour guider l'analyseur afin de se focaliser sur certaines parties de programmes ou certaines valeurs de données. Ces annotations permettent ainsi d'intégrer de manière naturelle débogage statique et dynamique (cf. module [*]).

Une première version de prototype a été réalisée. La partie haute (analyse syntaxique) est écrite à partir d'une version augmentée de Suif empruntée à l'évaluateur partiel Tempo (cf. avant-projet COMPOSE). La génération d'équations récursives est réalisée en SML et la résolution de ces équations repose sur un solveur de points fixes générique développé par ailleurs dans le projet.

Explications pour les bases de données déductives



Participants : Mireille Ducassé , Sarah Mallet


Mots-clés : Débogage, Explication, Génération et Analyse de trace, Base de Données Déductive


 

Résumé : Une base de données déductive est composée de faits (de base) et de règles de déduction déclaratives permettant d'augmenter la connaissance par des faits déduits. Ces règles permettent aux concepteurs de la base de données de se concentrer sur sa logique. Toutefois, les utilisateurs des bases de données ont besoin d'explications pour leur restituer cette logique qui peut être masquée par les détails opérationnels de la mise en oeuvre. Les systèmes d'explication existants rendent à l'utilisateur des arbres de preuve qui donnent une vue éclatée des évaluations. Pour rester plus proche de la notion ensembliste sous-jacente aux bases de données, nous proposons une structure de données, l'arbre DDB, reposant sur des ensembles de substitutions.

Une base de données déductive est composée de deux types de données: des faits de base stockés dans une base de données relationnelle et des données, dites virtuelles, déduites des données de la base à l'aide de règles.

La forme déclarative du langage de règles permet aux concepteurs de la base de données de se concentrer sur sa logique (cf. module [*]). Toutefois, la mise en oeuvre des bases de données déductives fait intervenir de nombreuses optimisations afin d'obtenir des manipulations de données efficaces. De ce fait, les utilisateurs des bases de données ont besoin de facilités de débogage et d'explication pour restituer la logique des programmes qui peut être masquée par les détails opérationnels (cf. module [*]).

Ces explications sont particulièrement nécessaires dans le contexte des bases de données déductives où les utilisateurs du logiciel doivent comprendre le déroulement des déductions pour en accepter les résultats ou mettre à jour les données, alors qu'ils ne sont pas forcément des informaticiens.

Les systèmes d'explication existants pour les bases de données déductives rendent à l'utilisateur des arbres de preuve. Ces arbres donnent une vision très détaillée de la manipulation des données. Cette présentation éclatée provoque une explosion du nombre d'arbres de preuve produits.

Pour rester plus proche de la notion ensembliste sous-jacente aux bases de données, nous proposons une structure de données, l'arbre DDB, reposant sur des ensembles de substitutions. Elle permet de rassembler les informations éparpillées dans les arbres de preuve et de les présenter de manière plus concise. Les arbres DDB peuvent être montrés aux utilisateurs ou bien analysés par un système d'explications [25,27,26].

Ce travail fait l'objet d'une coopération, pour l'instant informelle, avec une société nouvellement créée, Next Century Media. Cette société fournit un produit reposant sur la technologie des bases de données déductives et vise les applications dans la publicité ciblée. Des discussions sont en cours pour définir les informations qui doivent apparaître dans les traces d'exécution afin de bâtir des explications de plus haut niveau, par exemple l'arbre DDB mentionné précédemment.

Analyse de trace automatisée



Participants : Mireille Ducassé , Erwan Jahier


Mots-clés : Débogage, Analyse dynamique, Traceur abstrait, Prolog, Mercury


 

Résumé : Nous avons développé un outil, OPIUM, qui analyse les traces d'exécution de programmes générées par un traceur Prolog existant. Les programmeurs peuvent spécifier de manière précise ce qu'ils veulent observer du comportement du programme à l'aide de requêtes en Prolog. À partir du langage de requêtes, nous avons bâti des analyses qui donnent des vues abstraites des exécutions selon certains critères. Il a également été possible de mettre en oeuvre à faible coût des traceurs abstraits pour des langages de haut niveau. Si le contenu de la trace utilisée et les analyses proposées sont dédiés à Prolog, les techniques de base exploitent une trace dont le seul pré-requis est d'être séquentielle.

Nous avons développé un outil, OPIUM, qui analyse les traces d'exécution de programmes générées par un traceur Prolog existant (cf. module  [*]). Le programmeur peut poser des questions sur les exécutions à l'aide de Prolog et de quelques primitives dans une forme concise en s'appuyant sur la logique et les mécanismes de recherche de Prolog. Les programmeurs peuvent donc spécifier de manière précise ce qu'ils veulent voir du comportement du programme.

Ces requêtes peuvent être traitées à la volée ou a posteriori. Dans le cas d'un traitement à la volée, les performances du système permettent d'analyser plusieurs millions d'événements avec des temps de réponse supportables. L'analyse a posteriori nécessite de stocker les événements dans une base de données interne, ce qui prend un temps non négligeable. L'analyse proprement dite, par contre, ne prend pas plus de temps que l'analyse à la volée. Du point de vue de l'utilisateur, les requêtes se posent de la même manière dans les deux cas.

À partir du langage de requêtes, nous avons bâti des analyses qui donnent des vues abstraites des exécutions selon certains critères (par exemple, flot de contrôle ou flot de données). Il a également été possible de mettre en oeuvre à faible coût des traceurs abstraits pour des langages de haut niveau, ce qui a permis de mettre au point facilement des démonstrations d'applications sophistiquées.

Si le contenu de la trace utilisée et les analyses proposées sont dédiés à Prolog, les techniques de base pour mettre en oeuvre le langage de requêtes exploitent une trace qui peut être produite pour n'importe quel langage séquentiel.

Un article récapitule les techniques et les applications, il décrit en particulier de manière détaillée l'interface entre l'analyseur de traces et le traceur [13].

Dans le cadre du projet européen ARGo (cf. [*]), nous étudions le débogage de programmes logiques Mercury, un nouveau langage de programmation logique développé à l'université de Melbourne, Australie [SHC96]. Nous concevons actuellement un traceur pour Mercury. L'objectif à terme est d'appliquer des techniques proches de celles d'Opium pour analyser de manière automatique les traces générées.

Génération systématique de jeux de test



Participants : Daniel Le Métayer , Valérie-Anne Nicolas , Olivier Ridoux , Lionel Van Aertryck


  Mots-clés : Test en boîte noire, Test en boîte blanche, Test structurel, Contrainte, Jeu de test, Suite de test


Résumé : Nous avons proposé une méthode de génération de suites de tests qui forme le noyau de l'outil CASTING développé en collaboration avec la société AQL. La méthode est indépendante du format d'entrée, ce qui la rend uilisable aussi bien dans le cas du test structurel que fonctionnel. Les suites de tests engendrées dépendent de stratégies spécifiées par l'utilisateur, permettant ainsi d'atteindre la souplesse d'utilisation exigée pour un usage industriel.

Nous avons abordé le problème de la systématisation de la génération de jeux de test en tentant d'abolir la dichotomie <<boîte noire/boîte blanche>> (cf. module [*]). Pour ce faire, nous décomposons le processus de production des données de test en trois étapes [30]:

1.
L'acquisition des critères de test et la production des hypothèses de test associées, à partir de différents supports d'entrée.
2.
La décomposition des opérations en classes d'opérations et la génération d'un graphe d'accessibilité symbolique.
3.
La génération des jeux de test par parcours du graphe d'accessibilité en assurant un critère de couverture donné.
Les supports d'entrée peuvent être constitués de spécifications formelles ou informelles, de programmes sources ou de propriétés fournies directement par l'utilisateur. Les opérations peuvent être des machines abstraites dans le cas du langage B, des schémas pour le langage Z, des programmes dans le cas d'un langage de programmation, etc. Dans tous les cas, les critères de test sont implantés par des stratégies de test et se traduisent in fine par des hypothèses d'uniformité et hypothèses de régularité. Ces hypothèses permettent de préciser le sens (et les limites) des jeux de test qui seront engendrés (cf. module [*]). D'un point de vue pratique, une stratégie de test correspond à un mode d'extraction de contraintes à partir du texte source. Ces contraintes caractérisent les jeux de données qui devront être engendrés pour chaque opération du système. Le graphe d'accessibilité symbolique indique l'ordre dans lequel les opérations peuvent être appliquées pour satisfaire toutes les contraintes. Dans le cas général en effet on ne peut faire l'hypothèse qu'une opération est toujours applicable: selon l'état du système, il peut être nécessaire d'effectuer plusieurs opérations intermédiaires avant de pouvoir appliquer une opération donnée. La dernière phase consiste à explorer ce graphe en résolvant les contraintes associées pour générer les données de test effectives.

Ces travaux ont conduit au développement de l'outil d'aide à la génération de jeux de test CASTING [*] dont un prototype sera disponible en janvier 1998 [30]. La première version de CASTING prend en entrée des spécifications dans la notation AMN de la méthode B [Abr96] et fait appel à Ilog Solver[*] pour résoudre les contraintes engendrées.

Nous nous intéressons conjointement à la manière d'assurer que des programmes sont conformes à des hypothèses de test: cette assurance peut être obtenue par construction (contrôle a priori), ou par vérification (contrôle a posteriori) quand il s'agit de code existant. Nous considérons dans ce but des schémas de programmes qui peuvent être vus comme une manière de définir des classes de fautes: identifier un programme de manière non ambiguë dans une classe revient à assurer que toutes les fautes ayant pour effet de produire une version de programme dans cette classe seront détectées. Notre objectif actuel est d'associer à tout schéma de programme un type (ou contrainte) caractérisant les jeux de test discriminants pour cette classe.

Actions <<amont>>

Nous décrivons dans cette partie les travaux à plus long terme sur les langages fonctionnels (module [*]), Gamma Structuré, les types graphes et les architectures de logiciels (module [*]).

Implantation des langages fonctionnels

 

Taxonomie des implantations séquentielles



Participant : Pascal Fradet


Mots-clés : Compilation, Langage fonctionnel, Transformation de programme


Résumé : De nombreuses techniques ont été proposées pour implanter les langages fonctionnels et il est difficile d'établir des comparaisons rigoureuses entre les différentes options existantes. Nous avons proposé un cadre formel pour décrire et comparer les techniques de compilation des langages fonctionnels. Cela nous a permis d'établir une taxonomie des mises en oeuvre de langages fonctionnels et d'y faire figurer de nombreuses implantations classiques comme la SECD, la Cam, la G-machine, Tim, etc. Nous avons également étudié l'expression d'optimisations standards et la conception de machines hybrides (i.e. intégrant plusieurs choix de compilation).

De nombreuses techniques ont été proposées pour implanter les langages fonctionnels et il est difficile d'établir des comparaisons rigoureuses entre les différentes options existantes. Afin d'apporter des éléments de solution à ce problème difficile, nous avons proposé un cadre formel pour décrire et comparer les techniques de compilation des langages fonctionnels [12]. Nous avons repris l'idée centrale de nos précédents travaux [6] qui consiste à définir le processus de compilation comme une suite de transformations de programmes dans le cadre fonctionnel. Nous avons couvert une grande partie du domaine en considérant les mises en oeuvre de l'appel par valeur et l'appel par nécessité, qu'elles soient basées sur les environnements ou la réduction de graphe. Les principales tâches d'un compilateur sont la compilation du schéma d'évaluation, de la $\beta$-réduction et des transferts de contrôle (appels et retours de fonctions). Nous avons identifié pour chaque étape plusieurs choix fondamentaux et nous les avons décrits comme des transformations de programmes. La description dans un cadre unique a permis de mettre clairement en évidence les différences et les similitudes entre la réduction de graphe et les implantations à environnement.

Un des avantages de cette démarche est de décomposer et de structurer le processus de compilation. On peut, par exemple, montrer que des implantations apparemment très différentes partagent certaines options de mise en oeuvre. Nous avons établi une taxonomie des mises en oeuvre de langages fonctionnels et nous avons classifié de nombreuses implantations classiques comme la SECD, la Cam, la G-machine, Tim, etc. Nous avons également étudié l'expression d'optimisations classiques, la conception de machines hybrides (i.e. intégrant plusieurs choix de compilation) et proposé des comparaisons formelles sous la forme d'étude de complexité des transformations [12]. Ce travail a fait l'objet de la thèse de Rémi Douence, actuellement en séjour post-doctoral à l'université de Carnegie Mellon.

Langages restreints pour machines parallèles



Participants : Pascal Fradet , Julien Mallet , Mario Südholt


Mots-clés : Schéma de programme, Compilation, Parallélisme


Résumé : Nous étudions l'utilisation de langages fonctionnels restreints pour la programmation des machines massivement parallèles. La démarche que nous avons adoptée est celle des langages composés d'une collection de schémas de programmes (patrons) intrinsèquement parallèles (map, reduce, scan, etc). Nos travaux dans ce domaine concernent la description abstraite des distributions de données et le calcul précis des coûts de communication.

Le but des langages restreints pour la programmation des machines massivement parallèles est de concilier efficacité, portabilité et abstraction. La démarche que nous avons adoptée est celle des langages composés d'une collection de schémas de programmes (patrons) intrinsèquement parallèles (map, reduce, scan, etc). Les opérateurs parallèles de HPF ou des langages comme NESL peuvent être vus comme des exemples de ce style de programmation. Nous poursuivons deux directions de recherche:

Les types graphes

 

Notre réflexion sur l'introduction d'un système de types pour le langage Gamma nous a conduit à proposer une notion de type graphe correspondant à une classe de graphes d'une même forme. Ces graphes sont manipulés par des réactions qui extraient un sous-graphe selon des conditions locales et le remplacent par un nouveau sous-graphe. Nous avons conçu un algorithme de vérification qui permet d'assurer que le type est préservé par une réaction. Ces types peuvent alors être vus comme des invariants sur la forme des données.

Nous avons proposé une nouvelle version de Gamma, appelée Gamma Structuré, qui intègre les types graphes [14]. Il s'est avéré que les types graphes peuvent apporter des solutions à des problèmes très différents. Nous avons notamment étudié leur application pour la manipulation de structures de données avec partage dans les langages impératifs [19] ainsi que la description d'architectures de logiciels [23,33]. Nous détaillons séparément ces trois domaines d'application.

Gamma Structuré



Participants : Pascal Fradet , Daniel Le Métayer


Mots-clés : Réaction chimique, Multi-ensemble, Structure de données, Type graphe, Vérification de type, Invariant


Résumé : Nous avons proposé une version de Gamma qui intègre un moyen de définir des données structurées sans pour autant remettre en cause le modèle de calcul de base. Les types graphes permettent une description des structures de données de nature «topologique», sous forme de relations entre les valeurs du multi-ensemble. Ces types peuvent être vus comme des invariants sur les multi-ensembles, le point crucial étant que ces invariants peuvent être prouvés automatiquement. On obtient ainsi une vérification de types pour Gamma Structuré.

Le formalisme Gamma permet une description abstraite des programmes, dépourvue de contraintes d'ordonnancement inutiles. On peut illustrer le comportement des programmes Gamma à l'aide de la métaphore de la réaction chimique : l'exécution est une succession de réactions chimiques consommant les éléments d'un multi-ensemble pour produire de nouveaux éléments selon certaines règles.

Gamma Structuré intègre dans le langage un moyen de définir des données structurées sans pour autant remettre en cause le modèle de calcul de base. La difficulté vient du fait qu'on ne peut recourir à la méthode habituelle pour définir des structures de données (les types récursifs) car ceci induirait un style de programmation récursive (avec une manipulation globale des données) incompatible avec les principes de Gamma. Les types graphes permettent une description des structures de données de nature «topologique», sous forme de relations entre les valeurs du multi-ensemble [14]. Ces types peuvent être vus comme des invariants sur les multi-ensembles, le point crucial étant que ces invariants peuvent être prouvés automatiquement. On obtient ainsi une vérification de types pour Gamma Structuré. Il faut noter que ces types ne contraignent pas les valeurs du multi-ensemble, mais les relations qui les relient. Le bénéfice est double:

Shape-C



Participants : Pascal Fradet , Daniel Le Métayer , Florimond Ployette


Mots-clés : Type, Structure de donnée, Vérification, Pointeur, Sûreté


Résumé : Nous avons proposé une une extension de C intégrant la notion de type graphe. Elle permet de décrire précisément et de manipuler des structures classiques comme les listes circulaires, les listes avec sauts, les arbres binaires avec pointeur parent, etc. Une vérification de type permet d'assurer que la forme d'une structure spécifiée par un type graphe est un invariant du programme. Il devient ainsi possible de détecter de nombreuses erreurs de programmation, ce qui améliore la sûreté des manipulation de pointeurs.

De nombreuses erreurs de programmation ne sont pas détectées par les systèmes de types des langages impératifs. Leur manque d'expressivité les rend incapables, par exemple, de distinguer une liste doublement chaînée d'un arbre binaire. Nous avons proposé une solution à ce problème avec Shape-C, une extension de C intégrant la notion de type graphe (ou «shape type») [19]. On utilise en fait un sous-ensemble des types graphes de Gamma Structuré qui correspond aux structures de données manipulées dans les programmes impératifs. Il devient possible de décrire précisément et de manipuler des structures classiques comme les listes circulaires, les listes avec sauts, les arbres binaires avec pointeur parent, etc. Parmi les avantages de Shape-C on peut citer:

Un pré-processeur, traduisant des programmes Shape-C en C pur après vérification de types, a été réalisé. Il a notamment permis de déterminer plusieurs extensions souhaitables pour faciliter la programmation en Shape-C. Nous travaillons actuellement sur ce point.

Architectures de logiciels



Participants : Anne Alexandra Holzbacher , Daniel Le Métayer , Michaël Périn , Mario Südholt


  Mots-clés : Architecture de logiciel, Coordination, Communication, Style, Vérification


Résumé : Nous avons proposé une manière de spécifier des architectures en terme de graphes. La description d'une application est séparée en deux niveaux bien identifiés: d'une part, l'ensemble de ses entités de base qui représentent des calculs autonomes; d'autre part la coordination de ces entités. Les entités individuelles peuvent être décrites dans des langages traditionnels (par exemple séquentiels) et la coordination est assurée par un composant défini séparément: le coordinateur.

Les principes de base de notre contribution dans le domaine des architectures de logiciels sont les suivants [8]:

L'avantage de cette démarche est de concilier une vision dynamique de l'architecture avec des possibilités de vérification statique.

Ce cadre a été appliqué à un cas d'étude proposé par la société Signaal (Pays-Bas) qui consistait à modéliser un système de contrôle ferroviaire [23,33]. L'architecture ainsi que l'évolution dynamique du système ont pu être décris à l'aide des types graphes. Il a été notamment possible d'assurer statiquement que l'évolution du système respectait les invariants exprimés par la grammaire de graphes. Une implantation en ConCoord a ensuite été dérivée de cette spécification formelle.



Notes:

...
Computer Assisted Software Testing
...
Ilog Solver est une marque déposée par Ilog.


previous up next contents Précédent : Logiciels Remonter : Projet LANDE, Conception et validation Suivant : Actions industrielles