Analisi e Verifica di Programmi
Laboratorio di AVP
Corso di Laurea in Informatica
AA 2004-’05
Tino Cortesi
Analisi e Verifica
Cosa
Individuare proprietà interessanti dei nostri programmi:
Valori prodotti, dipendenze, uso, terminazione
Perché:
Verifica, Debugging
Type checking
Verifica di asserzioni
Ottimizzazioni:
Compile time garbage collection
Ottimizzazioni source-to-source
Come
Le proprietà non banali dei programmi sono indecidibili o
computazionalmente molto costose (NP)
Approssimazioni o condizioni sufficienti
Tino Cortesi
Analisi e Verifica di Programmi
2
Guyana francese, 4 giugno 1996
$600 miliardi di dollari bruciati per
un errore del software
Tino Cortesi
Analisi e Verifica di Programmi
3
Marte, 3 dicembre 1999
Crashed due to uninitialized
Tino Cortesi
Analisi e Verifica di Programmi
variable
4
Sillabo del corso
Introduzione
Interpretazione Astratta
Correttezza dell’analisi - Connessioni di Galois - Widening
Domini astratti per l’analisi di linguaggi dichiarativi
Domini astratti per l’analisi di linguaggi ad oggetti
Dataflow Analysis
Liveness Analysis
Reaching Definitions - Constant Folding - Available Expressions
Basic Blocks - Value Numbering - Alias Analysis
Loop Optimizations
Model Checking
Type Systems
Tino Cortesi
Analisi e Verifica di Programmi
5
Bibliografia
F.Nielson, H.R.Nielson, C. Hankin
Principles of Static Analysis
Springer Verlag, 1999
A. W. Appel
Modern Compiler Implementation in Java
Cambridge Univ. Press, 1998
Steven Mucknick
Compiler Design Implementation
Morgan Kaufmann Publ., 1997
Articoli su journals o proceedings di conferenze
Tino Cortesi
Analisi e Verifica di Programmi
6
Introduzione
Analisi
Metodo conoscitivo che procede dall’individuazione e
dallo studio dei particolari
Scomposizione di un tutto organico nelle sue parti
Tino Cortesi
Analisi e Verifica di Programmi
8
Analisi di Programmi
Tecniche automatiche per inferire a tempo di
compilazione informazione approssimata sul
comportamento run-time dei programmi
Applicazioni:
Ottimizzazione di compilatori
Verifica automatica dei programmi
Certificazione - validazione del software
Sicurezza
Tino Cortesi
Analisi e Verifica di Programmi
9
Etichettatura
input n;
m:=2;
while n>1 do
m:= m * n;
n:= n-1;
output m;
Tino Cortesi
(1) input n;
(2) m:=2;
while (3) n>1 do
(4) m:= m * n;
(5) n:= n-1;
(6) output m;
Analisi e Verifica di Programmi
10
Grafo di flusso
1
input n
(1) input n;
(2) m:=2;
while (3) n>1 do
(4) m:= m * n;
(5) n:= n-1;
(6) output m;
2
m:= 2
3
n>1
4
m:= m*n
5
n:= n-1
Tino Cortesi
Analisi e Verifica di Programmi
6
output m
11
Possiamo inferire mediante analisi statica
che al punto (6) il valore della variabile m
sarà pari per qualsiasi valore di input n.
1
input n
2
Per fare questo l’analisi dovrà propagare
“parity information”
m:= 2
3
Possiamo assegnare ad ogni variabile uno
dei tre “valori”:
Pari
il valore è
sicuramente pari
Dispari
il valore è
sicuramente dispari
Non_So
il valore può essere
pari oppure dispari
Tino Cortesi
Analisi e Verifica di Programmi
n>1
4
m:= m*n
5
n:= n-1
6
output m
12
(1) n Non_So
(1) input n;
(2)
(2) m:= 2;
while (3) n>1 do
(3)
(4) m:= m * n;
(4)
(5) n:= n - 1;
(5)
(6) output m;
(6)
Tino Cortesi
Analisi e Verifica di Programmi
m
n
m
n
m
n
m
n
m
n
m
Non_So
Non_So
Non_So
Non_So
Pari
Non_So
Pari
Non_So
Pari
Non_So
Pari
13
Questo programma, dato un
intero positivo n, calcola il
doppio del fattoriale: 2 * (n!)
(1) input n;
(2) m:= 2;
while (3) n>1 do
(4) m:= m * n;
n=1
n=2
n=3
n=4
(5) n:= n - 1;
(6) output m;
…
…
…
…
l’output
l’output
l’output
l’output
è
è
è
è
2
4
12
48
pari!
Tino Cortesi
Analisi e Verifica di Programmi
14
(1) input n;
(2) m:= 1;
while (3) n>1 do
(4) m:= m * n;
(5) n:= n - 1;
(6) output m;
Tino Cortesi
Questo programma, dato un
intero positivo n, calcola il
fattoriale: n!
In questo caso l’analisi
precedente non restituisce
nessuna informazione utile sulla
parità di m.
Questo è corretto (per n=1…)
Ma nemmeno se restringiamo
l’input a n pari e n>1 l’analisi è
precisa: in (3) sia n che m sono
Non_So.
Analisi e Verifica di Programmi
15
Precisione e Correttezza
Questa perdita di precisione è una caratteristica comune delle
tecniche di analisi statica
La maggior parte delle proprietà sui programmi che si vogliono
inferire sono indecidibili (o NP-complete), quindi non possiamo
pensare di inferirle con precisione (ed in tempi ragionevoli)
Dobbiamo assicurarci che il risultato dell’analisi sia in ogni caso
corretto:
Se l’analisi dice SI allora sicuramente la proprietà è verificata
Se l’analisi dice NO allora può darsi che la proprietà non sia
verificata
Tino Cortesi
Analisi e Verifica di Programmi
16
Esempio
Sappiamo che il problema della terminazione è
indecidibile (halting problem)
programmi
programmi che
terminano
Tino Cortesi
Analisi e Verifica di Programmi
17
programmi che
potrebbero terminare
programmi che
sicuramente terminano
programmi
programmi che
terminano
Tino Cortesi
Analisi e Verifica di Programmi
18
Precisione e Correttezza
Un’analisi di terminazione può essere quindi di due
tipi:
“Definite”
SI
il programma sicuramente termina
NO
il programma potrebbe non terminare
“Possible”
SI
il programma può terminare
NO
il programma sicuramente non termina
Tino Cortesi
Analisi e Verifica di Programmi
19
Precisione e Correttezza
Le tecniche di analisi che introdurremo si dicono
“semantics-based”.
Questo significa che l’informazione ottenuta
dall’analisi può essere dimostrata corretta rispetto
alla semantica del linguaggio di programmazione
Tino Cortesi
Analisi e Verifica di Programmi
20
Compilatori ottimizzanti
Un compilatore ottimizzante trasforma un programma
per migliorare la sua efficienza senza modificarne
l’output.
Trasformazioni che migliorano l’efficienza:
Allocazione dei registri
Eliminazione delle sotto-espressioni comuni: se una
espressione viene calcolata più di una volta, elimina le
ripetizioni
Eliminazione di “dead-code”: cancella una computazione il
cui risultato non viene mai usato
“Constant folding”: se gli operandi di un’espressione sono
costanti, il calcolo può essere fatto a tempo di compilazione.
Tino Cortesi
Analisi e Verifica di Programmi
21
Escape Analysis
Consideriamo queste due procedure in C:
typedef int A[10000];
typedef A* B;
void esegui1(){
int i;
B b;
b = (A*) malloc(sizeof(A));
for (i=0; i<100; i++)
(*b)[i]=0;
free(b);
}
Tino Cortesi
Analisi e Verifica di Programmi
void esegui2(){
int i;
A a;
for (i=0; i<100; i++)
a[i]=0;
}
22
ambiente
typedef int A[10000];
typedef A* B;
void esegui1(){
int i;
B b;
b = (A*) malloc(sizeof(A));
for (i=0; i<100; i++)
(*b)[i]=0;
free(b);
}
int
i
B
b
memoria
stack
heap
0
L’array viene allocato nello heap e poi eliminato...
Tino Cortesi
Analisi e Verifica di Programmi
23
ambiente
typedef int A[10000];
int
i
A
a
memoria
stack
heap
0
void esegui2(){
int i;
A a;
for (i=0; i<100; i++)
a[i]=0;
}
L’array viene allocato nello stack e poi eliminato...
Tino Cortesi
Analisi e Verifica di Programmi
24
allocare nello heap costa...
for (i=0; i<1000000; i++)
esegui1();
// circa 90 sec
for (i=0; i<1000000; i++)
esegui2();
// circa 35 sec
I programmi “sembrano” uguali, ma l’allocazione nello heap fa sì che il
primo sia tre volte più lento del secondo...
Se riesco a “predire” che una variabile dinamica “non esce” dalla
procedura dov’è introdotta, posso sostituirla con una variabile locale.
Tino Cortesi
Analisi e Verifica di Programmi
25
Esiste il compilatore ottimo?
Definiamo “fully optimizing compiler” un compilatore che
trasforma un programma P nel programma Opt(P) che è il più
piccolo programma con lo stesso output di P.
Dato un programma Q che non produce nessun output e non
termina, Opt(Q) è facile da trovare: è il programma L1: goto L1;
Supponiamo di avere un fully optimizing compiler. Potremmo
usarlo per risolvere il problema della terminazione:
per vedere se esiste un input per il quale P termina, basta
vedere se Opt(P) è il programma L1: goto L1;
Ma sappiamo che il problema della terminazione è indecidibile,
ovvero che non esiste nessun algoritmo che lo risolve. Quindi
non può esistere nemmeno un fully optimizing compiler.
Tino Cortesi
Analisi e Verifica di Programmi
26
Ottimizzazione del codice
Idea: anziché chiedere al programmatore di preoccuparsi
dell’efficienza del programma, si può demandare a un
compilatore ottimizzanti la trasformazione di un codice
inefficiente in uno (equivalente) con performances migliori
Control-Flow
Analysis
Tino Cortesi
Analisi
Analisi e Verifica di Programmi
Trasformazioni
27
Trasformazioni ottimizzanti
Le trasformazioni che un compilatore ottimizzante può operare
seguono principalmente la seguente metodologia:
Analisi: attraversa il grafo di flusso del programma e
raccogli informazioni su cosa può succedere a tempo si
esecuzione
Trasformazioni: modifica il programma per renderlo in
qualche modo più veloce. L’informazione ottenuta dall’analisi
garantisce che il risultato del nuovo programma è uguale a
quello del programma originario
Tino Cortesi
Analisi e Verifica di Programmi
28
Tecniche di Analisi Statica
Per definire un’analisi statica dobbiamo preoccuparci
di:
Come specificarla
Come implementarla efficientemente
Come provarne la correttezza
Tino Cortesi
Analisi e Verifica di Programmi
29
Principali tecniche
Abstract Interpretation
Valutazione Parziale
Dataflow Analysis
Control Flow Analysis
Model Checking
Type Systems
Tino Cortesi
Analisi e Verifica di Programmi
30
Scarica

Analisi e Verifica di Programmi