all p-uses- ¨ UberdeckungstestIm all p-uses- ¨Uberdeckungstest wird gefordert, dass f¨ur jede Entscheidung und f¨urjede darin verwendete Variable jede Kombination mit deren Definitionen,
Trang 1all p-uses- ¨ Uberdeckungstest
Im all p-uses- ¨Uberdeckungstest wird gefordert, dass f¨ur jede Entscheidung und f¨urjede darin verwendete Variable jede Kombination mit deren Definitionen, welche dieEntscheidung erreichen, gepr¨uft wird Die Testf¨alle m¨ussen also das folgende Krite-
rium erf¨ullen: F¨ur jeden Knoten viim datenflussattributierten Kontrollflussgraph und
jeder Variablen x ∈ de f s(v i) muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu jeder Kante in dpu(x, vi) getestet werden.
Beispiel 7.2.20 Betrachtet wird das Programm aus Beispiel 7.2.16 Um 100% all p-uses- ¨Uberdeckung zu erzielen, sind zwei Testfalleingaben notwendig:(vin ,v1,v3, vout ) und (vin ,v1,v2,v3,v out ) Damit werden die beiden Kanten (v1,v3) und (v1,v2),
die mit p-uses attributiert sind, getestet.
Somit subsumiert der all p-uses- ¨Uberdeckungstest den Zweig¨uberdeckungstest
all c-uses- ¨ Uberdeckungstest
Analog zum all p-uses- ¨ Uberdeckungstest wird im all c-uses- ¨Uberdeckungstest fordert, dass f¨ur jeden globalen berechnenden Zugriff und f¨ur jede darin verwendeteVariable jede Kombination mit deren Definitionen, die den Zugriff erreichen, gepr¨uftwird Die Testf¨alle m¨ussen somit das folgende Kriterium erf¨ullen: F¨ur jeden Knoten
ge-vi im datenflussattributierten Kontrollflussgraph und jeder Variablen x ∈ de f s(v i) muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten v izu jedem Kno-
ten in dcu(x, vi) getestet werden.
Beispiel 7.2.21 F¨ur das Programm aus Beispiel 7.2.16 f¨uhrt die Testfalleingabe (vin ,v1,v2,v3,v out) zu einer 100%-igen all c-uses- ¨Uberdeckung.
Der all c-uses- ¨Uberdeckungstest subsumiert weder Zweig-, Anweisungs- noch
einen anderen defs/uses- ¨Uberdeckungstest
all c-uses/some p-uses- ¨ Uberdeckungstest
Der all c-uses- ¨Uberdeckungstest pr¨uft offensichtlich lediglich Variablendefinitionen,die in berechnenden Zugriffen m¨unden Variablen, die ausschließlich pr¨adikativ ver-
wendet werden, werden somit nicht getestet Der all c-uses/some p-uses- ¨
Uberde-ckungstest erweitert den all c-uses- ¨Uberdeckungstest dahingehend, dass f¨ur schließlich pr¨adikativ verwendete Variablen ebenfalls Testf¨alle gefordert werden.Somit m¨ussen die Testf¨alle das folgende Kriterium erf¨ullen:
aus-• F¨ur jeden Knoten v iim datenflussattributierten Kontrollflussgraph und jeder
Va-riablen x ∈ de f s(v i) muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu jedem Knoten in dcu(x, vi) getestet werden.
• Ist dcu(x, v i) leer, so muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu einer Kante in dpu(x, vi) getestet werden.
Trang 2Beispiel 7.2.22 F¨ur das Programm aus Beispiel 7.2.16 fordert der all c-uses/some uses- ¨Uberdeckungstest die Testfalleingabe (vin ,v1,v2,v3,v out) Die Definitionen in vin und v2 erfordern den Test der berechnenden Zugriffe in v2und vout Da damit
p-bereits alle definierten Variablen getestet wurden, m¨ussen keine weiteren Testf¨allehinzugenommen werden, obwohl der Zweig(v1,v3) noch nicht getestet wurde
Der all c-uses/some p-uses- ¨ Uberdeckungstest subsumiert den all defs- ¨ckungstest Er subsumiert aber weder den Zweig- noch den Anweisungs¨uberde-ckungstest
Uberde-all p-uses/some c-uses- ¨ Uberdeckungstest
Analog zum all c-uses/some p-uses- ¨ Uberdeckungstest existiert der all p-uses/some uses- ¨ Uberdeckungstest Bei diesem wird zus¨atzlich zu den Testf¨allen des all p-uses-
c-¨
Uberdeckungstests gefordert, dass f¨ur eine Variable, die ausschließlich berechnendverwendet wird, also in keiner pr¨adikativen Benutzung auftaucht, weitere Testf¨allegeneriert werden m¨ussen Das Kriterium f¨ur die Testf¨alle lautet somit:
• F¨ur jeden Knoten v iim datenflussattributierten Kontrollflussgraph und jeder
Va-riablen x ∈ de f s(v i) muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu jeder Kante in dpu(x, vi) getestet werden.
• Ist dpu(x, v i) leer, so muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu einem Knoten in dcu(x, vi) getestet werden.
Beispiel 7.2.23 Betrachtet wird das Programm aus Beispiel 7.2.16 auf Seite 411 F¨ur eine 100%-ige all p-uses/some c-uses- ¨Uberdeckung sind die beiden Testfalleingaben
(vin ,v1,v3,v out ) und (vin ,v1,v2,v3,v out) notwendig Die Definitionen von min und
max in vinerfordern den Test der pr¨adikativen Verwendung in(v1,v3) und (v1,v2)
Zus¨atzlich wird aber durch die Definitionen in Knoten v2gefordert, dass der
berech-nende Zugriff in vout getestet wird Dies ist allerdings bereits im zweiten Testfallenthalten
Der all p-uses/some c-uses- ¨Uberdeckungstest subsumiert Zweig,
Anweisungs-und all p-uses- ¨Uberdeckungstest
all uses- ¨ Uberdeckungstest
Die Kombination aus den all c-uses/some p-uses- und all p-uses/some c-uses- ¨
Uber-deckungstests f¨uhrt zu dem all uses- ¨Uberdeckungstest Es wird dabei gefordert, dassf¨ur jede globale Definition jede erreichbare berechnende und pr¨adikative Verwen-
dung getestet wird Das Kriterium f¨ur die Testf¨alle lautet somit: F¨ur jeden Knoten vi
im datenflussattributierten Kontrollflussgraph und jeder Variablen x ∈ de f s(v i) muss mindestens ein definitionsfreier Pfad bez¨uglich x von Knoten vi zu jedem Knoten
in dpu(x, vi) und zu jeder Kante in dpu(x, vi) getestet werden Der all uses- ¨
Uber-deckungstest subsumiert somit den all c-uses/some p-uses- und den all p-uses/some c-uses- ¨Uberdeckungstest
Die Subsumierungsrelationen der in diesem Abschnitt vorgestellten Verfahrenzur Generierung strukturorientierter Testf¨alle ist zusammenfassend in Abb 7.20 dar-gestellt
Trang 3Bedingungs-
Mehrfach-¨uberdeckungstest Bedingungs- minimaler Mehrfach-
¨uberdeckungstest
Bedingungs-/ Entscheidungs-
¨uberdeckungstest Bedingungs- einfacher
¨uberdeckungstest Anweisungs-
¨uberdeckungstest Zweig-
¨ Uberdeckungstest
all
p-uses-¨ Uberdeckungstest
all p-uses/
some
c-uses-¨ Uberdeckungstest
all c-uses/
some
p-uses-¨ Uberdeckungstest
7.3 Formale funktionale Eigenschaftspr ¨ufung von Programmen
Die automatischen Verfahren zur formalen funktionalen Eigenschaftspr¨ufung vonProgrammen haben in den vergangenen Jahren enorme Fortschritte verzeichnet.Einen guten ¨Uberblick hier¨uber gibt die Ver¨offentlichung [140] Im Folgenden wer-den in Anlehnung an [140] einige Techniken n¨aher betrachtet
7.3.1 Statische Programmanalyse
Der Begriff statische Programmanalyse beschreibt Techniken, mit deren Hilfe
Infor-mationen ¨uber das Verhalten von Programmen ermittelt werden k¨onnen, ohne dieseauszuf¨uhren Viele dieser Techniken wurden f¨ur die Optimierungsphase in Com-pilern konzipiert Dennoch ist deren Anwendungsgebiet nicht auf diese Aufgabebeschr¨ankt Hier wird statische Programmanalyse im Kontext der formalen Eigen-schaftspr¨ufung von Programmen betrachtet
Ein grundlegendes Problem in der Programmverifikation ist, dass viele tionsfragen unentscheidbar oder zu rechenintensiv sind, um sie zu beantworten Des-halb berechnen Techniken zur statischen Programmanalyse typischerweise lediglichApproximationen f¨ur Eigenschaften Diese m¨ussen allerdings Korrektheitsgarantienliefern, d h nicht zu Fehlschl¨ussen verleiten
Trang 4Verifika-Beispiel 7.3.1 Ein statisches Verfahren zur Detektion von
”Division durch Fehlern in einem gegeben Programm muss s¨amtliche Werte eines Divisors, die zurLaufzeit des Programms auftreten k¨onnen, ber¨ucksichtigen Da eine Aufz¨ahlung al-ler potentiellen Werte in der Regel aus Zeitgr¨unden nicht m¨oglich ist, arbeiten Ver-fahren zur statischen Programmanalyse typischerweise mit Teil- bzw Obermengen
Null“-Verwendet die Analyse eine Teilmenge aller m¨oglichen Werte und findet dabei keine
Fehler, kann keine endg¨ultige Aussage ¨uber die Abwesenheit von
”Division durchNull“-Fehlern gemacht werden Liefert das Analyseverfahren dennoch dieses Ergeb-nis, so kann diese Aussage falsch sein, da die verwendet Approximation inkorrektist
Verwendet das Verfahren hingegen eine Obermenge aller m¨oglichen Werte eines
Divisors, so ist die Approximation korrekt, und die Aussage, dass keine
”Divisiondurch Null“-Fehlern existieren, gerechtfertigt Allerdings kann eine solche ¨Uberap-
proximation zu falschnegativen Ergebnissen f¨uhren, d h der Fehler tritt lediglich
unter Verwendung von Werten auf, die zwar in der Obermenge, nicht aber in der spr¨unglichen Menge m¨oglicher Werte des Divisors liegt In diesem Zusammenhang
ur-spricht man auch von unzul¨assigen Gegenbeispielen.
Im Gegensatz zu falschnegativen Ergebnissen k¨onnen bei der statischen grammanalyse auch falschpositive Ergebnisse auftreten Hierbei handelt es sich umTestf¨alle, die als fehlerfrei angesehen werden, es aber in Wirklichkeit gar nicht sind.Aufgrund der Unentscheidbarkeit statischer Analyseprobleme kann nicht garantiertwerden, dass eine Methode sowohl keine falschnegativen als auch keine falschposi-tiven Ergebnisse erzeugt
Pro-Statische Programmanalyse beruht auf der Idee der Fixpunktberechnung (sieheauch Anhang C.4) Dabei werden Wertemengen solange durch ein Programm pro-pagiert und angepasst, bis diese Mengen sich nicht weiter ¨andern Dies wird anhanddes Beispiels aus [140] illustriert:
Beispiel 7.3.2 Gegeben ist folgender Ausschnitt eines C-Programms:
hen Der Zustand v2repr¨asentiert dabei die Programmzeilen 2 und 3 Der Zustand v5
repr¨asentiert das Verlassen des Programmabschnitts und der Zustand veden zustand, der durch die assert-Anweisung erreicht wird
Fehler-Links in Abb 7.21 sind f¨ur die ersten beiden Iterationen f¨ur jeden Knoten desKontrollflussgraphen die zugeh¨orige Wertemenge f¨ur die Variable i angegeben Die-
se enth¨alt diejenigen Werte, welche die Variable i bei Erreichen des repr¨asentierten
Trang 5Abb 7.21 Kontroll-Datenflussgraph zu dem Programmsegment aus Beispiel 7.3.2
Zustands tragen kann Zu Beginn der ersten Iteration im Zustand v1h¨alt die ble i einen beliebigen Wert Die Wertemenge ist somit die Menge aller Werte inINT, die eine Variable von Typ int in der Programmiersprache C annehmen kann.F¨ur die statische Programmanalyse werden nun die Wertemengen entlang der Kon-trollflusskanten propagiert und entsprechend der Operationen im Datenflussgraphenmanipuliert
Varia-Die beiden ersten Iterationen laufen wie folgt ab:
1 Zu Beginn ist die Variable i nicht initialisiert und nimmt somit einen
beliebi-gen Wert aus der Menge INT an Bei Erreichen von Zustand v2wurde i bereitsinitialisiert und besitzt den Wert 0 Somit ergibt sich die Wertemenge{0} mit
lediglich einem Element Die assert-Anweisung in Zeile 3 ¨andert an dieser
Wertebelegung nichts Bei Erreichen von Zustand v4wurde die Variable i reits um 2 erh¨oht, weshalb die Wertemenge als einziges Element die 2 enth¨alt
be-Zustand v5und vewerden in der ersten Iteration aufgrund der gen 2< 5 bzw 0 ≤ 10 nicht erreicht.
Kontrollanweisun-2 Die Wertemenge{2} wird nun von Zustand v4 zu Zustand v2propagiert Da v2mehrere Eingangskanten besitzt, kann sich in diesem Knoten die Wertemengevergr¨oßern, da die Vereinigung aller ankommenden Wertemengen gebildet wer-den muss Das Ergebnis ist somit die Wertemenge{0,2}, wobei das Element 0
noch aus der ersten Iteration stammt Da beide Werte kleiner gleich zehn sind,
wird die Wertemenge nicht an Zustand ve propagiert Man beachte aber auch,
dass der Knoten v in diesem Durchlauf nicht wieder betrachtet werden muss,
Trang 6da sich die dort ermittelte Wertemenge nicht ge¨andert hat Die Menge {0,2} wird nun unver¨andert weiter an Knoten v3propagiert Dort werden die Elemente
um zwei erh¨oht, weshalb sich f¨ur Zustand v4die Wertemenge{2,4} ergibt Da beide Elemente echt kleiner f¨unf sind, wird die Wertemenge nicht an v5jedoch
aber an v2propagiert
Die Iteration wird solange fortgesetzt, bis ein Fixpunkt erreicht ist, d h keine ¨rungen in den Wertemengen auftreten
Ande-Die eben gerade beschriebene Programmanalyse wird als konkrete Interpretation
bezeichnet Dies liegt darin begr¨undet, dass Elemente aus INT und beliebigen
Teil-mengen daraus konkrete Werte und somit einen konkreten Wertebereich darstellen.
Abstrakte Interpretation
Konkrete Interpretation l¨asst sich in der Praxis nicht anwenden, da die gen schnell anwachsen Bereits 1965 stellte Peter Naur fest, dass es ausreichend seinkann, abstrakte Werte bei der Programmanalyse zu verwenden [341] Basierend auf
Wertemen-diesem Ergebnis entwickelten Cousot und Cousot 1977 die Methode der ten Interpretation [118] Abstrakte Interpretation bedient sich zweier zentraler Kon- zepte: dem sog abstrakten Wertebereich, der eine Approximation eines konkreten Wertebereichs ist, und sog abstrakter Funktionen, die dazu dienen, konkrete Werte
abstrak-in abstrakte Werte zu ¨ubersetzen Abstrakte Interpretation hat zum Ziel, eabstrak-ine ximative L¨osung des Programmanalyseproblems zu liefern Dabei wird das Verhal-ten eines Programms f¨ur abstrakte Wertebereiche analysiert Dies geschieht, indemdie Operationen auf den konkreten Wertebereichen aus der konkreten Interpretationdurch geeignete Funktionen ersetzt werden
appro-Abstrakte Wertebereiche lassen sich in relationale und nichtrelationale
abstrak-te Werabstrak-tebereiche einabstrak-teilen Beispiele f¨ur nichtrelationale Werabstrak-tebereiche sind zeichen-Wertebereiche, Intervall-Wertebereiche oder Kongruenz-Wertebereiche Der
Vor-Vorzeichen-Wertebereich besitzt drei Elemente{pos,neg,zero} zur Unterscheidung
positiver und negativer Zahlen, sowie der Null Intervall-Wertebereiche sind siver, da der Vorzeichen-Wertebereich durch{[−∞,0),[0,0],(0,∞]} repr¨asentiert
expres-werden kann
Beispiel 7.3.3 Betrachtet wird wiederum das Programmsegment aus Beispiel 7.3.2
mit dem Kontroll-Datenflussgraphen aus Abb 7.21 Ein m¨oglicher abstrakter bereich ist z B die Menge der Intervalle{[a,b] | a ≤ b∧a,b ∈ Z} Die in der konkre-
Werte-ten Interpretation verwendeWerte-ten Operationen Addition und Vereinigung m¨ussen f¨urdie abstrakte Interpretation ersetzt werden durch Addition und Vereinigung von In-tervallen Die abstrakte Interpretation ist in Abb 7.22 an dem Beispiel durchgef¨uhrt
Im Folgenden seien min bzw max der minimale bzw maximale Wert, der sich
im Wertebereich INT darstellen l¨asst In Zustand v1, zu Beginn der ersten Iteration,ist die Variable i noch nicht initialisiert und kann somit einen beliebigen Wert imIntervall[min,max] annehmen Nach der Initialisierung (Zustand v2) besitzt i den
Trang 7Abb 7.22 Abstrakte Interpretation mit Intervallen
konkreten Wert 0, was als Intervall[0,0] geschrieben wird Bis auf die
Intervall-schreibweise der Wertemengen verl¨auft die erste Iteration der abstrakten tation ¨aquivalent zur konkreten Interpretation aus Beispiel 7.3.2 Man beachte aber,dass Zuweisung und Addition sich jeweils auf Unter- und Obergrenze des Intervallsauswirkt Nach Beendigung der ersten Iteration wird das Intervall[2,2] von Zustand
Interpre-v4an Zustand v2propagiert Zusammen mit dem Intervall[0,0] aus der
Initialisie-rung von i ergibt sich eine ¨Uberapproximation des Wertebereichs durch das Intervall
[0,2] Man sieht also, dass die Mengenvereinigung in der Intervallarithmetik durch
die Minimums- bzw Maximumsoperation ¨uber die Bereichsgrenzen ersetzt wurde
Bei Erreichen von Zustand v4in der zweiten Iteration wird die Wertemenge mit demIntervall[2,4] approximiert.
Zu Beginn der dritten Iteration ergibt sich somit die Wertemenge f¨ur v2 von
[min{0,2},max{0,4}] = [0,4] Die Wertemenge f¨ur v4wird in dieser Iteration mit
[2,6] approximiert In diesem Fall ist die Obergrenze erstmals gr¨oßer als die
Schran-ke 5, die zur Steuerung der while-Schleife verwendet wird Aus diesem Grund mussdie Wertemenge geteilt werden, um eine Fallunterscheidung zu erreichen Die appro-ximierte Wertemenge[2,4] wird zur¨uck an v2propagiert, w¨ahrend das verbleibendeIntervall[5,6] an Zustand v5¨ubertragen wird Nach dieser Aufteilung der Wertemen-
ge ist das an v2propagierte Intervall aber das selbe, das auch am Ende von Iterationzwei propagiert wurde Eine vierte Iteration offenbart somit den Fixpunkt in der ab-strakten Interpretation
Trang 8Da es sich bei der Intervallbildung um eine ¨Uberapproximation handelt, kannman auch f¨ur jede konkrete Programmausf¨uhrung schließen, dass die Zusicherungassert(x <= 10) immer g¨ultig ist.
Kongruenz-Wertebereiche repr¨asentieren den Wert value(x) einer Variablen x als (value(x) mod k) f¨ur ein gegebenes k F¨ur k = 2 erh¨alt man den sog Parit¨atswertebe- reich {odd,even} Man beachte, dass der Intervall-Wertebereich zwar deutlich mehr
Elemente enthalten kann, aber dennoch nicht expressiver ist als der bereich, da die geraden und ungeraden Zahlen nicht durch eine endliche Anzahl
Parit¨atswerte-an Intervallen repr¨asentiert werden k¨onnen Das folgende Beispiel zu Wertebereichen stammt aus [140]
Kongruenz-Beispiel 7.3.4 Betrachtet wird der Ausdruck 1 /(x − y) Kann unter Verwendung der
abstrakten Interpretation mit Kongruenz-Wertebereichen f¨ur ein gegebenes, aber
be-liebiges k gezeigt werden, dass (x mod k) = (y mod k) ist, so kann daraus geschlossen
werden, dass eine Division durch Null in dieser Anweisung nicht auftreten kann.Nicht relationale abstrakte Wertebereiche lassen sich leicht handhaben Aller-
dings k¨onnen mit ihnen bereits Zusicherungen der Form x ≤ y nicht mehr ¨uberpr¨uft werden Relationale abstrakte Wertebereiche l¨osen dieses Problem Eine einfache Form relationaler abstrakter Wertebereiche sind die sog differenzenbeschr¨ankten Matrizen (engl difference bound matrices, DBMs) Diese bestehen aus Konjunk- tionen von Ungleichungen der Form x −y ≤ c Obwohl DBM-Wertebereiche expres-
siver als Intervall-Wertebereiche sind besteht dennoch eine Einschr¨ankung in derForm, dass Beschr¨ankungen der Art−x − y ≤ c nicht ausgedr¨uckt werden k¨onnen.
Oktagon-Wertebereiche beseitigen diese Einschr¨ankung, indem sie Beschr¨ankungen
der Form ax + by ≤ c erlauben, wobei a,b ∈ {−1,0,1} sind Eine Erweiterung
neh-men schließlich Oktaeder-Wertebereiche durch Verallgemeinerung auf mehr als zweiVariablen vor
Eine der ersten relationalen abstrakten Wertebereiche sind
Polyeder-Wertebe-reiche Diese werden durch die Konjunktion von Ungleichungen der Form a1x1+
a2x2+ ··· + anxn ≤ c beschrieben, wobei a1, ,a n ,c ∈ Z sind Die Manipulation
von Polyeder-Wertebereichen ist allerdings nicht trivial und bedarf der Berechnung
sog konvexer H¨ullen Die Komplexit¨at dieser Berechnungen ist exponentiell in der
Anzahl der Variablen
Die Expressivit¨at eines Wertebereichs entscheidet dar¨uber, welche funktionalenEigenschaften mit abstrakter Interpretation gezeigt werden k¨onnen Einige von denoben genannten abstrakten Wertebereichen lassen sich aber nicht hinsichtlich ih-rer Expressivit¨at vergleichen Hier sei nochmals das Beispiel des Parit¨atswertebe-reichs und des Intervall-Wertebereichs genannt Die Vorzeichen-, Intervall-, DBM-,Oktagon-, Oktaeder- und Polyeder-Wertebereiche bilden eine Hierarchie ObwohlPolyeder-Wertebereiche die Expressivsten in dieser Hierarchie sind, reichen diesenicht aus, um Eigenschaften ¨uber Ungleichheiten zu zeigen
Beispiel 7.3.5 Gegeben ist der Ausdruck 1 /(2·x+1−y) [140] Um mittels
abstrak-ter Inabstrak-terpretation zu zeigen, dass keine Division durch Null in dieser Anweisung treten kann, muss ein abstrakter Wertebereich gew¨ahlt werden, in dem 2· x + 1 = y
Trang 9auf-gezeigt werden kann Dies ist nicht mit den oben beschriebenen relationalen
abstrak-ten Wertebereichen m¨oglich Der abstrakte Wertebereich der linearen Kongruenzen
verbindet Polyeder-Wertebereiche mit Kongruenz-Wertebereichen Diese enthalten
auch Gleichungen der Form a1x1+ ··· + anxn = c mod k f¨ur ein gegebenes k Damit
kann gezeigt werden, dass(2 · x + 1) mod k ungleich y mod k ist, was auch die Frage
nach einer m¨oglichen Division durch Null beantwortet
7.3.2 SAT-basierte Modellpr ¨ufung von C-Programmen
F¨ur die SAT-basierte Modellpr¨ufung (siehe Abschnitt 5.3.2) wird, neben der zupr¨ufenden Eigenschaft, ein Modell des Programms ben¨otigt Dieses muss ein end-licher Automat sein, der aus Zust¨anden und Zustands¨uberg¨angen besteht In einemProgramm fasst ein Zustand den Wert des Programmz¨ahlers, die Werte der Pro-grammvariablen und den Zustand des Speichers zusammen Zustands¨uberg¨ange be-schreiben m¨ogliche ¨Anderungen bei der Programmausf¨uhrung von einem Zustandzum n¨achsten
Da der Kontrollfluss lediglich am Ende eines Grundblocks verzweigen und amAnfang eines Grundblocks eintreten kann, kann es sinnvoll sein, anstatt jede An-weisung einzeln zu codieren, eine Codierung auf Grundbl¨ocken vorzunehmen Einsolches Verfahren ist in [242] beschrieben
Beispiel 7.3.6 Gegeben ist das folgende C-Programm [242]:
Der resultierende Kontroll-Datenflussgraph ist in Abb 7.23 zu sehen Man erkennt,
dass jeder Zustand viim Kontrollflussgraphen einem Grundblock im Programm spricht Außerdem ist gezeigt, wie Parameter im Falle nichtrekursiver Funktions-aufrufe ¨ubergeben werden Dabei werden Argumente in der Variable l ¨ubergeben
Trang 10ent-und mittels der Variablen r wird unterschieden, an welche Variable das Ergebnis zu
Abb 7.23 Kontroll-Datenflussgraph f¨ur das Programm aus Beispiel 7.3.6
Jedem Grundblock i (Zustand im Kontrollflussgraphen) wird nun eine
einzel-ne bin¨are Variable bi ∈ B zugeordnet, die anzeigt, ob der Kontrollfluss gerade in diesem Grundblock liegt Die Belegung der Variablen bi kann direkt aus dem Pro-
grammz¨ahler abgeleitet werden Man beachte, dass bi = T impliziert, dass ∀ j =i b j=
F ist
Nach der Konstruktion des Kontroll-Datenflussgraphen werden zun¨achst die tenflussgraphen zu jedem Basisblock einzeln in Booleschen Formeln codiert Dabeiwird f¨ur jede Zuweisung var = expr eine kombinatorische Schaltung f¨ur expr er-
Trang 11Da-stellt, welche sich direkt als Boolesche Funktionen darstellen l¨asst Eine Addition
wird beispielsweise durch einen Ripple-Carry-Addierer ersetzt Der Ausdruck Vik
bezeichne dann die Zuweisung an die Variable vari im Grundblock k Die
Codie-rung aller Grundbl¨ocke resultiert darin, dass alle Variablen durch endliche Bitbreitenrepr¨asentiert werden Diese Bitbreiten ergeben sich aus der Mikroarchitektur des ver-wendeten Prozessors
Zum Aufstellen der Zustands¨ubergangsrelation R wird nun f¨ur jede Variable vari
der Folgezustand var i berechnet Unter der Annahme, dass es n Grundbl¨ocke gibt
und die Variable vari in den Grundbl¨ocken 1, ,m Werte zugewiesen bekommt (m ≤ n) und in den Bl¨ocken m + 1, ,n nicht geschrieben wird, kann dies durch
folgenden Ausdruck erfolgen:
abge-ein solches Vorgehen, wie das folgende Beispiel aus [140] zeigt
Beispiel 7.3.7 Abbildung 7.24a) zeigt den Kontrollflussgraphen eines Programms.
Jeder Knoten entspricht einem Basisblock und die Kanten zeigen den m¨oglichenKontrollfluss an, wobei die Bedingungen nicht dargestellt sind In Abb 7.24b) ist
der 6-fach abgerollte Kontrollflussgraph zu sehen Man beachte, dass v1lediglich in
Schritt 0 zu erreichen ist Weiterhin ist v2nur zu den Zeitschritten 1,3,5 erreichbar Zustandsraumreduktion durch Schleifenabwicklung
In Beispiel 7.3.7 wurde gezeigt, dass k-faches Abrollen des Kontrollflussgraphen in
einem Zustandsraum resultiert, der viele unerreichbare Zust¨ande enth¨alt Eine
wich-tige Beobachtung in diesem Zusammenhang ist, dass v1, v4und v5auf jedem Pfadmaximal einmal durchlaufen werden Dennoch sind in Abb 7.24b) drei erreichbare
Instanzen von v4und v5zu sehen Eine solche Redundanz kann verhindert werden.Die grundlegende Idee ist, dass nicht die gesamte ¨Ubergangsrelation abgerollt,sondern Schleifen separat abgewickelt werden Syntaktisch bedeutet dies, dass derSchleifenrumpf repliziert wird und der urspr¨ungliche Kontrollfluss durch geeigneteW¨achterfunktionen erhalten bleibt
Beispiel 7.3.8 Gegeben ist die folgende Schleife:
Trang 12k k
a)
1 0
v5
v3 v2 v1
v1
v5 v3
v1
v5 v3
v1
Abb 7.24 a) Kontrollflussgraph, b) f¨ur k= 6 abgerollter Kontrollflussgraph und c)
Kontroll-flussgraph mit abgewickelter Schleife [140]
Schlei-len von einem Zeitschritt, dass der Zustand v4erreichbar ist, w¨ahrend in Abb 7.24c)hierzu f¨unf Zeitschritte ben¨otigt werden
7.3.3 Modellpr ¨ufung durch Abstraktionsverfeinerung
Die Unterscheidung von statischer Programmanalyse und Modellpr¨ufung ist torisch entstanden W¨ahrend die statische Programmanalyse von Beginn an die
his-¨
Uberpr¨ufung einfacher Tatsachen in Programmen zum Ziel hatte, wurden pr¨ufungsverfahren zur ¨Uberpr¨ufung temporallogischer Aussagen auf endlichen Au-tomaten entwickelt Aufgrund der praktischeren Zielsetzung entwickelten sich dieMethoden zur statischen Programmanalyse schnell in Richtung Abstraktionstechni-ken Dahingegen blieb die Modellpr¨ufung jahrelang eine Technik, die exakte Ergeb-nisse ohne ¨Uberapproximationen liefert Mittlerweile unterst¨utzen Werkzeuge zur
Trang 13Modell-statischen Programmanalyse auch komplexe Formeln als Spezifikation zu pr¨ufenderfunktionaler Eigenschaften und Verfahren zur Modellpr¨ufung von Programmen inte-grieren Abstraktionstechniken Somit verschwindet die Grenze zwischen statischerProgrammanalyse und Modellpr¨ufung zunehmend.
F¨ur die SAT-basierte Modellpr¨ufung von Programmen muss das Programmzun¨achst in eine Boolesche Formel ¨ubersetzt werden Dies kann wie im vorheri-gen Abschnitt 7.3.2 vorgenommen werden, wobei der Programmz¨ahler, die globa-len Variablen und die Speicherbelegung den Zustand des Programms bilden Da derZustandsraum eines Programms durch die Verwendung von komplexen Datentypensehr groß sein kann, muss man typischerweise bei der Erstellung des Programm-modells davon abstrahieren Die heutzutage wichtigste Technik daf¨ur ist die sog
Pr¨adikatenabstraktion Die Wahl geeigneter Pr¨adikate ist bei der tion die zentrale Herausforderung Ein Verfahren, welches auf Abstraktionsverfeine- rung beruht, wurde von Clarke et al [100] vorgeschlagen und ist unter dem Namen CEGAR (engl CounterExample-Guided Abstraction Refinement) bekannt geworden.
Pr¨adikatenabstrak-Die prinzipielle Idee des Verfahrens ist in Abb 7.25 zu sehen
C-Programm
zul¨assig?
Ja Nein
Eigenschaft erf¨ullt
berichte Gegenbeispiel
fehlerfrei?
Ja
Nein Abstraktion Modellpr¨ufung
Simulation
Verfeinerung
Abb 7.25 Eine durch Gegenbeispiele gesteuerte Abstraktionsverfeinerung
Die Abstraktionsverfeinerung wird dabei durch Gegenbeispiele, die ein pr¨ufungsverfahren liefert, gesteuert Dabei wird in einem ersten Schritt eine Pr¨adi-katenabstraktion des C-Programms bestimmt Anschließend wird das Modell dahin-gehend ¨uberpr¨uft, ob es die zu pr¨ufende Eigenschaft erf¨ullt Ist dies der Fall, ist dieModellpr¨ufung erfolgreich abgeschlossen, d h das C-Programm erf¨ullt die gefor-derte Eigenschaft Liefert die Modellpr¨ufung jedoch ein negatives Ergebnis, musszun¨achst gepr¨uft werden, ob das gefundene Gegenbeispiel ¨uberhaupt einem zul¨assi-gen Berechnungspfad im C-Programm entspricht Dies ist notwendig, da durch die
Trang 14Uberapproximation falschnegative Ergebnisse entstehen k¨onnen Die ¨Uberpr¨ufungkann beispielsweise durch die Simulation des Gegenbeispiels mit dem Programmerfolgen Ist das gefundene Gegenbeispiel zul¨assig, kann es mit dem C-Programmreproduziert werden Somit wurde ein echtes Gegenbeispiel gefunden und das C-Programm erf¨ullt die geforderte Eigenschaft nicht Ist das Gegenbeispiel hingegennicht zul¨assig, muss verhindert werden, dass die Modellpr¨ufung in einer weiterenIteration den gleichen Fehler im abstrakten Modell findet, der ja im konkreten Pro-gramm nicht vorliegt Dies erfolgt dadurch, dass die ¨Uberapproximation verfeinertwird
Die Abstraktionsverfeinerung im CEGAR-Ansatz l¨auft also iterativ in vier ten ab: Abstraktion, Modellpr¨ufung, Simulation und Verfeinerung Diese Schrittewerden im Folgenden n¨aher diskutiert (siehe auch [140]):
Schrit-Abstraktion
C-Programme bestehen aus Sequenzen von Anweisungen i1,i2, Durch die Ausf¨uhrung einer Instruktion i wird der momentane Programmzustand in einen Fol- gezustand ¨uberf¨uhrt Dies wird durch die Relation Ribeschrieben, die f¨ur Programm-
zust¨ande Folgezust¨ande, die aus der Ausf¨uhrung von i resultieren, bestimmt Die Vereinigung aller Relationen Riist die ¨Ubergangsrelation R des Programms, welche
die Grundlage f¨ur die Modellpr¨ufung bildet
Da f¨ur reale Programme die ¨Ubergangsrelation R zu komplex werden w¨urde,
muss zun¨achst eine Abstraktion ˆR bestimmt werden Dies kann mittels
Pr¨adikaten-abstraktion erfolgen, wobei Pr¨adikate ¨uber Programmvariablen gebildet werden
Da-bei bipartitioniert ein Pr¨adikat p die Zustandsmenge des Programms in Zust¨ande, in denen p = T ist, und Zust¨ande, in denen p = F gilt Durch Verwendung mehrerer
Pr¨adikate werden die Partitionen weiter unterteilt Jede Partition bildet dann einen
abstrakten Zustand Werden also n Pr¨adikate gebildet, so wird der Zustandsraum
in 2nabstrakte Zust¨ande partitioniert Jeder abstrakte Zustand entspricht dann einerder 2nm¨oglichen Variablenbelegungen Dieses Vorgehen entspricht dem der funktio-nalen ¨Aquivalenzklassenbildung zur funktionsorientierten Testfallgenerierung (sieheAbschnitt 7.2.1)
Seien ˆs0und ˆs1abstrakte Zust¨ande Es gilt dann: Es gibt einen Zustands¨ubergang
in ˆR, wenn es konkrete Zust¨ande s0und s1in den zugeh¨origen Partitionen gibt, diedurch einen Zustands¨ubergang miteinander verbunden sind, d h
(ˆs0, ˆs1) ∈ ˆR ⇒ ∃s0∈ ˆs0,s1 ∈ ˆs1:(s0,s1) ∈ R.
Dies wird als existentielle Abstraktion bezeichnet, die korrekte Abstraktionen f¨ur
Er-reichbarkeitsanalysen liefert [101] Das resultierende abstrakte Programm, welchessich aus ˆR ergibt, wird durch ein sog Boolesches Programm repr¨asentiert [27] Die-
ses Boolesche Programm besitzt die selben Kontrollanweisungen wie das originaleC-Programm, allerdings sind alle verwendeten Variablen nun bin¨ar
Beispiel 7.3.9 Betrachtet wird ein Programm ¨ahnlich wie in Beispiel 7.3.2 In Zeile
4 ist das Inkrement um 2 durch ein Inkrement um 1 ersetzt Das neue Programm sieht
Trang 15somit wie folgt aus:
Abb 7.26 Kontroll-Datenflussgraph f¨ur das Programm aus Beispiel 7.3.9
Pr¨adikatenabstraktion f¨ur diesen Programm l¨asst sich durch das Pr¨adikat i= 0
erreichen Der Wert des Pr¨adikates wird in der bin¨aren Variablen b1∈ B
repr¨asen-tiert Abb 7.27a) zeigt den Kontroll-Datenflussgraphen f¨ur das resultierende
Boo-lesche Programm Zun¨achst wird im Datenflussgraphen im Zustand v1die
Zuwei-sung b1:= T vorgenommen, was dem Pr¨adikatenwert entsprechend der Zuweisung
i = 0 im C-Programm entspricht Die Bedingungen der Verzweigung im Zustand
v2m¨ussen nun auch ausschließlich mit der Variablen b1erfolgen Da der ¨Ubergang
in den Fehlerzustand ve nur eintreten kann, wenn i > 10 ist, muss in diesem Fall b1
zwingend gleichF sein Der Fall i ≤ 10 schließt auch i = 0 ein, was zur Folge hat,
dass die Bedingung zur KonstantenT wird, also ¨uberhaupt nicht von b1abh¨angt Die
Pr¨adikatenabstraktion f¨ur die Konditionale in der Verzweigung in Zustand v4werdenauf die gleiche Art bestimmt
Die Berechnung des Inkrements im Zustand v3 muss nun ebenfalls nur mit b1
erfolgen Besitzt die Variable i bei Erreichen der Inkrement-Anweisung den Wert 0