Projet Pampa

previous up next contents
Précédent : Présentation générale et objectifs Remonter : Projet PAMPA, Modèles et outils Suivant : Grands domaines d'application



Fondements scientifiques

Panorama

Résumé : le projet élabore de nouvelles technologies logicielles permettant d'aider le développement des logiciels répartis. Les problèmes centraux sont la modélisation des processus et comportements, et le développement d'algorithmes associés pour raffiner la conception, générer du code ou des tests. Ces questions sont examinées dans le cadre des architectures logicielles (à objets répartis). Les techniques de validation utilisées s'appuient sur des simulations complexes des modèles considérés.


Logiciel réparti: désigne un programme informatique dont l'exécution met en jeu un ensemble de calculateurs travaillant en réseau. Nous considérons généralement que l'interaction entre ces calculateurs est asynchrone et s'effectue par échange de messages. Asynchrone signifie qu'un message peut rester en transit un temps non déterminé, découplant ainsi fortement l'activité des processus s'exécutant sur ces calculateurs. En général ce type de logiciel est aussi réactif dans le sens où chacun des processus doit réagir aux sollicitations de son environnement et émettre des réponses à ces sollicitations.

Fondements mathématiques des systèmes réactifs et répartis

 

Mots-clés : systèmes de transitions étiquetés, ensembles partiellement ordonnés


Résumé : La structure mathématique qui caractérise le mieux les fondements des travaux de recherche en vérification et génération de programmes répartis sont les systèmes de transitions étiquetés (labelled transition systems en anglais, abréviation LTS) [Arn92]. Cette structure, développée il y a près de cinquante ans est l'un des fondements de l'informatique ; aussi il nous a paru utile de préciser de quelle façon nous utilisons cette structure, notamment sa construction au vol. L'autre aspect fondamental est la notion de causalité entre événements dans les exécutions réparties. C'est le concept central qui permet de parler de l'analyse des comportements des systèmes distribués [Jar94]. Il est aussi à la base des plus beaux résultats en algorithmique répartie.


Un LTS est un graphe orienté dont les arêtes, appelées transitions, sont étiquetées par une lettre prise dans un alphabet d'événements. Les sommets de ce graphe sont appelés états.

$M=(Q^{M},A,T^{M}\subset Q^{M}\times A\times Q^{M},q_{init}^{M})$

Avec : $Q^{M}$ ensemble des états, $q_{init}^{M}$ l'état initial, $A$l'ensemble des événements, $T^{M}$ la relation de transition.

Il est usuel de parler d'automate d'états finis pour désigner un système de transitions étiqueté dont l'ensemble des états et celui des événements sont finis. Il s'agit en fait du modèle de machine le plus simple que l'on puisse imaginer. Nous employons les LTS pour modéliser des systèmes réactifs le plus souvent répartis. Dans ce cadre les événements représentent les interactions (entrées ou sorties) du système avec son environnement. On parle alors de système de transitions entrées-sorties ou de IOLTS (input-output LTS).

Ces systèmes de transitions sont obtenus à partir de spécifications de systèmes réactifs répartis décrits dans des langages de haut niveau comme LDS ou LOTOS. L'association d'un LTS à un programme se fait par l'intermédiaire d'une définition opérationnelle de la sémantique du langage et est en général formalisée sous la forme d'un système de déductions. Pour un langage aussi simple qu'une algèbre de processus (CCS par exemple), la définition de sa sémantique opérationnelle tient en moins de dix axiomes et règles d'inférences ; alors que pour un langage aussi complexe que LDS, cela est plutôt l'affaire d'un document de plus de cent pages.

Pour des raisons de performance, ces sémantiques opérationnelles ne sont jamais mises en oeuvre directement ; mais font l'objet de transformations diverses. En particulier, la compacité du codage des états est un facteur déterminant de l'efficacité de la génération des LTS.

Les calculs et transformations opérés sur les LTS se résument à des parcours et calculs de points fixes sur les graphes. L'originalité réside dans la façon de les effectuer : par calcul explicite du LTS ou bien implicitement, sans calcul ou stockage exhaustif du LTS.

Les algorithmes classiques de théorie des langages construisent explicitement des automates d'états finis. Ils sont le plus souvent intégralement stockés en mémoire. Cependant, pour les problèmes qui nous intéressent, la construction (ou la mémorisation) exhaustive des LTS n'est pas toujours nécessaire. Une construction partielle suffit et des stratégies analogues aux évaluations paresseuses des programmes fonctionnels peuvent être employées : seule la partie nécessaire à l'algorithme est calculée.

Dans le même esprit il est possible d'oublier certaines parties précédemment calculées du LTS ; et par recyclage judicieux de la mémoire, il est possible d'économiser l'espace mémoire utilisé par nos algorithmes.

La combinaison de ces stratégies de calcul sur des LTS implicites permet de traiter des systèmes de taille réelle même en utilisant des moyens de calcul tout à fait ordinaires.

Ensembles Partiellement Ordonnés

On considère qu'une exécution répartie sur un réseau de processus $I$ est faite d'événements atomiques $E$, certains étant observables, d'autre ne l'étant pas. Chaque événement est l'occurrence d'une action ou opération (Notons $\Sigma$ l'alphabet des actions) ; on considère habituellement qu'une action a lieu sur un seul et même processus du réseau. Nous avons donc :

\begin{displaymath}\left\{ \begin{array}{lr}I & \; \mbox{ensemble fini de pro... ...\Sigma & \; \mbox{étiquetage des événements}\end{array} \right.\end{displaymath}

La relation de causalité décrit le plus petit ordonnancement partiel sur les événements que l'on peut déduire du modèle de fonctionnement du réseau de processus que l'on s'est donné. Il a été présenté sous cette forme pour la première fois dans [Lam78] avec comme hypothèses sur le fonctionnement de l'architecture répartie :

1.
Les processus sont séquentiels. Deux événements ayant eu lieu sur le même processus sont ordonnés.
2.
Les communications sont asynchrones et points à points. L'émission d'un message précède causalement sa réception.

Ces deux axiomes nous permettent de définir une relation d'ordre $\leq$ sur $E$ : c'est la relation de causalité.

Tout ce que l'on peut dire est que cet ordonnancement aurait respecté l'ordonnancement causal ; autrement dit, le comportement réel du système est une extension linéaire de l'ordre de causalité.

Il n'est cependant ni réaliste ni même utile de chercher à savoir quelle extension linéaire s'est réellement produite. Cela n'est pas réaliste car les architectures réparties existantes n'offrent pas les moyens de synchroniser des horloges locales à chaque processus avec une précision suffisante. Cela n'est pas utile car cet ordonnancement dépend de conditions d'exécution qui ne sont pas contrôlables ou répétables. Il faut donc considérer que toute extension linéaire de la relation de causalité est un ordonnancement plausible.

La difficulté est dans la combinatoire en général exponentielle dans le nombre d'événements des extensions linéaires d'une relation d'ordre. Il existe cependant une structure intéressante pour représenter l'ensemble les états dans lequel le système a pu se trouver : c'est le treillis des antichaînes de la relation d'ordre [DP90]. En terme d'exécution répartie ceci correspond à désigner pour chaque processus quel a été le dernier événement qui s'est produit ; cela définit sans ambiguïté un état possible du système. Ce treillis (distributif) peut être représenté sous la forme d'un LTS, avec comme relation de transition, la relation de couverture.

Génération automatique de tests

 

Mots-clés : Test de conformité, spécification, implantation sous test (IUT), cas de test, objectif de test, point de contrôle et d'observation (PCO), système de transitions à entrées sorties (IOLTS)


Spécification: Une description du comportement attendu du système à tester. Dans le cadre de la génération automatique de tests, la spécification est supposée donnée dans un langage formel (LDS, LOTOS, Estelle ou autre). Son comportement donné par la sémantique opérationnelle du langage est décrit par un système de transitions.

Implantation sous test (IUT): L'implantation réelle du système à tester. On fait l'hypothèse que le comportement de l'IUT est modélisable par un système de transition.

Cas de test: Décrit un ensemble d'interactions entre le testeur et l'implantation sous test, des opérations sur des temporisateurs de test, et des verdicts.

Objectif de test: Description abstraite d'un aspect du système à tester.

Point de contrôle et d'observation (PCO): Interface du système par lequel le testeur peut interagir avec l'IUT.

Relation de conformité: Relation entre spécifications et modèles d'implantations qui caractérise les implantations correctes.

Résumé : Le test de conformité est un test de type boîte noire. On se donne une spécification d'un système ouvert qui sert de modèle de référence et une implémentation réelle de ce système dont on ne connait le comportement que par ses interactions avec l'environnement. Un test consiste à stimuler l'IUT par des événements émis depuis l'environnement par un testeur, à observer les réactions de l'IUT, et à en déduire la correction ou non de l'IUT par rapport à sa spécification en fonction d'une relation de conformité. En pratique le test de conformité ne pouvant être exhaustif, les tests permettent de montrer la présence d'erreurs (non-conformité) mais ne permettent jamais de montrer leur absence (conformité). La génération automatique de test consiste à produire automatiquement des cas de tests en fonction de la spécification et de la relation de conformité choisie. La sélection d'un ensemble significatif de cas de tests peut se faire en utilisant des objectifs de test. C'est l'approche suivie actuellement lors de l'écriture manuelle des tests.


Système de transitions à entrées sorties

Le modèle de base permettant la modélisation des objets relatifs au test de conformité est un modèle dérivé des systèmes de transition, appelé système de transitions à entrée sortie (IOLTS). Il distingue explicitement les entrées des sorties, permettant ainsi de modéliser le contrôle et l'observation.

Un IOLTS est un système de transitions où l'alphabet est décomposé en $A= \{?\} \times A_I \cup \{!\}\times A_O \cup \{\tau\}$, $A_I$ l'alphabet d'entrées, $A_O$ l'alphabet de sorties, et $\tau$ est une action interne.

Spécification, implantation et objectifs de test

Le comportement de la spécification est décrit par un IOLTS $S$.Le test de conformité permettant de considérer uniquement des traces d'exécutions visibles aux PCOs, le comportement observable de la spécification est obtenu par abstraction des actions internes et déterminisation, éventuellement minimisation. C'est aussi un IOLTS $S_{vis}$.

L'implantation n'est connue que par ses interactions avec l'environnement. Mais on suppose que le comportement de l'IUT peut être vu comme un IOLTS I qui ne peut refuser aucune entrée. Cette hypothèse de base est nécessaire pour permettre de raisonner sur la conformité des implantations.

Un objectif de test sert à sélectionner des cas de test par rapport aux comportements décrits dans la spécification. Dans les tests manuels, il décrit informellement une propriété attendue de l'implantation. Dans le cadre formel, il est décrit par un IOLTS $TP$ muni d'un ensemble d'états accepteurs, lui donnant ainsi la structure d'automate.

Cas de test et conformité

Un cas de test décrit des comportements du testeur. Il sera modélisé par une extension d'IOLTS $TC$ dont les entrées/sorties sont images miroir d'actions de la spécification. Certaines actions peuvent porter sur des temporisateurs (armement, désarmement, échéance) et/ou contenir des verdicts PASS, (PASS), FAIL et INCONCLUSIVE.

Plusieurs relations de conformité peuvent être choisies. Ce choix dépend du niveau de contrôle et d'observation que l'on peut espérer avoir sur l'IUT ainsi que des différences de comportement que l'on tolère entre la spécification et l'IUT. Il paraît raisonnable de penser que l'on peut observer des traces et des blocages et de tolérer que l'IUT permette des comportements non prévus dans la spécification à condition qu'ils diffèrent à partir d'une entrée de l'IUT (la spécification est partielle). La relation de conformité ioconf choisie dit donc que l'IUT $I$est conforme à la spécification $S$ si après toute trace de la spécification, les sorties possibles de l'IUT sont incluses dans celles de la spécification et l'IUT se bloque si et seulement si la spécification le peut également.

Algorithmique

L'algorithme de génération de test [FJJV96] est adapté d'algorithmes connus issus du domaine de la vérification, en particulier les algorithmes de vérification à la volée qui permettent de vérifier des propriétés pendant la construction du système de transitions de la spécification. L'algorithme parcourt un produit synchrone entre l'objectif $TP$et l'IOLTS $S_{vis}$ représentant le comportement visible de la spécification. Le cas de test $TC$ muni de ses verdicts est synthétisé pendant le parcours. Il correspond à l'image miroir d'un sous-graphe de $S_{vis}$composé de séquences acceptées par $TP$. Il est contrôlable au sens où il ne permet aucun choix entre une sortie et une entrée ou une sortie. Les opérations sur les timers sont ajoutées par un parcours ultérieur de $TC$.

Afin d'éviter le problème d'explosion combinatoire lors du calcul de $S$, l'algorithme précédent peut être utilisé à la volée, cf. module [*].

L'algorithme de génération de test prend en entrée un objectif $TP$ et une spécification dont le comportement est décrit explicitement ou implicitement (génération à la volée) par $S$, et produit en sortie un cas de test $TC$.On peut montrer que les cas de tests produits sont valides (sans biais) i.e. si l'application de $TC$ sur une implantation $I$produit un verdict FAIL alors l'implantation $I$ est non conforme à $S$ pour la relation ioconf. Du fait du non-déterminisme et de la sélection des tests, il est impossible d'avoir la propriété inverse qui garantirait l'exhaustivité. Par contre, on peut montrer que si une IUT $I$ est non conforme, il existe un objectif $TP$ tel que l'algorithme de génération peut produire un cas de test $TC$ qui peut produire un verdict FAIL. En d'autres termes, moyennant une hypothèse d'équité sur les comportements de l'IUT, l'ensemble (infini) des tests que peut produire l'algorithme est exhaustif pour la relation ioconf.

Schéma d'exécution distribuée et technologie objet dans un contexte de génie logiciel

  Mots-clés : objets, composants logiciels, motifs de conception, frameworks


L'approche objet pour le génie logiciel

L'approche objet est aujourd'hui devenue incontournable pour l'analyse, la conception et la réalisation des grands systèmes d'information devant évoluer sur de longues périodes de temps, et pour lesquels l'effort principal en termes de logiciel (parfois jusqu'à 80% ou plus) est consacré à la maintenance [Mey88]. Fondée sur la notion d'objets, c'est à dire d'instances de classes modélisant les entités stables d'un système d'information comme des modules autonomes organisés selon des relations d'héritage (classification) et d'utilisation, cette approche permet en effet de gérer la nature fondamentalement incrémentale, itérative et évolutive du développement de tels logiciels [Jac85,Boo94]. Les diverses phases d'analyse, de conception et de réalisation utilisent le même cadre conceptuel (fondé sur cette notion d'objet) et n'ont pas de frontières rigides entre elles, ce qui fait que le processus de développement objet est parfois qualifié de continu [Jéz96].

Cette notion d'objet fourni le substrat nécessaire au développement du concept de composant logiciel: l'encapsulation et le masquage d'information permettent d'établir une analogie avec les composants matériels, avec en plus la notion d'adaptabilité apportée par l'héritage qui permet de les spécialiser souplement. L'idée maîtresse est ici de réaliser des économies d'échelle dans le développement de logiciels en réutilisant des composants plutôt qu'en redéveloppant les applications à partir de zéro. Ceci a un impact majeur sur le cycle de vie du logiciel, qui doit maintenant intégrer des activités de :

Mais pour espérer faire de réelles économies d'échelle dans le développement de logiciels, il faut aller au delà de la réutilisation de composants dits techniques (e.g. structures de données générales, objets d'interfaces graphiques, etc.) et réutiliser aussi des composants «métiers», ce qui pose des problèmes difficiles d'intégration et de validation, comme en témoigne le crash du vol Ariane 501 [18].

Approche par objets, parallélisme et répartition

Si la construction de systèmes complexes à l'aide des technologies objets commence à être assez bien maîtrisée, en revanche le bât blesse encore pour tout ce qui touche au parallélisme et à la répartition. Il paraît en particulier difficile d'apporter une solution universelle prenant en compte tous les aspects possibles de leur programmation. Ne serait-ce qu'en termes de sémantique de la communication entre processus (rendez-vous, files, RPC, hypothèses de fiabilité, d'ordonnancement, d'isochronisme, etc.) quel que soit le choix effectué, il serait forcément fermé, donc trop limitatif pour certaines applications (comment exprimer que je n'ai que faire de perdre des messages pour certains flots multi-média?), et par essence inapproprié pour des systèmes ouverts et évolutifs.

Mais un peu comme on a sorti les structures de données des languages de programmation modernes pour les ranger dans des bibliothèques ouvertes aux modifications et améliorations, il paraît prometteur de définir des modèles spécifiques à des domaines d'applications particuliers, comme par exemple le modèle d'exécution SPMD associé à la distribution de données pour le calcul scientifique intensif, ou le modèle en couches de protocoles pour les systèmes de télécommunications; et de fournir des cadres de conception, de réalisation et de validation adaptés à ces modèles : c'est la notion de «framework».

Un framework fournit un ensemble intégré de fonctionnalités spécifiques à un domaine, implanté par une collection de classes liées entre elles par de multiples schémas (patterns) de collaboration statiques et dynamiques. Il fournit un modèle d'interaction entre les différents objets instances des classes définies (ou seulement spécifiées pour les classes abstraites) dans le framework. Celui-ci présente en général une inversion du contrôle à l'exécution : alors qu'une application utilisant une bibliothèque s'appuie sur celle-ci, dans le cas d'une application utilisant un framework, c'est le framework qui effectue l'essentiel du travail et appelle «de temps en temps» un composant spécifique réalisé par l'implanteur de l'application. Un framework peut donc être vu comme une application semi-complète. Des applications complètes sont développées en héritant et en instantiant des composants paramétrés de frameworks. Il suffit donc en quelque sorte d'enficher dans un framework les composants spécifiques de son application pour obtenir une application complète.

Dans un contexte de programmation par objets, on s'appuie sur le mécanisme de la liaison dynamique pour dissocier la spécification d'une opération (donnée dans une classe du framework) de son implantation dans une sous-classe, qui fait partie du code applicatif fourni par l'utilisateur du framework.



previous up next contents Précédent : Présentation générale et objectifs Remonter : Projet PAMPA, Modèles et outils Suivant : Grands domaines d'application