Gives a technical overview of the SPARK Examiner and the various forms of static analysis that it performs:End:
 

Praxis High Integrity Systems logo

Praxis High Integrity Systems
arrowPraxis home arrowSPARKAda home arrowSPARK Language and Toolset arrowThe Examiner
Photo


The SPARK Examiner

Standard Program Analysis

The Examiner checks conformance of a SPARK source text to the rules of SPARK by performing the following analyses.

Lexical and Syntactic Analysis
The Examiner first carries out lexical and syntactic analysis of each compilation unit. In addition to checking for syntactic correctness, this analysis also permits the Examiner to identify which other units are required in order to carry out the complete analysis of each unit. These other units are then read by the Examiner.

Static Semantic Analysis
The Examiner then checks that the given compilation units satisfy the static-semantic rules of SPARK. This analysis checks for items such as aliasing and elaboration order dependencies.

Control Flow Analysis
This analysis checks that the placement of exit and return statements conforms to the rules of SPARK, thus guaranteeing that SPARK programs are well-structured.

Data and Information Flow Analysis
The Examiner performs data and information flow analysis in order to check for:

references to uninitialised data
ineffective statements
inconsistencies between the expected and actual information flow
loop stability
variables declared but not used
variables whose value will not change.


Run-Time Error Checking

Compliance with the rules of SPARK (as checked by standard program analysis) does provide protection against many common programming errors such as failure to initialise variables, and also against more obscure defects such as aliasing of procedure parameters. However, there is an important class of errors against which protection is not immediately provided – that of run-time errors. Run-time errors may be characterised as those which cannot be detected (or at least, not easily detected) at compilation time. In order to demonstrate that a SPARK program never gives rise to a run-time error, theorem proving is required.

Run-Time Errors in SPARK Programs
Many of the run-time errors of Ada cannot occur in SPARK programs. However, the following run-time errors may still arise in SPARK programs:

array index out of range
type range violation
division by zero
numerical overflow

Generating Proof Obligations
The formal definition of SPARK was used to specify the appropriate proof obligation for each run-time error. The proof obligation is a theorem which must be proved in order to show that the run-time error will never occur. The Run-Time Error Checker systematically generates the proof obligations for all possible run-time errors in a SPARK program.

Automatic Proof
The Automatic Simplifier will discharge many of the generated proof obligations automatically. Any remaining undischarged obligations may be proved with the assistance of the SPADE Proof Checker.


Program Verification

Overview
The functional requirements of a SPARK subprogram can be expressed formally as a precondition (ie a predicate which expresses a constraint on the imported variables of the subprogram) and a postcondition (ie a predicate which expresses the relationship between the initial values of the imported variables and the final values of the exported variables of the subprogram).
A program is said to be partially correct if, given that the precondition is satisfied on entry to the program and the program terminates, then the postcondition holds upon exit from the program. A program is said to be totally correct if it is partially correct and it always terminates. Note that even a totally correct program may still suffer from run-time errors and so a complete analysis of the program must also involve the use of the Run-Time Error Checker.
The Examiner provides fully-automated support for the generation of proof obligations for partial correctness.

Proof Contexts
In SPARK, preconditions and postconditions are expressed as annotations called proof contexts. These are special non-mandatory annotations which support the formal proof of SPARK programs. They are written in an expression language which is an extension of Ada's. Each proof context is associated with a particular location in the code:
the precondition of a procedure or function is associated with the begin of its body: it specifies conditions under which the procedure or function should be called;
the postcondition of a procedure (or the return annotation of a function) is associated with the end of the subprogram body: it defines the expected final state;
each assert statement (eg a loop invariant) or check statement (ie a well-formation statement) is located between two executable statements: it specifies the state expected of the program at that point.

The Origin of Proof Annotations
Ideally, the proof contexts should be derived from a formal mathematical specification. This derivation is not completely mechanical; apart from the inevitable differences between the specification and proof context notations, data types in the proof contexts must be expressed in concrete form (eg as arrays), rather than in the abstract form (eg sets or sequences) likely to be encountered in a formal specification.

Generating Verification Conditions
A verification condition describes the requirements for correctness of the path between two cutpoints in a SPARK program (ie a precondition, postcondition or assert annotation). It is generated automatically by the Examiner from the assertions at the cutpoints and the SPARK program statements which lie along the path.

Automatic Proof
The Automatic Simplifier will discharge many of the generated verification conditions automatically. Any remaining undischarged conditions may be proved with the assistance of the SPADE Proof Checker.


Analysis of Timing and Memory Usage


The SPARK language is designed to be statically analysable for both its worst-case execution time and memory usage. Constructs which may lead to unpredictable execution time (e.g. allocators) or memory use (e.g. recursion) are excluded from SPARK.

Worst-Case Execution Time Analysis
The static analysis of program execution time requires analysis of program source text, object code, and user-supplied annotations. The latter supply information that cannot be determined automatically, such as a bound on the number of iterations made by each looping statement. By combining the control-flow graph for each subprogram, the program's call-tree, and the worst-case execution time for each object code fragment, the worst-case execution time (WCET) of a subprogram can be determined. Such analysis has several uses:
Potential timing problems can be found earlier than usual, and thus can be corrected more cheaply and effectively.
Watchdog timers can be set to monitor and control the execution of time-critical code.
The program path that leads to WCET can be identified and tested with confidence.

Worst-Case Stack Usage Analysis
In real-time and embedded systems, the accurate analysis of memory usage is crucial. Ideally, an engineer should be able to show that a program will never exceed its memory allocation through an entirely static analysis of program source text and/or object code. SPARK enables such analysis through excluding language features which may lead to unpredictable memory use either explicitly (e.g. allocators and access types) or implicitly (e.g. functions returning unconstrained types, and the "&" operator).
Ada programs typically allocate data in one of three areas:
the stack,
a fixed-size area for compile-time allocated data,
a "heap" structure.

SPARK programs can be written which use no heap, and so the static analysis of memory usage in SPARK programs reduces to an analysis of worst-case stack usage.

Again, through an analysis of program source text, object code, and annotations, worst-case stack usage can be predicted.

Tool Support
Several research groups have demonstrated static WCET and stack-usage tools, and commercial tool support is beginning to appear. A future release of the Examiner will include source-level annotations supporting static analysis of timing and stack usage. These will be processed by the Examiner into a form which is acceptable to third-party tools, initially the STAMP toolset from York Software Engineering Ltd.

 

 
 

© Website Content Praxis High Integrity Systems 2008

arrowNormal text arrowLarge text

 

corner Site index
cornerSitesearch

corner

bulletSPARKAda home
bulletNews and Events
bulletSPARK for Beginners
bulletSPARK Language & Toolset
bulletSPARK Philosophy
bulletTechnical Overview
bulletThe Examiner
bulletSPARK Language
bulletSPADE
bulletSPARK and Compilers
bulletSPARK and Standards
bulletMISRA-C
bulletUsing SPARK
bulletPress
bulletDownloads and Publications

bulletContact SPARK Team

bulletSPARK Book (latest tools)

bulletTraining in SPARK



line

Contact Us +44 01225 466991
bulletOffice contact details, maps
bulletRecruitment and vacancies