320 likes | 434 Vues
This overview covers the importance of static analysis in programming, specifically using the Ada language. It highlights how static analysis allows the identification of program properties without execution, including style adherence, coding standards, and dubious constructs. The document discusses the role of annotations in strengthening specifications, enabling early error detection before full implementation. It particularly emphasizes how exact language and removal of ambiguities contribute to more effective analysis. The evolution of annotations in Ada is also explored, showcasing their transition from describing code to capturing abstract concepts.
E N D
A Language for Systems not Just Software Peter Amey Praxis Critical Systems
Static Analysis Overview • Identifying properties of a program without execution • style, coding standards, dubious construct detection • language subset conformance, wellformedness • control flow and complexity • data flow analysis • information flow analysis • proof (or formal verification) • An Ada compiler is a powerful static analyser • Analysis: shows that a program should work in all cases • Testing: shows that it does work for certain specific cases
SPARK Goals • Precise static analysis • Early use of static analysis • Facilitated by: • an exact language • removal of ambiguous and erroneous constructs • annotations
Why Annotations? • Annotations strengthen specifications • Ada separation of specifications/implementations too weak • Allows analysis without access to implementations • which can be done early on during development • even before programs are complete or compilable • Allows efficient detection of erroneous constructs
An example procedure Inc (X : inout Integer); --# globalinout Callcount; detection of function side-effect function AddOne (X : Integer) return Integer is XLocal : Integer := X; begin Inc(Xlocal); return XLocal; end AddOne; detection of aliasing Inc (CallCount);
Evolution of Annotations • Initially annotations were about code
Evolution of Annotations package P is procedure Inc (X : inout Integer); --# globalinout CallCount; end P; • Initially annotations were about code
Evolution of Annotations package P is procedure Inc (X : inout Integer); --# globalinout CallCount; end P; --# own CallCount; --# initializes CallCount; • Initially annotations were about code
Evolution of Annotations package P is procedure Inc (X : inout Integer); --# globalinout CallCount; end P; --# own CallCount; --# initializes CallCount; • Initially annotations were about code packagebody P is CallCount : Integer := 0; procedure Inc (X : inout Integer) is begin X := X + 1; CallCount := CallCount + 1; end Inc; endP;
Evolution of Annotations package P is procedure Inc (X : inout Integer); --# globalinout CallCount; end P; --# ownCallCount; --# initializes CallCount; • Initially annotations were about code packagebody P is CallCount : Integer := 0; procedure Inc (X : inout Integer) is begin X := X + 1; CallCount := CallCount + 1; end Inc; endP;
Evolution of Annotations package P is procedure Inc (X : inout Integer); --# globalinout CallCount; end P; --# own CallCount; --# initializes CallCount; • Initially annotations were about code • They evolved better to describe abstractions
package Stack --# own State; is procedure Clear; --# globalout State; --# derives State from ; procedure Push (X : in Integer); --# globalinout State; --# derives State from State, X; procedure Pop (X : out Integer); --# globalinout State; --# derives X, State from State; end Stack; packagebody Stack --# own State is Vector, Ptr; is MaxDepth : constant := 100; type Ptrs isrange 0 .. MaxDepth; subtype Indexes is Ptrs range 1 .. MaxDepth; type Vectors isarray (Indexes) of Integer; Ptr : Ptrs; Vector : Vectors; ... procedure Push (X : in Integer); --# globalinout Ptr, Vector; --# derives Vector from Vector, --# X, Ptr & --# Ptr from Ptr; ... Refinement
package Stack --# ownState; is procedure Clear; --# globalout State; --# derives State from ; procedure Push (X : in Integer); --# globalinout State; --# derives State from State, X; procedure Pop (X : out Integer); --# globalinout State; --# derives X, State from State; end Stack; packagebody Stack --# own State is Vector, Ptr; is MaxDepth : constant := 100; type Ptrs isrange 0 .. MaxDepth; subtype Indexes is Ptrs range 1 .. MaxDepth; type Vectors isarray (Indexes) of Integer; Ptr : Ptrs; Vector : Vectors; ... procedure Push (X : in Integer); --# globalinout Ptr, Vector; --# derives Vector from Vector, --# X, Ptr & --# Ptr from Ptr; ... Refinement
package Stack --# ownState; is procedure Clear; --# globalout State; --# derives State from ; procedure Push (X : in Integer); --# globalinout State; --# derives State from State, X; procedure Pop (X : out Integer); --# globalinout State; --# derives X, State from State; end Stack; packagebody Stack --# own State isVector, Ptr; is MaxDepth : constant := 100; type Ptrs isrange 0 .. MaxDepth; subtype Indexes is Ptrs range 1 .. MaxDepth; type Vectors isarray (Indexes) of Integer; Ptr : Ptrs; Vector : Vectors; ... procedure Push (X : in Integer); --# globalinout Ptr, Vector; --# derives Vector from Vector, --# X, Ptr & --# Ptr from Ptr; ... Refinement
package Stack --# own State; is procedure Clear; --# globalout State; --# derives State from ; procedure Push (X : in Integer); --# globalinout State; --# derives State from State, X; procedure Pop (X : out Integer); --# global in out State; --# derives X, State from State; end Stack; packagebody Stack --# own State is Vector, Ptr; is MaxDepth : constant := 100; type Ptrs isrange 0 .. MaxDepth; subtype Indexes is Ptrs range 1 .. MaxDepth; type Vectors isarray (Indexes) of Integer; Ptr : Ptrs; Vector : Vectors; ... procedure Push (X : in Integer); --# globalinout Ptr, Vector; --# derives Vector from Vector, --# X, Ptr & --# Ptr from Ptr; ... Refinement
Volatility Z : integer; for Z’Address use ... X := Z; Y := Z; does X = Y?
Modelling Volatility package Temperature --# own Inputs; --# initializes Inputs; is procedure Read (X : out Celsius); --# globalinout Inputs; --# derives X from Inputs & --# Inputs from Inputs; end Temperature;
Modelling Volatility package Temperature --# ownin Inputs; --# initializes Inputs; is procedure Read (X : out Celsius); --# globalinout Inputs; --# derives X from Inputs;& --# Inputs from Inputs; end Temperature;
Modelling Volatility package Temperature --# owninInputs; is procedure Read (X : out Celsius); --# globalin Inputs; --# derives X from Inputs; end Temperature;
Sensors package WaterHighSensor --# ownin State; is function IsActive return Boolean; --# global State; end WaterHighSensor; package WaterLowSensor --# ownin State; is function IsActive return Boolean; --# global State; end WaterLowSensor;
package Valve is type T is (Open, Shut); end Valve; with Valve; --# inherit Valve; package FillValve --# ownout State; is procedure SetTo (Setting : in Valve.T); --# globalout State; --# derives State from Setting; end FillValve; Actuators
Fault Integrator Abstract Type package FaultIntegrator is type T islimitedprivate; procedure Init (FI : out T; Threshold : in Positive); --# derives FI from Threshold; procedure Test (FI : inout T; CurrentEvent : in Boolean; IntegratedEvent : out Boolean); --# derives IntegratedEvent, --# FI from FI, CurrentEvent; private --# hide FaultIntegrator; end FaultIntegrator;
procedure Main --# globalin WaterHighSensor.State, --# WaterLowSensor.State; --# out FillValve.State, --# DrainValve.State; --# derives FillValve.State from --# WaterLowSensor.State & --# DrainValve.State from --# WaterHighSensor.State; is HighIntegrator, LowIntegrator : FaultIntegrator.T; HighThreshold : constant Positive := 10; LowThreshold : constant Positive := 10; Main controller
procedure ControlHigh --# globalin WaterHighSensor.State; --# out DrainValve.State; --# inout HighIntegrator; --# derives DrainValve.State, --# HighIntegrator from --# HighIntegrator, --# WaterHighSensor.State; isseparate; procedure ControlLow --# globalin WaterLowSensor.State; --# out FillValve.State; --# inout LowIntegrator; --# derives FillValve.State, --# LowIntegrator from --# LowIntegrator, --# WaterLowSensor.State; isseparate; Main controller
Main controller begin -- Main FaultIntegrator.Init (HighIntegrator, HighThreshold); FaultIntegrator.Init (LowIntegrator, LowThreshold); FillValve.SetTo (Valve.Shut); DrainValve.SetTo (Valve.Shut); loop ControlHigh; ControlLow; endloop; end Main;
separate (Main) procedure ControlHigh is RawFullEvent, TooFull : Boolean; begin RawFullEvent := WaterHighSensor.IsActive; FaultIntegrator.Test (HighIntegrator, RawFullEvent, -- to get TooFull); if TooFull then DrainValve.SetTo (Valve.Open); else DrainValve.SetTo (Valve.Shut); endif; end ControlHigh; Subunits
packagebody WaterHighSensor --# own State isin HighSensorPort; is type Byte ismod 256; ActiveValue : constant Byte := 255; HighSensorPort : Byte; for HighSensorPort'Address use ... function IsActive return Boolean --# global HighSensorPort; is RawVal : Byte; Result : Boolean; begin RawVal := HighSensorPort; if RawVal'Valid then Result := RawVal = ActiveValue; else Result := True; -- show too full on sensor failure endif; return Result; end IsActive; end WaterHighSensor; Device drivers
Low level annotation in implementation terms function IsActive return Boolean --# global HighSensorPort; Traceability and Abstraction --# own State isin HighSensorPort; Refinement hiding implementation detail function IsActive return Boolean; --# global State; Annotation in spec is in abstract terms --# derives FillValve.State from --# WaterLowSensor.State & --# DrainValve.State from --# WaterHighSensor.State; Main controller annotation entirely in abstract terms
Conclusions • SPARK and the Examiner originated from research concerned with reverse engineering of code • SPARK has evolved into something much more concerned with program construction than program analysis • The combination of abstract own variables and modes provides mechanisms for parallel descriptions of systems and implementations that analysis binds together