Systems development lifecycle
Outline
- Requirements engineering
- Informal (system-level) specification
- Formal specification
Requirements engineering
- Often collected during discussions/negotiations between stakeholders
- Customer
- System designer
- Software provider
- ...
It is widely acknowledged amongst researchers and industry practitioners that software projects
are critically vulnerable when the requirements- related activities are poorly performed.
P. Bourque and R.E. Fairley, SWEBOK Version 3.0, IEEE Computer Society
Types of requirements (FURPS+)
- Functionality
- Features, capabilities, security
- Usability
- Human-Computer Interaction
- Documentation
- Reliability
- Frequency of failure
- Error recovery
- Performance
- Supportability
- Adaptability, Configurability
- Maintenance
- Many others
- Implementation
- User interface
- Licencing
Standards (examples)
- ISO/IEC/IEEE 12207:2017(E) - International Standard - Systems and software engineering -- Software life cycle processes
- 6.4.2: Stakeholder Needs and Requirements Definition process
- 6.4.3: System/Software requirements definition process
- ECSS-E-ST-40C - Software (2009)
- 4.2.4: Software requirements and architecture engineering process
Example - ATM
- Functionality
- An ATM shall dispense money to people presenting a valid debit card and entering the correct PIN
- Usability
- ATM shall be usable for visually impaired people
- ATM shall be usable for colour blind people
- Reliability
- There shall be at most one failure per calender year
- ATM shall be operational at least 99.99% of the time
- After a restart, the ATM shall check the current balance against the main bank system
Detailed design phase
- Individual components of the system are specified/designed
- Expected behaviour of functions/methods
- Assumptions about environment
- Invariants of individual components
- Different formalisms possible
- Contracts
- Assume/Assert statements
- ...
(Function) Contracts
- Central concept of detailed specification
- Describe intended behaviour as a function between
- The caller (user of functionality)
- The callee (provider of functionality)
- Consists of a pair of
- A set of preconditions (assumptions made by callee)
- A set of postconditions (guarantees given by callee)
Example - ATM
/**
* PRE: Card is inserted, user not authenticated
* POST: If pin is correct, then user is authenticated
* POST: If pin is incorrect, and counter < 2, then
counter is incremented by 1, and user is not
authenticated
* POST: If pin is incorrect, and counter >= 2, then card is
confiscated and user is not authenticated
*/
bool enterPIN(int pin, int * counter);
Example - Stack
#define STACK_SIZE 10
int stackData[STACK_SIZE];
int topPointer = 0;
void push(int data) {
stackData[topPointer] = data;
topPointer++;
}
int pop() {
int res = stackData[topPointer];
topPointer--;
return res;
}
Example - Stack
#define STACK_SIZE 10
int stackData[STACK_SIZE];
int topPointer = 0;
/**
* PRE: stack is not full
* POST: data is added as topmost element to the stack
*/
void push(int data) {
stackData[topPointer] = data;
topPointer++;
}
Specification so far
- Description still fairly high-level
- Relies on natual language
- Ambiguous
- Cannot be validated automatically
- Need for more specialized specification language
Formal specification
- Need a language to capture
- Preconditions
- Postconditions
- Invariants, assumptions, etc.
- Eventually we would also like to
- use automation in testing
- perform formal verification
Propositional logic
Language: propositions, boolean variables
Connective |
Meaning |
C Syntax |
$\neg P$ |
not P |
! P |
$P \land Q$ |
P and Q |
P && Q |
$P \lor Q$ |
P or Q |
P || Q |
$P \rightarrow Q$ |
P implies Q |
!P || Q |
$P \leftrightarrow Q$ |
P equivalent Q |
P == Q |
Properties of formulas
A formula containing variables P, Q, ... can be
- Satisfiable
- holds for some assignment of values to P, Q, ...
- Valid (tautology)
- Always holds, independent of values of P, Q, ...
- Unsatisfiable
- Invalid
Useful valid formulas
- $\neg(\Phi \land \Psi) \leftrightarrow \neg \Phi \lor \neg \Psi$
- $\neg(\Phi \lor \Psi) \leftrightarrow \neg \Phi \land \neg \Psi$
- $(true \land \Phi) \leftrightarrow \Phi$
- $(false \lor \Phi) \leftrightarrow \Phi$
- $\Phi \rightarrow true$
- $false \rightarrow \Phi$
Checking propositional formulas
- By hand
- Truth table
- Formula simplifications
- By using dedicated software tools
- SAT solvers
- Usually also provide a model for satisfiable formulas
- SAT is NP complete, but often solveable in practice
Propositional logic in verification
- Propositional logic is relatively weak
- Cannot express many of the "interesting" properties
- Widely applied for specifying hardware
- Sometimes used for abstractions of programs
- Abstracting a boolean program with same control flow
- E.g. SLAM toolkit
First-order logic
Connective |
Meaning |
C Syntax |
... |
... |
... |
$\forall x : S.\,P$ |
$P(x)$ for all $x \in S$ |
|
$\exists x : S.\,P$ |
$P(x)$ for some $x \in S$ |
|
FOL - Examples
- Formulas over pre-defined datatypes (e.g. int)
- $x \geq 0 \land y \geq 42 \rightarrow x + y \geq 10$
- $\forall x: S.\, p(x) \rightarrow \exists y: S.\, p(y)$
- Formulas over "uninterpreted" symbols
- $\forall x, y: \mathbb{Z}.\, (x \geq y \rightarrow f(x) \geq f(y)) \rightarrow f(2) \geq f(1)$
- $\forall x: S.\, p(x) \rightarrow \exists y: S.\, p(y)$
FOL Properties
- Same as before
- Satisfiable
- Valid
- Unsatisfiable
- Invalid
- Can be solved with SMT solver
Program states
- Pre-, Postconditions, and invariants => distinguishing program states
- Need language/logic that supports native datatypes (int, real, etc.)
- Program variables then become formula variables!
Example
/**
* result >= a /\ result >= b /\ (result = a \/ result = b)
*/
int max(int a, int b)
{
if (a > b) return a;
else return b;
}
Concrete contract languages
- Builtin support: Ada 2012, Eiffel, Scala, Dafny, Kotlin, ...
- Support through additional tools:
- OCaml: GOSPEL
- Java: Java Modeling Language (JML)
- C: ANSI C Specification Language (ACSL)
Example
/*@
ensures
\result >= a &&
\result >= b &&
(\result == a || \result == b);
*/
int max(int a, int b)
{
if (a > b) return a;
else return b;
}
Example - Stack
#define STACK_SIZE 10
int stackData[STACK_SIZE];
int topPointer = 0;
/*@
requires topPointer < STACK_SIZE;
ensures
topPointer == \old(topPointer) + 1 &&
stackData[\old(topPointer)] == data;
*/
void push(int data) {
stackData[topPointer] = data;
topPointer++;
}
Assertions and assumptions
Assertions
- Alternative to contracts
- assert statement present in many languages
- Commonly used by programmers to state invariants or intentions
Example - Stack
#define STACK_SIZE 10
int stackData[STACK_SIZE];
int topPointer = 0;
/**
* PRE: stack is not empty;
* POST: topmost element is removed and returned
*/
int pop() {
int res = stackData[topPointer];
topPointer--;
return res;
}
Example - Stack
#define STACK_SIZE 10
int stackData[STACK_SIZE];
int topPointer = 0;
/**
* PRE: stack is not empty;
* POST: topmost element is removed and returned
*/
int pop() {
assert(topPointer > 0);
int oldTopPointer = topPointer;
int res = stackData[topPointer];
topPointer--;
assert(res == stackData[oldTopPointer]);
return res;
}
Semantics of assert
- If a formula P evaluates to true, then assert(P) has no effect
- If a formula P evaluates to false, then assert(P) raises an error
Assertions vs contracts
- Problem 1: assert mixes specification and implementation
- Problem 2: concepts like pre-state (\old) are cumbersome
- Problem 3: assert mixes up responsibilities
- PRE => responsibility of caller
- POST => responsibility of callee
Assumptions
- If a formula P evaluates to true, then assume(P) has no effect
- If a formula P evalutes to false, then assume(P) suspends execution
- After assume(false), it's like the program was never started
- assume is not an executable statement
What about system-level specification?
- For detailed design: Contracts, assertions, assumptions
- For system-level specification
- Formal temporal properties: (LTL, CTL, Timed Automata, ...)
- Observers and runtime monitors
Observers
- Watchdogs check that system is reactive
- Observers generalise this concept
- Environment assumptions: Received inputs are meaningful (PRE)
- No errors: System operates within legal ranges (POST)
- System invariants are not violated
Observers
Observers
- Usually implemented in software
- Observers can monitor all system inputs and outputs
- Perform sanity checks (derived from system specification)
- Ongoing research about automated generation of observers