ΤΕΧΝΟΛΟΓΙΑ ΛΟΓΙΣΜΙΚΟΥ http://courses.softlab.ntua.gr/softeng/ ιδάσκοντες: (nickie@softlab.ntua.gr) Γιάννης Μαΐστρος (maistros@cs.ntua.gr) Βασίλης Βεσκούκης (bxb@softlab.ntua.gr) Τυπικές Μέθοδοι στην Ανάπτυξη Συστηµάτων Λογισµικού Εισαγωγή Τυπικές µέθοδοι: υπέρ και κατά Εργαλεία και φορµαλισµοί Παράδειγµα 1
Εισαγωγή (i) Software Engineering Όλες οι (άλλες) επιστήµες µηχανικών βασίζονται σε σταθερό θεωρητικό (µαθηµατικό) υπόβαθρο Θεωρητικό υπόβαθρο για την ΤΛ Επιστήµη υπολογιστών Επιστηµονικοί κλάδοι που αντιστοιχούν σε συγκεκριµένες περιοχές εφαρµογών, π.χ. µαθηµατικά, φυσική, στατική, αρχιτεκτονική 2
Εισαγωγή (ii) Μειονεκτήµατα της παραδοσιακής διαδικασίας ανάπτυξης λογισµικού Χρειάζεται µεγάλη προσπάθεια ώστε οι προδιαγραφές να µην είναι: διφορούµενες αφηρηµένες αντιφατικές ελλιπείς Ο έλεγχος κοστίζει και παρέχει µικρό βαθµό βεβαιότητας (~10 4 ) 114,000 χρόνια για 10 9! 3
Εισαγωγή (iii) Τυπικές µέθοδοι formal methods τεχνικές για την περιγραφή των ιδιοτήτων ενός συστήµατος, βασισµένες στα µαθηµατικά χρησιµοποιούνται για την προδιαγραφή, ανάπτυξη και επαλήθευση συστηµάτων µε συστηµατικό τρόπο η µαθηµατική βάση συνοδεύεται συνήθως από µια γλώσσα τυπικών προδιαγραφών και ορίζει έννοιες όπως: συνέπεια, πληρότητα προδιαγραφή, υλοποίηση, ορθότητα 4
Τυπικές µέθοδοι: υπέρ και κατά (i) Γιατί τίθεται έτσι αυτό το ζήτηµα; Πλεονεκτήµατα καλύτερη κατανόηση των απαιτήσεων και της σχεδίασης συστηµάτων λογισµικού χειρισµός προδιαγραφών ως µαθηµατικών αντικειµένων, π.χ. επαλήθευση (verification) αντί επικύρωσης (validation) αυτόµατη επεξεργασία προδιαγραφών και υποστήριξη από εργαλεία µείωση κόστους για έλεγχο και συντήρηση 5
Τυπικές µέθοδοι: υπέρ και κατά (ii) Μειονεκτήµατα αύξηση κόστους για ανάλυση και σχεδίαση έλλειψη σχετικής εκπαίδευσης ή/και απειρία των µηχανικών λογισµικού αδυναµία ή εγγενής δυσκολία των τυπικών µεθόδων να προδιαγράψουν ορισµένα είδη συστηµάτων λογισµικού έλλειψη καλών εργαλείων υποστήριξης Αποτέλεσµα αδράνεια και σκεπτικισµός ή ακόµα και άρνηση από την βιοµηχανία λογισµικού 6
Τυπικές µέθοδοι: υπέρ και κατά (iii) Μύθοι σχετικοί µε τις τυπικές µεθόδους µε τις ΤΜ παράγεται τέλειο λογισµικό ΤΜ = απόδειξη ορθότητας προγραµµάτων λόγω κόστους, οι ΤΜ έχουν νόηµα µόνο για συστήµατα λογισµικού υψηλής ασφάλειας οι ΤΜ αυξάνουν το συνολικό κόστος ανάπτυξης συστηµάτων λογισµικού οι πελάτες δεν µπορούν να καταλάβουν τις τυπικές προδιαγραφές οι ΤΜ έχουν χρησιµοποιηθεί µόνο για τετριµµένα ή πολύ απλά προβλήµατα [Hall 1990] 7
Εργαλεία και φορµαλισµοί (i) Εργαλεία (ηµι-)αυτόµατης απόδειξης ACL2 Coq HOL IMPS Isabelle Nuprl PVS 8
Εργαλεία και φορµαλισµοί (ii) Μέθοδοι, φορµαλισµοί και εργαλεία ASM: Abstract State Machines B-Method CSP: Communicating Sequential Processes DC: Duration Calculus ITL: Interval Temporal Logic Esterel Larch LOTOS Obj / CafeOBJ 9
Εργαλεία και φορµαλισµοί (iii) Μέθοδοι, φορµαλισµοί και εργαλεία (συνέχεια) 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 10
(i) Πρόβληµα: αµοιβαίος αποκλεισµός σε περιβάλλον παράλληλης εκτέλεσης Μία λύση: the Bakery Algorithm υπάρχει ένα µηχάνηµα που εκδίδει αριθµούς προτεραιότητας κατά αύξουσα σειρά κάθε πελάτης που θέλει να εξυπηρετηθεί παίρνει αριθµό προτεραιότητας κάθε φορά εξυπηρετείται ο πελάτης µε το µικρότερο αριθµό προτεραιότητας 11
(ii) Μία άτυπη περιγραφή της λύσης Κάθε πελάτης έχει µια αριθµητική µεταβλητή Αρχικά, η µεταβλητή του έχει την τιµή µηδέν Κάθε φορά που θέλει να εξυπηρετηθεί, δίνει στη µεταβλητή του µια τιµή µεγαλύτερη από τις µεταβλητές όλων των άλλων πελατών Στη συνέχεια περιµένει έως ότου η τιµή της µεταβλητής του γίνει µικρότερη από τις µεταβλητές όλων των άλλων πελατών Τότε εξυπηρετείται και στη συνέχεια ξαναδίνει στη µεταβλητή του την τιµή µηδέν 12
(iii) Μία υλοποίηση σε Java class Client extends Thread { static List allclients = new LinkedList(); int ticket; public Client () { ticket = 0; allclients.add(this); } void getserved (); void eatbread (); 13
(iv) Μία υλοποίηση σε Java (συνέχεια) static int maxticket () { int result = 0; } for (Iterator i = allclients.iterator(); i.hasnext();) { Process p = (Process) i.next(); if (p.ticket > 0 && p.ticket > result)) result = p.ticket; } return result; 14
(v) Μία υλοποίηση σε Java (συνέχεια) static int minticket () { int result = 0; } for (Iterator i = clients.iterator(); i.hasnext();) { Process p = (Process) i.next(); if (p.ticket > 0 && (result == 0 p.ticket < result)) result = p.ticket; } return result; 15
(vi) Μία υλοποίηση σε Java (συνέχεια) } public void run () { for (;;) { synchronized (getclass()) { ticket = maxticket() + 1; } while (ticket > minticket()); } } getserved(); ticket = 0; eatbread(); // trying // critical // idle 16
(vii) Απαιτήσεις Ασφάλεια (safety): κάθε στιγµή εξυπηρετείται το πολύ ένας πελάτης Ζωντάνια (liveness): κάθε πελάτης κάποια στιγµή θα εξυπηρετηθεί Ανάλυση ενός µοντέλου για τη λύση Πόση πληροφορία θα περιέχει το µοντέλο; Υπερβολικά πολλή πληροφορία: η ανάλυση µπορεί να µην είναι εφικτή Υπερβολικά λίγη πληροφορία: η ανάλυση µπορεί να µην είναι ακριβής 17
(viii) Παραδοχές Εστιαζόµαστε σε µια αφηρηµένη υλοποίηση, σε ένα ιδανικό περιβάλλον εκτέλεσης Αν και η λύση είναι κατάλληλη για n 1 πελάτες, εξετάζουµε την περίπτωση n = 2 Καταστάσεις Για κάθε πελάτη, i { 1, 2 } phase p i { idle, trying, critical } ticket t i N σ = (p 1, p 2, t 1, t 2 ) S Αρχική κατάσταση σ 0 : p 1 = p 2 = idle, t 1 = t 2 = 0 18
(ix) Μετάβαση 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 = critical τότε p 1 ' =idle και t 1 ' = 0 19
(x) Ασφάλεια Safe S όταν σ Safe γράφουµε Safe(σ) Ορισµός (p 1 = critical p 2 = critical) Θεώρηµα ασφάλειας Safe(σ 0 ) Safe(σ) Step(σ, σ') Safe(σ') η ιδιότητα Safe είναι αναλλοίωτη (invariant) για τον Bakery Algorithm 20
(xi) Ηαπόδειξη του παραπάνω θεωρήµατος δυστυχώς δεν είναι δυνατή έστω p 1 = trying, p 2 = critical, t 1 = 1, t 2 = 0 τότε p 1 ' = critical και Safe(σ), Step(σ, σ') αλλά Safe(σ')! Πού βρίσκεται το πρόβληµα; Είναι αδύνατο p 2 = critical χωρίς t 2 >0 Ο αλγόριθµος δε θα φτάσει ποτέ στην κατάσταση σ, όµως αυτό δεν µπορούµε να το εκµεταλλευτούµε κατά την απόδειξη 21
(xii) Ασφάλεια (ver.2) ExtraSafe S όταν σ ExtraSafe γράφουµε ExtraSafe(σ) Ορισµός (p 1 = critical p 2 = critical) t i = 0 p i = idle Θεώρηµα ασφάλειας (ver.2) ExtraSafe(σ 0 ) ExtraSafe(σ) Step(σ, σ') ExtraSafe(σ') 22
(xiii) Και πάλι η απόδειξη του θεωρήµατος δεν είναι δυνατή έστω p 1 = trying, p 2 = critical, t 1 = 1, t 2 = 2 τότε p 1 ' = critical και ExtraSafe(σ), Step(σ, σ') αλλά ExtraSafe(σ')! Πού βρίσκεται το πρόβληµα αυτή τη φορά; Είναι αδύνατο p 2 = critical και t 1 < t 2 23
(xiv) Ασφάλεια (ver.3) 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 Θεώρηµα ασφάλειας (ver.3) UltraSafe(σ 0 ) UltraSafe(σ) Step(σ, σ') UltraSafe(σ') 24
(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. s') Inversion Inversion Auto. Rewrite [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 Inversion Apply [and] 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. (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 [and] H7. Coq process3a : (t1 = O -> p1 = idle) /\ Auto. Assumption. (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 Intro. [and] [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 Decompose Decompose Assumption. Intro. [and] [and] H7. H7.