Trace and collecting semantics

Trace and collecting semantics
Program Analysis and Synthesis 2016, ETH Zürich
March 7, 2017
1 Basics
In this note we shall discuss both the trace and the collecting semantics in more detail.
Our starting point is a control-flow graph G = (V, E) of some program P , where
1. V ⊆ Label is a set of labels;
2. E ⊆ Label × Action × Label is a set of arcs labeled by actions.
Each action f ∈ Action has an interpretation as a concrete transformer, i.e., a relation
from program stores to program stores:
Jf KStore ⊆ Store × Store.
An action corresponds to the execution of one statement in the given program, and its
interpretation simply determines the next stores that the program can transition to.
Recall that the program store is simply a map from variables to concrete values:
Store = Var → Value.
Example 1. Figure 1 shows a program together with its control-flow graph. There are
ten actions that appear in the graph. Their interpretation is:
Ji := 0KStore
Jj := 0KStore
JtrueKStore
J¬trueKStore
Ji > 0KStore
J¬(i > 0)KStore
Ji := i - 1KStore
Jj := j + 1KStore
Ji := i + j + 1KStore
JgotoKStore
=
=
=
=
=
=
=
=
=
=
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
{(σ, σ 0 ) | σ 0
1
= σ[i 7→ 0]}
= σ[j 7→ 0]}
= σ, σ = σ}
= σ, σ 6= σ}
= σ, σ(i) > 0}
= σ, σ(i) ≤ 0}
= σ[i 7→ σ(i) − 1]}
= σ[j 7→ σ(j) + 1]}
= σ[i 7→ σ(i) + σ(j) + 1]}
= σ}
1
2
3
4
5
6
7
8
9
10
11
i := 0
j := 0
while ( true )
i f ( i > 0)
i := i −
j := j +
} else {
i := i +
j := 0
}
}
{
{
1
1
j + 1
1
i := 0
2
j := 0
3
¬true
exit
true
4
¬(i > 0)
i > 0
5
goto
8
i := i + j + 1
i := i - 1
6
9
j := j + 1
j := 0
7
10
goto
goto
11
Figure 1: A simple program together with its control-flow graph.
2
2 Trace semantics
The trace semantics of a program models its possible executions. We can represent the
program’s state during execution as a pair (l, σ), where l is label pointing to the next
statement to be executed, while σ is the current program store. A trace is simply a
sequence of program states (not necessarily corresponding to a legal execution). In our
discussion we will focus on finite executions only, and therefore we define:
State = Label × Store
Trace = State∗
It would be useful to split the definition of the trace semantics into parts. Let ι be the
initial program label, i.e., the one without incoming arcs in G = (V, E).
1. The initT set defines which are the possible initial states. For concreteness, we
choose to initialize all variables arbitrarily but other choices are also possible.
initT = {(ι, σ) ∈ Trace | σ ∈ Store} ∪ {ε}
2. The stepT function performs all the possible transitions from the last state of every
trace in a given set T by applying the corresponding transformers.
[
stepT (T ) = {τ · (l, σ) · (l0 , σ 0 ) | τ · (l, σ) ∈ T, (l, f, l0 ) ∈ E, (σ, σ 0 ) ∈ Jf KStore }
3. The monotone map F T : P(Trace) → P(Trace) incorporates the definition of initial
states and the possible transitions into a single object:
F T (T ) = initT ∪ stepT (T ).
By a theorem of Knaster and Tarski [2] F T has a least fixed-point lfp(F T ), which we
declare to be the trace semantics of P . If we start iterating from the empty set:
∅ ⊆ F T (∅) ⊆ F T (F T (∅)) ⊆ F T (F T (F T (∅))) ⊆ . . . ,
then we would perform a breadth-first exploration of all the possible states that the
program can reach from an initial state. Evidently, the set of traces obtained during
this exploration is a fixed-point of F T , and therefore it equals the trace semantics.
Observe that initT and stepT are defined in a way such that the trace semantics is
prefix-closed: if any trace τ · υ belongs to lfp(F T ), then so does its prefix τ .
3
Example 2. Let us compute the trace semantics of the program in Figure 1. Iterating
from the empty set we obtain the sequence:
FT
F
0
T 1
(∅) = ∅
(∅) = (F T )0 (∅) ∪ {(1, σ) | σ ∈ Store} = initT
FT
2
(∅) = (F T )1 (∅) ∪ {(1, σ) · (2, σ[i 7→ 0]) | σ ∈ Store}
FT
3
(∅) = (F T )2 (∅) ∪ {(1, σ) · (2, σ[i 7→ 0]) · (3, σ[i 7→ 0, j 7→ 0]) | σ ∈ Store}
FT
4
(∅) = (F T )3 (∅) ∪ {(1, σ) · (2, σ[i 7→ 0]) · (3, σ[i 7→ 0, j 7→ 0]) · (4, σ[i 7→ 0, j 7→ 0]) | σ ∈ Store}
...
Because our program is deterministic and non-terminating, except for the empty trace
its trace semantics equals the prefix closure of the set of infinite sequences of the form:
( 1, σ)
( 2, σ[i 7→ 0])
( 3, σ[i 7→ 0, j
( 4, σ[i 7→ 0, j
( 8, σ[i 7→ 1, j
( 9, σ[i 7→ 1, j
(10, σ[i 7→ 1, j
(11, σ[i 7→ 1, j
7 0])
→
7→ 0])
7→ 0])
7→ 0])
7→ 0])
7→ 0])
( 3, σ[i 7→ 1, j
( 4, σ[i 7→ 1, j
( 5, σ[i 7→ 1, j
( 6, σ[i 7→ 0, j
( 7, σ[i 7→ 0, j
(11, σ[i 7→ 0, j
7 0])
→
7→ 0])
7→ 0])
7→ 0])
7→ 1])
7→ 1])
( 3, σ[i 7→ 0, j
( 4, σ[i 7→ 0, j
( 8, σ[i 7→ 0, j
( 9, σ[i 7→ 2, j
(10, σ[i 7→ 2, j
(11, σ[i 7→ 2, j
7→ 1]) ( 3, σ[i 7→ 2, j 7→ 0])
7→ 1]) . . .
7→ 1])
7→ 1])
7→ 0])
7→ 0])
3 Collecting semantics
The trace semantics of a program keeps temporal information about executions: a trace
records the order in which the program states are reached during execution. Quite often,
however, we are not interested in any temporal information but only in the invariants
that the program has at each label. This is the case, for example, when we reason about
assertions. The idea behind the collecting semantics of a program is to abstract away
any temporal information and keep only the program invariants at each label.
More formally, an invariant of the program P at the label l is any property I ∈ P(Store)
that it holds every time l is reached:
∀ τ · (l, σ) ∈ lfp(F T ). σ ∈ I.
Obviously, any intersection of invariants at a given label is also an invariant there, i.e.,
a most precise invariant exists. The collecting semantics of P is simply the association
between a label and the corresponding most precise invariant:
l 7→ {σ | ∃ τ · (l, σ) ∈ lfp(F C )}.
4
This way, to prove that an assertion is a program invariant at a given label, it is sufficient
to know the collecting semantics. However, computing the collecting semantics is hard
(undecidable in general) and one is often forced to approximate it. For this goal, it
is useful to obtain it as the least fixed-point of a certain monotone map F C over the
complete lattice Label → P(Store).
In order to define F C , we shall first lift the concrete transformers to operate on P(Store):
Jf KC (S) = {σ 0 | ∃σ ∈ S. (σ, σ 0 ) ∈ Jf KStore }
The invariants that hold at the initial label ι are represented by the assignment:
(
{σ | (ι, σ) ∈ initT } if l0 = ι
initC = l0 7→
∅
otherwise
One can step into a label l0 from any of its predecessors in the graph G = (V, E):
[
Jf KC (m(l))
stepC (m) = l0 7→
(l,f,l0 )∈E
Combining the above with the join t in the lattice Label → P(Store) we obtain
F C (m) = initC t stepC (m)
We declare lfp(F C ) to be the collecting semantics of the program P .
A fixed-point iteration with F C from ⊥ again explores all the possible states that the
program can reach from an initial state. However, the temporal ordering is not recorded
and one keeps track only if a given store σ was reached at a given label l0 :
n
n
σ ∈ F C (⊥)(l0 ) ⇐⇒ ∃τ. τ · (l0 , σ) ∈ F T (∅).
Example 3. Let us compute the collecting semantics of the program in Figure 1. We
assume that each variable ranges in the set of all naturals N. Iterating from ⊥ we obtain:
0
F C (⊥) = ⊥
1
0
F C (⊥) = F C (⊥)[ 1 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ N × N}] = initC
2
1
F C (⊥) = F C (⊥)[ 2 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {0} × N}]
3
2
F C (⊥) = F C (⊥)[ 3 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0)}}]
4
3
F C (⊥) = F C (⊥)[ 4 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0)}}]
5
4
F C (⊥) = F C (⊥)[ 8 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0)}}]
6
5
F C (⊥) = F C (⊥)[ 9 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0)}}]
7
6
F C (⊥) = F C (⊥)[10 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0)}}]
8
7
F C (⊥) = F C (⊥)[11 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0)}}]
5
8
F
C
9
F
C 10
F
C 11
F
C 12
F
C 13
F
C 14
F
C 15
F
C 16
F
C 17
F
C 18
F
C 19
F
C 20
FC
7
(⊥) = F
C
8
(⊥) = F
C
9
(⊥) = F
C 10
(⊥)[ 6 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0)}}]
(⊥) = F
C 11
(⊥)[ 7 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 1)}}]
(⊥) = F
C 12
(⊥)[11 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 1), (1, 0)}}]
(⊥) = F
C 13
(⊥)[ 3 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0), (0, 1), (1, 0)}}]
(⊥) = F
C 14
(⊥)[ 4 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0), (0, 1), (1, 0)}}]
(⊥) = F
C 15
(⊥)[ 8 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0), (0, 1)}]
(⊥) = F
C 16
(⊥)[ 9 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0), (2, 1)}]
(⊥) = F
C 17
(⊥)[10 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0), (2, 0)}]
(⊥) = F
C 18
(⊥)[11 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 1), (1, 0), (2, 0)}]
(⊥) = F
C 19
(⊥)[ 3 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 1), (1, 0), (2, 0)}]
(⊥) = F C
(⊥)[ 3 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0), (1, 0)}}]
(⊥)[ 4 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(0, 0), (1, 0)}}]
(⊥)[ 5 7→ {(i 7→ m, j 7→ n) | (m, n) ∈ {(1, 0)}}]
...
Performing the fixed-point iteration by hand is very tedious. With some ingenuity we
can predict where this process goes to (find out the most-precise invariant at each label).
The fixed-point, which is the union of all intermediate computations, equals
1 7→ {(i 7→ m, j 7→ n) | m ∈ N, n ∈ N}
2 7→ {(i 7→ m, j 7→ n) | m = 0, n ∈ N}
3 7→ {(i 7→ m, j 7→ n) | m ∈ N, n ∈ N}
4 7→ {(i 7→ m, j 7→ n) | m ∈ N, n ∈ N}
5 7→ {(i 7→ m, j 7→ n) | m 6= 0, n ∈ N}
6 7→ {(i 7→ m, j 7→ n) | m ∈ N, n ∈ N}
7 7→ {(i 7→ m, j 7→ n) | m ∈ N, n 6= 0}
8 7→ {(i 7→ m, j 7→ n) | m = 0, n ∈ N}
9 7→ {(i 7→ m, j 7→ n) | m = n + 1, n ∈ N}
10 7→ {(i 7→ m, j 7→ n) | m 6= 0, n = 0}
11 7→ {(i 7→ m, j 7→ n) | (m, n) 6= (0, 0)}
6
4 Related
We strongly advise anyone to read the source paper [1] on abstract interpretation by
Patrick and Radhia Cousot.
5 Problems
Problem 1. Find the trace and collecting semantics of the following program:
1
2
3
4
5
6
7
8
9
10
k := 0
while ( true )
i := k
j := 0
while ( i >
i := i −
j := j +
}
k := k + 1
}
{
0) {
1
1
Problem 2. Find the trace and collecting semantics of the following program:
1
2
3
4
5
6
7
8
i f (n > 0) {
while ( n /= 1 ) {
i f (n % 2 = 0)
n := n / 2
else
n := 3 ∗ n + 1
}
}
7
References
[1]
P. Cousot and R. Cousot. “Abstract interpretation: a unified lattice model for static
analysis of programs by construction or approximation of fixpoints”. In: POPL.
1977. url: http://www.di.ens.fr/~cousot/COUSOTpapers/publications.www/
CousotCousot-POPL-77-ACM-p238--252-1977.pdf.
[2]
Alfred Tarski. “A lattice-theoretical fixpoint theorem and its applications.” In: Pacific J. Math. 5.2 (1955), pp. 285–309. url: http://projecteuclid.org/euclid.
pjm/1103044538.
8