Note that the name of a state machine
is simply the name of its initial state. Also note that the transitions
of the state machine are labelled with
actions.Such
state machines are also known as labelled transition systems (LTSs).
The transition from the initial state (S1) is labelled by an action
call_foo. This action encapsulates
the externally observable event of library routine
foo being invoked. Such actions,
with only names, are also called
basic
actions.
Back to Top
Return actions
As anyone familiar with the FSP will realize, we extend the FSP
notation to express a special class of actions called
return actions. Return actions are
of the form
return {expression} or
return {} where the former
expresses the return of an integer value and the latter expresses
returning a
void value. In a
return action of the form
return
{expression}, the
expression
represents
a condition satisfied by the return value. The return value itself is
represented by the dummy variable
$0.For
instance, the action
return {$0 <
5} represents the return of an integer value less than 5.
Note: The notation for return
actions in version 1.0 is a
major
departure from version 0.1. In version 0.1 we could write
return {10} to specify the return
of 10 but there was no way to specify the return of a value less that
10. In version 1.0 this can be achieved simply by
return {$0 < 10}.
Back to Top
Procedure
Block
We are now ready to express the fact that
S1 specifies the behavior of
my_proc when the first argument of
my_proc is equal to zero. In
MAGIC, this can be achieved by the following
procedure block:
cproc
my_proc {
abstract { abs_1 , ($1 == 0) , S1 };
}
Note the keywords
cproc and
abstract. The block keyword
cproc indicates that we are going
to say something about a C procedure. It is followed by the name of the
procedure and then by a set of statements enclosed within a pair of
curly braces. Each such statement typically consists of an
statement keyword followed by other
terms. The procedure whose name follows
cproc is often referred to as the
scope procedure.
One such statement keyword is
abstract.This
keyword indicates that we are expressing an abstraction relation
between the scope procedure and an LTS. Note the guard
($1 == 0) where
$1 refers to the first argument.
In general
$i can be used to
refer to the
i-th argument
of
the scope procedure. Finally note that the abstraction statement has a
name,
abs_1. For procedure
blocks, the abstraction names are just placeholders and have no special
significance. However, soon we will discuss
program blocks and for them, the
names of abstraction statements will be of crucial importance.
The following LTS expresses the behavior of
my_proc when its first argument is
not equal to zero:
In general, multiple procedure blocks
can be combined into one as long as they have the same scope procedure.
Also the order of statements within a procedure block is irrelevant.
Thus, the above two procedure blocks together is equivalent to the
following single procedure block:
MAGIC requires that the guards of
abstraction statements for any scope procedure be mutually disjoint and
complete (i.e. cover all possibilities of argument valuations). This is
necessary to enable MAGIC to unambiguously identify the applicable
abstraction in any given calling context of the scope procedure.
Back to Top
Specifying Library Routines
In order to construct a proper model for
my_proc MAGIC must know about the
behavior of the library routines called by
my_proc. Let us assume that the
actual code for
foo and
bar are unavailable. In such a
case, MAGIC requires that the user supply appropriate
abstractions for these two
routines.
In particular, suppose that
foo and
bar are respectively abstracted by
the LTSs
FOO and
BAR described below:
FOO
= ( call_foo -> return {$0 == -1} -> STOP ).
BAR = ( call_bar -> return {$0
== 50} -> STOP ).
Then the following program blocks can be used to express the relation
between
foo, bar and their
abstractions.
cproc
foo {
abstract { abs_3 , (1) , FOO };
}
cproc bar {
abstract { abs_4, (1), BAR };
}
Note that the guard in both abstraction statements is
1, which
denotes
TRUE according to C
semantics. This therefore means that under all calling contexts,
foo and
bar are abstracted by
FOO and
BAR respectively.
Also note that specifications and abstractions are syntactically
identical. This makes sense because both abstractions and
specifications
are essentially asserting the same thing viz. that under a certain
calling context, a procedure's behavior is subsumed by the behavior of
an LTS. The only difference is that the assertion made by an
abstraction
can be
assumed to be true
while
the assertion made by a specification needs to be
validated. This has at least two
significant consequences:
- Verifying Incomplete Code:
In practice, one cannot assume that the actual code for each and every
library routine used by a program will be available to the verifier.
Hence being able to provide abstractions allows MAGIC to analyze such
incomplete implementations. In effect, abstractions allow us to specify
assumptions about the environment in which a program operates.
- Compositionality: Often
programs are simply too big to be analyzed as one chunk of code.
Abstractions allow us to decompose such large implementations into
smaller, more manageable fragments. Fragments can be verified one at a
time. While verifying one fragment, the abstractions of other fragments
can be used as assumptions. The fact that specifications and
abstractions are identical implies that they can naturally switch from
one role to the other depending on which fragment is being verified.
Back to Top
Program Block
It is now time to specify the entire program that we want to verify. In
our case the program is sequential, i.e. it has a single component
consisting of the procedure
my_proc.
The
following
program block expresses
the relation between our program and its specification:
cprog my_prog
= my_proc {
abstract abs_5, {($1 == 0)}, S1;
abstract abs_6, {($1 != 0)}, S3;
}
This looks a lot like a procedure block but there are some
crucial differences. First, it begins with the keyword
cprog and not
cproc. This is followed by the
name of the program (which is again a placeholder and does not serve
any other purpose), an equal to sign and then a list of
procedure names. Intuitively these
are the names of the procedures which execute in parallel and
constitute
the program. In the above block this list has a single procedure name
viz.
my_proc, signifying
that
our program has just one component that executes
my_proc.
Following the list of procedure names we have a sequence of
statements enclosed within curly braces. Just like procedure blocks,
abstraction statements are used to provide specifications. But
abstraction statements for program blocks are syntactically different.
They do begin with the
abstract keyword,
but the rest of it them is not enclosed within curly braces. Instead
there are three components. The first is the name of the abstraction
statement. This is used by MAGIC to identify the target abstraction to
be validated. The second is a
list
of
guards, one for each component of the program. Each guard in
the
list expresses the beginning state of the corresponding component. In
the above block, the list has just one element that expresses the
starting context of
my_proc. Note
that the list of guards is enclosed within curly braces. The third and
final component is the name of the LTS which specifies the program.
Back to Top
Comments
You can use either C-style or C++ style comments in specification files.
/* this is a comment */
// so is this one
Back to Top
Running MAGIC
We are now ready to try out MAGIC. First save the C program in a file
whose name must end with ".pp", say
my_proc.pp. Next save the specifications
in another file whose name ends
with ".spec", for example
my_spec-1.0.spec. Finally run
MAGIC:
$
magic --abstraction abs_5 my_proc.pp my_spec-1.0.spec --optPred
MAGIC will try to validate the abstraction statement with name
abs_5. The
--optPred options tells MAGIC to
perform counterexample guided abstraction refinement with predicate
minimization. It is usually a good idea to always use this option. For
details on other options that MAGIC can accept, look at the user's
manual. If all goes well, MAGIC should be
able to successfully verify the abstraction and produce an output that
ends with something like this:
conformance
relation exists !!
abstraction
abs_5 is valid ...
Simplify
process destroyed ...
terminating
normally ...
Similarly you can try to verify
abs_6and
MAGIC should be able to do it. If you look again at
my_spec-1.0.spec
you
will notice that we have added two more abstraction statements,
abs_7 and
abs_8, to the
my_prog block. They are similar to
abs_5 and
abs_6 except that the guard
conditions have been switched. Clearly they are invalid specifications
and MAGIC should be able to detect this. Try verifying
abs_7 by typing the following:
$
magic --abstraction abs_7 my_proc.pp my_spec-1.0.spec --optPred
--ceShowAct
MAGIC should tell you that this is an invalid specification and further
provide you with a counterexample. The output should look something
like
the following:
*******************************************
branch ( P0::x == 0 ) : [P0::x == 0] : TRUE
############ P0::epsilon ############
P0::y = foo ( ) : []
############ P0::epsilon ############
P0::y = foo ( ) : []
############ call_foo ############
P0::y = foo ( ) : []
############ {P0::y = [ -1 ]} ############
branch ( P0::y > 0 ) : [] : TRUE
############ P0::epsilon ############
return ( 10 ) : []
############ return { ! } ############
*******************************************
CE dag projections analysed ...
conformance relation does not exist !!
abstraction abs_7 is invalid ...
Simplify process destroyed ...
terminating normally ...
Back to Top
A
Concurrent Example
Let us now verify a concurrent program. Our concurrent program will be
very simple. It will be two copies of
my_proc executing
in parallel. This is easy to understand because the resulting parallel
program should behave exactly like a single copy of
my_proc (since our notion of
parallel composition is idempotent). All we need to do is create a new
program block specifying our example. Here is a sample
my_conc-1.0.spec.Notice
that it has four abstraction statements
abs_9, abs_10, abs_11 and
abs_12 out of which the first two
are valid while the last two are invalid
.We
can try to verify
abs_9 by
the following command:
$
magic --abstraction abs_9 my_proc.pp my_conc-1.0.spec --optPred
This should succeed. Likewise MAGIC should be able to prove that
abs_10 is also valid while
abs_11 and
abs_12 are both invalid.
Back to Top
Other
Keywords
In addition to
abstract, there
are several other keywords that can be used in procedure blocks for
performing specific tasks. In this section we mention a few important
ones.
Back to Top
Supplying predicates
The user can manually supply predicates to guide MAGIC's predicate
abstraction. Often this is useful when MAGIC fails to discover a
satisfactory set of predicates in a reasonable amount of time.
Predicates are supplied in a per-procedure basis. An important feature
of MAGIC is that all user-supplied predicates for a procedure
proc must be syntactically
equivalent to some branch condition in
proc.Otherwise
that predicate will be simply ignored by MAGIC. For example consider
the following C procedure:
int
proc()
{
int x = 5;
if(x < 10) return -1;
else return 0;
}
Suppose we want to prove using MAGIC that
proc is correctly specified by the
following LTS:
PROC
= ( return {$0 == -1} -> STOP ).
Normally we would do this by simply asking MAGIC to perform
automated abstraction refinement (using the
--optPred option). However suppose
we have a good idea about which predicate MAGIC will need to complete
successfully. For example, in this case
(x < 10) is the required
predicate (note that this corresponds to a branch condition in
proc). Then we can simply tell
MAGIC to use this predicate by using the
predicate keyword. The following
procedure block shows how to do this:
cproc
proc {
predicate (x < 10);
}
MAGIC will look for branch statements in
proc which have a branch condition
(x < 10). If it finds any
such
branch, it will use the corresponding branch condition as a
seed predicate. Otherwise it will
ignore the user supplied predicate. Multiple predicates can be supplied
in one statement using a comma-separated list or they can be supplied
via multiple predicate statements. Also the order in which predicates
are supplied is irrelevant. For example the two following procedure
blocks each have the same effect as the procedure block above:
cproc
proc {
predicate (y == 10) , (w == 5) , (z +w > 20) , (x < 10) , (x+y !=
5);
}
cproc proc {
predicate (x+y != 5);
predicate (z+w > 20) , (y == 10);
predicate (x < 10) , (w == 5);
}
Back to Top
Inlining Procedures
Suppose procedure
foo calls
procedure
bar. Normally
MAGIC
will
not inline
bar within
foo even if the code for
bar is available. It has to be
told explicitly to do this via the
inline keyword.
Here's a procedure block that demonstrates how to do this. Once again
inlining has to be done on a procedure-to-procedure basis. For example
the following procedure block will not cause
bar to be inlined within some
other procedure
baz.
cproc
foo {
inline bar;
}
Back to Top
Drawing
with MAGIC
MAGIC can be used to output control flow graphs, LTSs and intermediate
data structures as postscript files. This is very useful for
visualization and understanding. For example, using the following
command line on these
draw.pp and
draw-1.0.spec files produces this
draw-1.0.ps file.
$
magic --abstraction abs_1 draw.pp draw-1.0.spec --optPred
--drawPredAbsLTS --drawFile draw-1.0.ps
Also,
using the following command line on
my_proc.pp
and
my_spec-1.0.spec yields this
my_proc-1.0.ps.
$ magic --abstraction abs_5 my_proc.pp my_spec-1.0.spec --optPred
--drawPredAbsLTS --drawFile my_proc-1.0.ps
Please look at the user's
manual for
more
details on command line options that control MAGIC's drawing
capabilities. Also
note that
in order to draw its figures MAGIC
requires the
Graphviz
package, and in particular the
DOT
tool. However, if you do not want to use MAGIC's drawing capabilities,
there is no need to install Graphviz.
Back to Top
Questions and Comments
At this point, you should be more or less familiar with MAGIC. However
I am sure there will be many questions and suggestions. Please feel
free
to
email me and I will do my
best
to respond promptly and correctly. Have fun with MAGIC !!
Back to Top