ΤΕΧΝΟΛΟΓΙΑ ΛΟΓΙΣΜΙΚΟΥ http://coursessoftlabntuagr/softeng/ ιδάσκοντες: (nickie@softlabntuagr) Γιάννης Μαΐστρος (maistros@csntuagr) Βασίλης Βεσκούκης (bxb@softlabntuagr) Τυπικές Μέθοδοι στην Ανάπτυξη Συστηµάτων Λογισµικού Τυπικές µέθοδοι: υπέρ και κατά Software Engineering Όλες οι (άλλες) επιστήµες µηχανικών βασίζονται σε σταθερό θεωρητικό (µαθηµατικό) υπόβαθρο Θεωρητικό υπόβαθρο για την ΤΛ Επιστήµη υπολογιστών Επιστηµονικοί κλάδοι που αντιστοιχούν σε συγκεκριµένες περιοχές εφαρµογών, πχ µαθηµατικά, φυσική, στατική, αρχιτεκτονική 1 2 Μειονεκτήµατα της παραδοσιακής διαδικασίας ανάπτυξης λογισµικού Χρειάζεται µεγάλη προσπάθεια ώστε οι προδιαγραφές να µην είναι: διφορούµενες αφηρηµένες αντιφατικές ελλιπείς Ο έλεγχος κοστίζει και παρέχει µικρό βαθµό βεβαιότητας (~10 4 ) 114,000 χρόνια για 10 9! 3 Τυπικές µέθοδοι formal methods τεχνικές για την περιγραφή των ιδιοτήτων ενός συστήµατος, βασισµένες στα µαθηµατικά χρησιµοποιούνται για την προδιαγραφή, ανάπτυξη και επαλήθευση συστηµάτων µε συστηµατικό τρόπο η µαθηµατική βάση συνοδεύεται συνήθως από µια γλώσσα τυπικών προδιαγραφών και ορίζει έννοιες όπως: συνέπεια, πληρότητα προδιαγραφή, υλοποίηση, ορθότητα 4 Τυπικές µέθοδοι: υπέρ και κατά Τυπικές µέθοδοι: υπέρ και κατά Γιατί τίθεται έτσι αυτό το ζήτηµα; Πλεονεκτήµατα καλύτερη κατανόηση των απαιτήσεων και της σχεδίασης συστηµάτων λογισµικού χειρισµός προδιαγραφών ως µαθηµατικών αντικειµένων, πχ επαλήθευση (verification) αντί επικύρωσης (validation) αυτόµατη επεξεργασία προδιαγραφών και υποστήριξη από εργαλεία µείωση κόστους για έλεγχο και συντήρηση Μειονεκτήµατα αύξηση κόστους για ανάλυση και σχεδίαση έλλειψη σχετικής εκπαίδευσης ή/και απειρία των µηχανικών λογισµικού αδυναµία ή εγγενής δυσκολία των τυπικών µεθόδων να προδιαγράψουν ορισµένα είδη συστηµάτων λογισµικού έλλειψη καλών εργαλείων υποστήριξης Αποτέλεσµα αδράνεια και σκεπτικισµός ή ακόµα και άρνηση από την βιοµηχανία λογισµικού 5 6 1
Τυπικές µέθοδοι: υπέρ και κατά Μύθοι σχετικοί µε τις τυπικές µεθόδους µε τις ΤΜ παράγεται τέλειο λογισµικό ΤΜ = απόδειξη ορθότητας προγραµµάτων λόγω κόστους, οι ΤΜ έχουν νόηµα µόνο για συστήµατα λογισµικού υψηλής ασφάλειας οι ΤΜ αυξάνουν το συνολικό κόστος ανάπτυξης συστηµάτων λογισµικού οι πελάτες δεν µπορούν να καταλάβουν τις τυπικές προδιαγραφές οι ΤΜ έχουν χρησιµοποιηθεί µόνο για τετριµµένα ή πολύ απλά προβλήµατα [Hall 1990] Εργαλεία (ηµι-)αυτόµατης απόδειξης ACL2 Coq HOL IMPS Isabelle Nuprl PVS 7 8 Μέθοδοι, φορµαλισµοί και εργαλεία ASM: Abstract State Machines B-Method CSP: Communicating Sequential Processes DC: Duration Calculus ITL: Interval Temporal Logic Esterel Larch LOTOS Obj / CafeOBJ Μέθοδοι, φορµαλισµοί και εργαλεία (συνέχεια) Petri Nets Pi-Calculus RAISE: (Rigorous Approach to Industrial Software Engineering) SDL: Specification and Description Language TLA: Temporal Logic of Actions VDM: Vienna Development Method Z Notation 9 10 Πρόβληµα: αµοιβαίος αποκλεισµός σε περιβάλλον παράλληλης εκτέλεσης Μία λύση: the Bakery Algorithm υπάρχει ένα µηχάνηµα που εκδίδει αριθµούς προτεραιότητας κατά αύξουσα σειρά κάθε πελάτης που θέλει να εξυπηρετηθεί παίρνει αριθµό προτεραιότητας κάθε φορά εξυπηρετείται ο πελάτης µε το µικρότερο αριθµό προτεραιότητας Μία άτυπη περιγραφή της λύσης Κάθε πελάτης έχει µια αριθµητική µεταβλητή Αρχικά, η µεταβλητή του έχει την τιµή µηδέν Κάθε φορά που θέλει να εξυπηρετηθεί, δίνει στη µεταβλητή του µια τιµή µεγαλύτερη από τις µεταβλητές όλων των άλλων πελατών Στη συνέχεια περιµένει έως ότου η τιµή της µεταβλητής του γίνει µικρότερη από τις µεταβλητές όλων των άλλων πελατών Τότε εξυπηρετείται και στη συνέχεια ξαναδίνει στη µεταβλητή του την τιµή µηδέν 11 12 2
(iv) Μία υλοποίηση σε Java class Client extends Thread static List allclients = new LinkedList(); int ticket; public Client () ticket = 0; allclientsadd(this); void getserved (); void eatbread (); static int maxticket () int result = 0; for (Iterator i = allclientsiterator(); ihasnext();) Process p = (Process) inext(); if (pticket > 0 && pticket > result)) result = pticket; return result; 13 14 (v) (vi) static int minticket () int result = 0; for (Iterator i = clientsiterator(); ihasnext();) Process p = (Process) inext(); if (pticket > 0 && (result == 0 pticket < result)) result = pticket; return result; public void run () for (;;) synchronized (getclass()) ticket = maxticket() + 1; while (ticket > minticket()); // trying getserved(); // critical ticket = 0; eatbread(); // idle 15 16 Απαιτήσεις Ασφάλεια (safety): κάθε στιγµή εξυπηρετείται το πολύ ένας πελάτης Ζωντάνια (liveness): κάθε πελάτης κάποια στιγµή θα εξυπηρετηθεί Ανάλυση ενός µοντέλου για τη λύση Πόση πληροφορία θα περιέχει το µοντέλο; Υπερβολικά πολλή πληροφορία: η ανάλυση µπορεί να µην είναι εφικτή Υπερβολικά λίγη πληροφορία: η ανάλυση µπορεί να µην είναι ακριβής (vii) Παραδοχές (viii) Εστιαζόµαστε σε µια αφηρηµένη υλοποίηση, σε ένα ιδανικό περιβάλλον εκτέλεσης Αν και η λύση είναι κατάλληλη για n 1 πελάτες, εξετάζουµε την περίπτωση n = 2 Καταστάσεις σ = (p 1, p 2, t 1, t 2 ) S Για κάθε πελάτη, i 1, 2 phase p i idle, trying, critical ticket t i N Αρχική κατάσταση σ 0 : p 1 = p 2 = idle, t 1 = t 2 = 0 17 18 3
(ix) (x) Μετάβαση Step S S όταν (σ, σ') Step γράφουµε Step(σ, σ') (για i =1, οµοίως για i =2) αν p 1 = idle τότε p 1 ' =trying και t 1 ' = t 2 +1 αν p 1 = trying και t 2 = 0 ή t 1 < t 2 αν p 1 = critical τότε p 1 ' =idle και t 1 ' = 0 Ασφάλεια Safe S όταν σ Safe γράφουµε Safe(σ) (p 1 = critical p 2 = critical) Θεώρηµα ασφάλειας Safe(σ 0 ) Safe(σ) Step(σ, σ') Safe(σ') η ιδιότητα Safe είναι αναλλοίωτη (invariant) για τον Bakery Algorithm 19 20 (xi) (xii) Η απόδειξη του παραπάνω θεωρήµατος δυστυχώς δεν είναι δυνατή έστω p 1 = trying, p 2 = critical, t 1 = 1, t 2 = 0 και Safe(σ), Step(σ, σ') αλλά Safe(σ')! Πού βρίσκεται το πρόβληµα; Είναι αδύνατο p 2 = critical χωρίς t 2 >0 Ο αλγόριθµος δε θα φτάσει ποτέ στην κατάσταση σ, όµως αυτό δεν µπορούµε να το εκµεταλλευτούµε κατά την απόδειξη Ασφάλεια (ver2) ExtraSafe S όταν σ ExtraSafe γράφουµε ExtraSafe(σ) (p 1 = critical p 2 = critical) t i = 0 p i = idle Θεώρηµα ασφάλειας (ver2) ExtraSafe(σ 0 ) ExtraSafe(σ) Step(σ, σ') ExtraSafe(σ') 21 22 (xiii) (xiv) Και πάλι η απόδειξη του θεωρήµατος δεν είναι δυνατή έστω p 1 = trying, p 2 = critical, t 1 = 1, t 2 = 2 και ExtraSafe(σ), Step(σ, σ') αλλά ExtraSafe(σ')! Πού βρίσκεται το πρόβληµααυτή τη φορά; Είναι αδύνατο p 2 = critical και t 1 < t 2 Ασφάλεια (ver3) UltraSafe S όταν σ UltraSafe γράφουµε UltraSafe(σ) (p 1 = critical p 2 = critical) t i = 0 p i = idle p i = critical p j = trying t i < t j Θεώρηµα ασφάλειας (ver3) UltraSafe(σ 0 ) UltraSafe(σ) Step(σ, σ') UltraSafe(σ') 23 24 4
(xv) Reset Initial Require Arith Inversion H10 process4b : (p1 : Phase) (t1, t2 : Ticket) Apply Inductive Phase : Set := Split H7 (Process (state p1 t1 critical t2) (state p1 t1 idle O)) Split idle : Phase Split Assumption Decompose [and] H8 trying : Phase Intro Intro Assumption critical : Phase Definition s0 : State := Inversion Decompose Assumption Split H11 [and] Auto H7 (state idle O idle O) Split Cut p1=idle Auto Definition Ticket : Set := Inversion Intro Apply Rewrite Intro H0 H4 -> H10 nat Inductive Reachable : State -> Prop := Intro Compute s Auto Auto Intro Decompose Split Intro [and] H8 Inversion Inversion Intro init : Induction Rewrite s <- H1 in H Assumption H11 H11 Decompose [and] (Reachable s0) Compute Compute Intro in Intro H Inductive State : Set transition := : Intros Decompose Decompose Decompose Apply Compute Inversion H10 [and] [and] H [and] H5 H7 Compute H7 state : Phase -> Ticket (s,-> s' Phase : State) -> Ticket (Reachable -> State s) -> (Process Decompose s Split Inversion Inversion Auto Rewrite s') [and] -> (Reachable H H9 H10 Rewrite <- H2 in H Compute s') in Intro <- H1 in H Apply Intros Compute H H1 Assumption Decompose Compute Compute Compute Decompose Decompose in H [and] H [and] [and] H7 Decompose Inductive Process Definition : State -> safe State :-> State Prop ->:= Prop := Qed Inversion Rewrite Rewrite H9 <-Rewrite Split Inversion [and] H9 H H1 <-in H1 H <-in Split H2 H in H process1a : Compute Compute in Compute Intros Qed H in H in Intros H [s : State] (p2 : Phase) (t1, Cases t2 s : of Ticket) Lemma strong_safe_s0 Split Decompose Decompose Decompose Decompose :[and] [and] H Decompose [and] [and] H H H8 (Process (state (state idle t1 p1 p2 t1 t2) p2 t2) (state => trying (S t2) p2 (strong_safe t2)) Intro Split Split Split Rewrite Lemma -> H10 strong_safet [and] H7 Inversion in H7 s0) process1b : (p2 : Phase) ~(p1 (t1, = critical t2 : Ticket) /\ p2 = critical) Inversion Intro Intro Intros Cut ~(le (S (s t1) : State) H9 t2) (Rea H7 (p1 : Phase) (t1, endt2 : Ticket) Proof Decompose Decompose Decompose Intro [and] [and] H7 Split [and] H7 H8 (Process (state p1 t1 idle t2) (state p1 t1 trying (S Compute t1))) Split Cut p2=idle Cut p1=idle Rewrite Apply -> H12 Proof Auto H11 in H9 process2a : Split Assumption Rewrite Rewrite -> Cut H10 -> ~(le Apply H9 (S H7 Intros t2) t1) (p2 : Phase) Definition (t1 : Ticket) strong_safe : State -> Prop := Intro Intro Intro Intros Auto Induction H Split Apply strong_saf (Process (state [s : trying State] t1 p2 O) (state critical t1 p2 Decompose O)) Split Inversion [and] Inversion Apply H H11 H11 H12 Assumption Apply Apply H9 gt_not_le process2b : Cases s of Inversion Intro H0 (p1 : Phase) (t2 (state : Ticket) p1 t1 p2 t2) => Decompose Apply Apply [and] H4 Auto Unfold gt Apply strong_saf H5 H7 Split Apply lt_s Assumption Coq (Process (state p1 ~(p1 O trying = critical t2) (state /\ p2 = p1 critical) O /\ t2)) Split Inversion Auto Auto H9 Intro Apply Assumption gt_not_le Decompose process3a : (t1 = O -> p1 = idle) /\ Auto Assumption [and] H7 (p2 : Phase) (t1, t2 (t2: = Ticket) O -> p2 = idle) /\ Intro Split Split Unfold gt Inversion (lt t1 t2) -> (p1 = critical /\ p2 = trying -> (lt Split t1 t2)) Auto /\ Intro Assumption Apply Split Qed H9 lt_s (Process (state(p2 trying = critical t1 p2 t2) /\ p1 (state = trying critical -> (lt t1 Auto t2 p2 t1)) t2)) Cut trying=idle Assumption Assumption Intro Theorem safety : process3b : end Compute Intro Split Decompose (p1 : Phase) (t1, t2 : Ticket) Split Rewrite Inversion <-Intro Split Split (s : State) [and] H7 (Rea H1 in H9 H Inversion (lt t2 t1) -> Intro Compute in Cut H trying=idle Intro Intro H10 (Process Lemma (state strong_is_strong p1 t1 trying t2) :(state p1 t1 critical Decompose t2)) Decompose Apply [and] Intro [and] H5 Cut trying=idle Cut trying=idle Proof H H Compute process4a : (s : State) Inversion Split Assumption Inversion Intro Intro Intros H9 Rewrite H0 Inversion Inversion Apply <- H10 H10 strong_is_ H1 in H (p2 : Phase) (t1,(strong_safe t2 : Ticket) s) -> (safe s) Intros Compute Apply in strong_saf H (Process (state critical t1 p2 t2) (state idle O p2 t2)) Intro Decompose Split Apply [and] H4 H7 Decompose [and] H Proof Decompose Inversion Assumption Assumption Apply Apply H6 H5 Assumption [and] H10 Split H Assumption Assumption Qed Intro Inversion H0 Qed Split Split Split Decompose [and] H7 Assumption Intro Intro Split Split 25 Lemma strong_safe_step Decompose Decompose Assumption Intro [and] [and] : H7 H7 li C t Inversion 2 idl H10 5