Développement des systèmes informatiques


Méthode de traces


Qu’est-ce qu’une trace ?

Pourquoi les traces sont-elles importantes ?

Quand est-ce que deux traces sont équivalentes ?

Qu’est-ce qu’une trace canonique ?

Exemple

Module pile avec les fonctions : PUSH(int I), POP(), TOP()

pile

Exemple

Module int avec les fonctions : succ(), pred()

pile

Pourquoi les traces canoniques sont-elles importantes ?

Extensions d’une trace canonique par un événement

Il n’y a pas d’autres possibilités !

La fonction d'extension

Soit extP fonction d’extension pour le programme P

extP : traces canoniques x appels de P → traces canoniques

Exemple :

extPUSH(PUSH(2).PUSH(5), PUSH(3)) = PUSH(2).PUSH(5).PUSH(3)

PUSH(2).PUSH(5) représente l'état "avant", PUSH(3) représente un appel de PUSH (une extension de l'état "avant") et PUSH(2).PUSH(5).PUSH(3) représente le nouvel état.

Comment calculer la trace canonique équivalente à une trace donnée ?

Considérons la trace T égale à P1(...).P2(...). ... . Pn(...)

La trace canonique équivalente à T est définie à l’aide de la fonction red (réduction).

red(T) = extPn( red(P1(…).P2(…). … . Pn-1(…) ), Pn(…)) si n > 1

red(T) = extP1( _ , P1(…)) si la trace vide est canonique

red(T) = extP1( _c , P1(…)) si la trace vide n’est pas canonique et _c représente la trace canonique équivalente à la trace vide

Légalité

Trois catégories de jetons :

La légalité est définie pour les appels.

PUSH(4).POP().POP().PUSH(2).TOP().PUSH(6)

Le deuxième appel de POP() est illégal mais la trace entière est équivalente à PUSH(2).PUSH(6).

Notation

Ci-dessous x et y sont des noms d’objet, T, T1, T2 sont des traces et S est une portion d’une trace (une sous-chaîne de caractères).

symbolisme formelsignification
_ (soulignement) trace vide
. séparateur entre des éléments dans une représentation écrite de traces
[S]i=m..n portion Z d’une trace qui est obtenue comme suit : dénotons par Sα la chaîne S dans laquelle toutes les occurrences de i ont été remplacées par α;
Z = _    si m > n ou
Z = Sm.Sm+1. ... .Sn    si m ≤ n
(x,T1) ≡ (y,T2) équivalence de traces (définie par la fonction d’extension)
T1 ≡ T2 ∀ x,y; (x,T1) ≡ (y,T2)
* symbole générique utilisé dans des traces canoniques
P(...)↓ la valeur retournée par l’invocation P(...)
P(..., a↓, ...) la valeur retournée dans l’argument de sortie a
P(...)↓v v est la valeur retournée par l’invocation P(...)
P(..., a↓b, ...) b est la valeur retournée dans l’argument de sortie a
<M> l'ensemble des traces canoniques dans le module M
<<M>> l'ensemble de toutes les traces dans le module M

Exemples de traces

Si une trace contient des appels de programmes non-déterministes, des valeurs de sortie apparaîtront dans cette trace.

Quelles informations trouverons-nous dans une spécification de module ?

Exemple

Nom de programmeArg#1Arg#2Type de résultat
Pm:VOint:V
Qm:O
Rm:Vint

Remarque :

Les lettres V et O après le type d'argument (m ou int) décrivent le mode de passage d'argument :

Nous devons spécifier

Plus formellement, une spécification d’un module contient :

Propriétés de spécifications

Quand une spécification est-elle complète ?

Quand une spécification est-elle consistante ?

Comment traitons-nous le non-déterminisme ?

Objets nommés

Symbole générique ("wild-card character") '*'

Fonction d'extension (complément)

Comment interpréter la notation suivante ?
    PUSH((n,T)↓, a) = T.PUSH(*,a)

Un état du module dont les objets sont dénotés par les noms n1,n2,...,nn peut être présenté par une séquence
    (n1,val1), (n2,val2), ..., (nn,valn)

L’effet de l’invocation PUSH(n,a) peut être illustré comme suit :
fonction d'extension

Exemples de spécifications

Spécification de l’interface du module Pile

(1) SYNTAXE

PROGRAMMES D'ACCÈS

Nom de programmeArg#1Arg#2Type de résultat
PUSHpile:VOint:V
POPpile:VO
TOPpile:Vint

(2) TRACES CANONIQUES

canonique(T) ≡ ∃n:int, a1,...,an:int; T = [PUSH(*, ai)]i=1..n

(3) ÉQUIVALENCES

légalité(PUSH((n,T), a)) = %legal%

PUSH((n,T)↓, a) = T.PUSH(*, a)

légalité(POP((n,T))) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

POP((n,T)↓) = T1 where T1:<pile>; a:int; (T = T1.PUSH(*,a))

légalité(TOP(T)) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

TOP(T)↓ = a where T1:<pile>; a:int; (T = T1.PUSH(*, a))

Exercice : réduisez la trace ci-dessous à une trace canonique.

PUSH(n,5).POP(n).TOP(n).PUSH(n,3).PUSH(n,5).PUSH(n,11).POP(n).PUSH(n,7).POP(n)

Une autre spécification de l’interface du module Pile

(1) SYNTAXE

PROGRAMMES D'ACCÈS

Nom de programmeArg#1Arg#2Type de résultat
CREATESTACKpile:O
PUSHpile:VOint:V
POPpile:VO
TOPpile:Vint

(2) TRACES CANONIQUES

canonique(T) ≡ T = _ ∨ ∃n:int, a1,...,an:int; T = CREATESTACK(*).[PUSH(*, ai)]i=1..n

(3) ÉQUIVALENCES

légalité(CREATESTACK(n)) = %legal%

CREATESTACK(n↓) = CREATESTACK(*)

légalité(PUSH((n,T), a)) =

ConditionValeur
T = _%pile_invalide%
T ≠ _%legal%

PUSH((n,T)↓, a) = T.PUSH(*, a)

légalité(POP((n,T))) =

ConditionValeur
T = _%pile_invalide%
length(T) = 1%vide%
length(T) > 1%legal%

POP((n,T)↓) = T1 where T1:<pile>; a:int; (T = T1.PUSH(*,a))

légalité(TOP(T)) =

ConditionValeur
T = _%pile_invalide%
length(T) = 1%vide%
length(T) > 1%legal%

TOP(T)↓ = a where T1:<pile>; a:int; (T = T1.PUSH(*, a))

Spécification de l’interface du module File12

Introduction informelle

Le module implémente une file d’entiers dont la capacité ≤ 12.

(1) SYNTAXE

PROGRAMMES D'ACCÈS

Nom de programmeArg#1Arg#2Type de résultat
ADDfile12:VOint:V
REMOVEfile12:VO
FRONTfile12:Vint

(2) TRACES CANONIQUES

canonique(T) ≡ ∃n:int, a1,...,an:int; (T = [ADD(*, ai)]i=1..n ∧ 0 ≤ n ≤ 12)

(3) ÉQUIVALENCES

légalité(ADD((n,T), a)) =

ConditionValeur
length(T) = 12%pleine%
length(T) < 12%legal%

ADD((n,T)↓, a) = T.ADD(*, a)

légalité(REMOVE((n,T))) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

REMOVE((n,T)↓) = T1 where T1:<file12>; a:int; (T = ADD(*,a).T1)

légalité(FRONT(T)) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

FRONT(T)↓ = a where T1:<file12>; a:int; (T = ADD(*, a).T1)

Spécification de l’interface du module Pile(taille)

(1) SYNTAXE

PROGRAMMES D'ACCÈS

Nom de programmeArg#1Arg#2Type de résultat
PUSHpile:VOint:V
POPpile:VO
TOPpile:Vint

(2) TRACES CANONIQUES

canonique(T) ≡ ∃n:int, a1,...,an:int; (T = [PUSH(*, ai)]i=1..n ∧ n ≤ taille)

(3) ÉQUIVALENCES

légalité(PUSH((n,T), a)) = %legal%

PUSH((n,T)↓, a) =

ConditionValeur
length(T) < tailleT.PUSH(*, a)
length(T) = tailleT1.PUSH(*, a) where
T1:<pile> b:int; (T = PUSH(*, b).T1)

légalité(POP((n,T))) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

POP((n,T)↓) = T1 where T1:<pile>; a:int; (T = T1.PUSH(*,a))

légalité(TOP(T)) =

ConditionValeur
T = _%vide%
T ≠ _%legal%

TOP(T)↓ = a mod 255 where T1:<pile>; a:int; (T = T1.PUSH(*, a))

Spécification de l’interface du module Producteur d'entiers

(0) Caractéristiques

type specifié: intg

(1) SYNTAXE

PROGRAMMES D'ACCÈS

Nom de programmeArg#1Type de résultat
GETINTintg:VOint:R

Commentaire : 'R' est utilisé pour indiquer une valeur non-déterministe.

(2) TRACES CANONIQUES

canonique(T) ≡ ∃n:int, x1,...,xn:int; (T = [GETINT(*)↓xi)]i=1..n ∧ ∀i:int; (1 ≤ i ≤ n ⇒ xi < xi+1))

(3) ÉQUIVALENCES

légalité(GETINT((n,T))) = %legal%

GETINT((n,T)↓)↓v = S1.GETINT(*)↓v.S2 where S1,S2:<intg> (T = S1.S2 ∧ canonique(S1.GETINT(*)↓v.S2))

GETINT((n,T))↓a  |  ¬∃S1,S2:<intg> [T = S1.GETINT(*)↓a.S2]

Traces utilisées comme valeurs