Metrics - overview and description
Title in the “Standard metrics” view | Description |
---|---|
Code size | |
Variables size | |
Stack size | |
Calls | |
Tasks | |
Globals | |
IOs | |
Locals | |
Inputs | |
Outputs | |
NOS | |
Comments | |
McCabe | |
Complexity | |
DIT | |
NOC | |
RFC | |
CBO | |
Elshof | |
LCOM | |
SFC branches | |
SFC steps |
Detailed description
Code size [number of bytes]
Title short form | Code size |
Categories | Informative, efficiency |
Definition | Number of bytes that a function block contributes to the application code |
Further information | The number also depends on the code generator. For example, the code generator for Arm® processors generally generates more bytes than the code generator for x86 processors. |
Variables size [number of bytes]
Title short form | Variables size |
Categories | Informative, efficiency |
Definition | Size of the static memory used by the object |
Further information | For function blocks, this is the size used for an instance of this function block (which may also contain memory gaps depending on the memory alignment). For programs, functions and global variable lists, this is the sum of the size of all static variables. |
Sample:
FUNCTION F_Sample : INT
VAR_INPUT
a,b : INT;
END_VAR
VAR
c,d : INT;
END_VAR
VAR_STAT
f,g,h : INT;
END_VAR
The function has three static variables of type INT (f, g, h
), each of which requires 2 bytes of memory. F_Sample
therefore has a variable size of 6 bytes.
Stack size [number of bytes]
Title short form | Stack size |
Categories | Informative, efficient, reliable |
Definition | Number of bytes required to call a function or function block |
Further information | Input variables and output variables are aligned with the memory. This can create a gap between these variables and the local variables. This gap is counted. Return values from called functions that do not fit into a register are pushed onto the stack. The largest of these values determines the additionally allocated memory, which is also counted. Functions or function blocks that are called within the POUs under consideration have their own stack frame. Therefore, the memory for such calls does not count. Depending on the code generator used, intermediate results of calculations also use the stack. These results are not counted. |
Sample:
FUNCTION F_Sample : INT
VAR_INPUT
a,b : INT;
END_VAR
VAR
c,d,e : INT;
END_VAR
VAR_STAT
f,g,h : INT;
END_VAR
c := b;
d := a;
e := a + b;
Assumption: The "TwinCAT RT (x86)" solution platform is used for the calculation. The device has a stack alignment of 4 bytes, which can result in gaps between the variables.
The total stack size of F_Sample
is 16 bytes and consists of:
- 2 input variables with 2 bytes each = 4 bytes
- No padding bytes
- Return value INT = 2 bytes
- Padding bytes for the stack alignment = 2 bytes
- 3 local variables with 2 bytes each = 6 bytes
- Padding bytes for the stack alignment = 2 bytes
VAR_STAT is not stored on the stack and therefore does not increase the stack size of a POU.
Number of calls
Title short form | Calls |
Categories | Informative |
Definition | Number of calls to the program organization unit (POU) within the application |
Further information | If a program is called in a task, this call is also counted. |
Called in tasks
Title short form | Tasks |
Categories | Maintainability, reliability |
Definition | Number of tasks in which the program organization unit (POU) is called up |
Further information | For function blocks, the number of tasks in which the function block itself or any function block in the function block's inheritance tree is called is counted. For methods and actions, the number of tasks in which the (higher-level) function block is called is displayed. |
Sample:
FUNCTION_BLOCK FB1
FUNCTION_BLOCK FB2 EXTENDS FB1
FUNCTION_BLOCK FB3 EXTENDS FB2
Each function block is instantiated and called in a separate program. In addition, each program is called in a separate task.
The metric Called in tasks therefore results:
- For
FB3
: 1 - For
FB2
: 2, as the calls fromFB2
andFB3
(EXTENDS FB2
) are counted - For
FB1
: 3, as the calls fromFB1
,FB2
andFB3
are counted
Number of global variables used
Title short form | Globals |
Categories | Maintainability, reusability |
Definition | Number of different global variables used in the program organization unit (POU) |
Number of address accesses
Title short form | IOs |
Categories | Reusability, maintainability |
Definition | Number of address accesses in the implementation of the object |
Sample:
PROGRAM MAIN
VAR
bVar : BOOL;
bIn AT%I* : BOOL;
bOut AT%Q* : BOOL;
END_VAR
bVar := TRUE;
bOut := bIn;
bOut := NOT bOut AND bIn;
The number of address accesses for the program MAIN
is 5 and is made up of 2 write and 3 read accesses.
Number of local variables
Title short form | Locals |
Categories | Informative, efficiency |
Definition | Number of variables declared in the VAR area of the program organization unit (POU) |
Further information | Inherited local variables are not counted. |
Number of input variables
Number of input variables of the function block (VAR_INPUT).
Title short form | Inputs |
Categories | Maintainability, reusability |
Definition | Number of variables declared in the VAR_INPUT area of the program organization unit (POU) |
Further information | Inherited input variables are not counted. |
Standard upper limit for the associated rule SA0166 | 10 |
Number of output variables
Number of output variables of the function block (VAR_OUTPUT).
Title short form | Outputs |
Categories | Maintainability, reusability |
Definition | Number of variables declared in the VAR_OUTPUT area of the program organization unit (POU) |
Further information | For function blocks, this is the number of user-defined output variables (VAR_OUTPUT). For methods and functions, this is the number of user-defined output variables (VAR_OUTPUT) plus one if they have a return value. The return value is also counted. Inherited output variables are not counted. A high number of output variables is a sign of a violation of the principle of clear responsibility. |
Standard upper limit for the associated rule SA0166 | 10 |
Sample:
METHOD METH : BOOL
VAR_OUTPUT
a : INT;
b : LREAL;
END_VAR
The method METH
has three outputs:
- Return value
METH
a
b
METHOD METH1
VAR_OUTPUT
a : ARRAY[0..10] OF INT;
b : LREAL;
END_VAR
The method METH1
has two outputs:
a
b
Number Of Statements (NOS)
Title short form | NOS |
Categories | Informative |
Definition | Number of executable statements in the implementation of a function block, function or method |
Further information | NOS = Number Of executable Statements Statements in the declaration, empty statements or pragmas are not counted. |
Sample:
FUNCTION_BLOCK FB_Sample
VAR_OUTPUT
nTest : INT;
i : INT;
END_VAR
VAR
bVar : BOOL;
c : INT := 100; // statements in the declaration are not counted
END_VAR
IF bVar THEN //if statement: +1
nTest := 0; // +1
END_IF
WHILE nTest = 1 DO //while statement: +1
; // empty statements do not add to the statement count
END_WHILE
FOR c := 0 TO 10 BY 2 DO //for statement: +1
i := i+i; // +1
END_FOR
{text 'simple text pragma'} //pragmas are not counted
nTest := 2; //+1
The sample has six statements.
Percentage of comment
Title short form | Comments |
Categories | Maintainability |
Definition | Percentage of comments in the source code This number is calculated using the following formula: Percentage = 100 * <letters in comments> / <letters in source code and comments together> |
Further information | Multiple consecutive spaces in the source code are counted as one space, which prevents a high weighting of indented source code. A percentage of 0 is returned for empty objects (no source code and no comments). The statements also include declaration statements, for example. |
Complexity (McCabe)
Title short form | McCabe |
Categories | Testability |
Definition | Number of binary branches in the control flow of the POU (for example, the number of branches for IF and CASE statements and loops) |
Further information | McCabe's cyclomatic complexity is a measure of the readability and testability of source code. It is calculated by counting the number of binary branches in the control flow of the POU. Cyclomatic complexity penalizes high branching because high branching increases the number of test cases required for high test coverage. |
Recommended upper limit | 10 |
The following samples show how complexity is calculated according to McCabe.
Sample: IF statement
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
IF b1 THEN // +1 for the THEN branch
;
ELSIF b2 THEN // +1 for the THEN branch of the IF inside the ELSE
;
ELSE
IF b3 OR b4 THEN // +1 for the THEN branch
;
END_IF
END_IF
The code snippet has a cyclomatic complexity of 4.
Sample: CASE statement
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
CASE a OF
1: ; // +1
2: ; // +1
3,4,5: ; // +1
ELSE // the ELSE statement does not increase the cyclomatic complexity
;
END_CASE
The code snippet has a cyclomatic complexity of 4.
Sample: Loop statement
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
WHILE b1 DO // +1 for the WHILE loop
;
END_WHILE
REPEAT // +1 for the REPEAT loop
;
UNTIL b2
END_REPEAT
FOR a := 0 TO 100 BY 2 DO // +1 for the REPEAT loop
;
END_FOR
The code snippet has a cyclomatic complexity of 4.
Sample: Other statements
The following statements also lead to an increase in cyclomatic complexity:
FUNCTION FUN : STRING
VAR_INPUT
bReturn : BOOL;
bJump : BOOL;
END_VAR
// every POU has an initial cyclomatic complexity of 1, since it has at least 1 branch
JMP(bJump) lbl; //Conditional jumps increase the cyclomatic complexity by 1
FUN := 'u';
RETURN(condition_return); //Conditional returns increase the cyclomatic complexity by 1, too
lbl:
FUN := 't';
The code snippet has a cyclomatic complexity of 3.
Cognitive complexity
Title short form | Cognitive complexity |
Categories | Maintainability |
Definition | Sum of partial complexities resulting, for example, from branches in the control flow of the POU and from sophisticated Boolean expressions |
Further information | Cognitive complexity is a measure of the readability and comprehensibility of source code introduced by Sonarsource™ in 2016. It penalizes a strong nesting of the control flow and sophisticated Boolean expressions. The cognitive complexity is only calculated for structured text implementations. |
Standard upper limit for the associated rule SA0178 | 20 |
Tip You can also use Command 'Show cognitive complexity for current editor' to display the increments for Structured Text directly in the editor. |
The following samples show how cognitive complexity is calculated.
Sample: Control flow
Statements that manipulate the control flow increase the cognitive complexity by 1.
IF TRUE THEN //+1 cognitive complexity
;
END_IF
WHILE TRUE DO //+1 cognitive complexity
;
END_WHILE
FOR i := 0 TO 10 BY 1 DO //+1 cognitive complexity
;
END_FOR
REPEAT //+1 cognitive complexity
;
UNTIL TRUE
END_REPEAT
The code snippet has a cognitive complexity of 4.
Sample: Nesting of the control flow
When nesting the control flow, an increment of 1 is added for each level of nesting.
IF TRUE THEN //+1 cognitive complexity
WHILE TRUE DO //+2 (+1 for the loop itself, +1 for the nesting inside the IF)
FOR i := 0 TO 10 BY 1 DO //+3 (+1 for the FOR loop itself, +2 for the nesting inside the WHILE and the IF)
;
END_FOR
END_WHILE
REPEAT //+2 (+1 for the loop itself, +1 for the nesting inside the IF)
;
UNTIL TRUE
END_REPEAT
END_IF
The code snippet has a cognitive complexity of 8.
Sample: Boolean expression
Since Boolean expressions play a major role in understanding source code, they are also taken into account when calculating cognitive complexity.
Understanding Boolean expressions that are connected with the same Boolean operator is not as difficult as understanding a Boolean expression that contains alternating Boolean operators. Therefore, each chain of equal Boolean operators in an expression increases the cognitive complexity.
b := b1; //+0: a simple expression, containing no operators, has no increment
The simple expression without operator has an increment of 0.
b := b1 AND b2; //+1: one chain of AND operators
The expression with an AND operation has an increment of 1.
b := b1 AND b2 AND b3; //+1: one more AND, but the number of chains of operators does not change
The expression has an AND more. But since it is the same operator, the number of chains formed with identical operators does not change.
b := b1 AND b2 OR b3; //+2: one chain of AND operators and one chain of OR operators
The expression has a chain of AND operators and a chain of OR operators. This results in an increment of 2.
b := b1 AND b2 OR b3 AND b4 AND b5; //+3: one chain of AND operators, one chain of OR operators and another chain of AND operators
The code snippet has an increment of 3.
b := b1 AND NOT b2 AND b3; //+1: the unary NOT operator is not considered in the cognitive complexity
The unary operator NOT is not taken into account in the cognitive complexity.
Sample: Further statements with increment
Structured Text has additional statements and expressions that change the control flow.
The following statements are penalized with an increment in cognitive complexity:
aNewLabel:
x := MUX(i, a,b,c); //+1 for MUX operator
y := SEL(b, i,j); //+1 for SEL operator
JMP aNewLabel; //+1 for JMP to label
EXIT and RETURN statements do not increase cognitive complexity.
Depth of Inheritance Tree (DIT)
Title short form | DIT |
Categories | Maintainability |
Definition | Number of inheritances until a function block is reached that does not extend any other function block |
Further information | DIT = Depth of Inheritance Tree
|
Sample:
FUNCTION_BLOCK FB_Base
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base
FUNCTION_BLOCK FB_SubSub EXTENDS FB_Sub
The metric Depth of Inheritance Tree is:
- For
FB_Base
: 0, as it is itself a function block that does not extend any other function block. - For
FB_Sub
: 1, as one step is required to get toFB_Base
. - For
FB_SubSub
: 2, as one step toFB_Sub
and another toFB_Base
is required.
Number Of Children (NOC)
Title short form | NOC |
Categories | Reusability, maintainability |
Definition | Number of function blocks that extend the given basic function block. Function blocks that indirectly extend a basic function block are not counted. |
Further information | NOC = Number Of Children
|
Sample:
FUNCTION_BLOCK FB_Base
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base
FUNCTION_BLOCK FB_SubSub1 EXTENDS FB_Sub
FUNCTION_BLOCK FB_SubSub2 EXTENDS FB_Sub
The metric Number Of Children is:
- For
FB_Base
: 1 child (FB_Sub) - For
FB_Sub
: 2 children (FB_SubSub1, FB_SubSub2) - For
FB_SubSub1
: 0 children - For
FB_SubSub2
: 0 children
Response For Class (RFC)
Title short form | RFC |
Categories | Maintainability, reusability |
Definition | Number of different POUs, methods or actions that can be called by a POU |
Further information | RFC = Response For Class The value is used for measuring the complexity (in terms of testability and maintainability). All possible direct and indirect method calls can be reached via associations are taken into account. These can be used to respond to an incoming message or to respond to an event that has occurred. |
Sample:
Function block FB1:
FUNCTION_BLOCK FB1
VAR
d,x,y : INT;
END_VAR
x := METH(d+10);
y := FUN(42, 0.815);
Method FB1.METH:
METHOD METH : INT
VAR_INPUT
i : INT;
END_VAR
METH := FUN(CUBE(i), 3.1415);
Function Cube:
FUNCTION CUBE : INT
VAR_INPUT
i : INT;
END_VAR
CUBE := i*i*i;
Function FUN:
FUNCTION FUN : INT
VAR_INPUT
a : INT;
f : LREAL;
END_VAR
FUN := LREAL_TO_INT(f*10)*a;
FUN
,CUBE
: These functions have an RFC of 0, as neither function calls other functions, function blocks or methods for their calculations.FB1.METH:
The method usesFUN
andCUBE
, which results in an RFC of 2.FB1:
- The function block
FB1
callsMETH
andFUN
, which increases its RFC by 2. - For
FB1
, its methodMETH
must also be taken into account.METH
usesFUN
andCUBE
.FUN
has already been added to the RFC ofFB1
(see previous point). Thus, only the use ofCUBE
inMETH
increases the RFC forFB1
to 3.
Coupling Between Objects (CBO)
Title short form | CBO |
Categories | Maintainability, reusability |
Definition | Number of additional function blocks that are instantiated and used in a function block |
Further information | CBO = Coupling Between Objects A function block with a high level of coupling between objects is likely to be involved in many different tasks and therefore violates the principle of clear responsibility. |
Standard upper limit for the associated rule SA0179 | 30 |
Sample:
FUNCTION_BLOCK FB_Base
VAR
fb3 : FB3; // +1 instantiated here
END_VAR
FUNCTION_BLOCK FB_Sub EXTENDS FB_Base // +0 for EXTENDS
VAR
fb1 : FB1; // +1: instantiated here
fb2 : FB2; // +1: instantiated here
END_VAR
fb3(); // +0: instantiated in FB_Base, no increment for call
- The extension of a function block does not increase the coupling between objects.
- fb3 is instantiated in the implementation of
FB_Base
and inherited byFB_Sub
. The call inFB_Sub
does not increase the coupling between the objects. - The metric Coupling Between Objects for
FB_Sub
is therefore : 2
Complexity of reference (Elshof)
Complexity of reference = referenced data (number of variables) / number of data references
Title short form | Elshof |
Categories | Efficiency, maintainability, reusability |
Definition | Complexity of the data flow of a POU The complexity of reference is calculated using the following formula: <Number of variables used> / <Number of variable accesses> |
Further information | Only variable accesses in the implementation part of the POU are taken into account. |
Sample:
PROGRAM MAIN
VAR
i, j : INT;
k : INT := GVL.m;
b, c : BOOL;
fb : FB_Sample;
END_VAR
fb(paramA := b); // +3 accesses (fb, paramA and b)
i := j; // +2 accesses (i and j)
j := GVL.d; // +2 accesses (j and GVL.d)
For the metric Complexity of reference (Elshof), MAIN
:
- Number of variables used = 6
- Number of variable accesses = 7
- Complexity of reference (Elshof) = number of variables used/number of variable accesses = 6/7 = 0.85
Attention:
- c and k are not used and therefore do not count as "variables used".
- The assignment
k : INT := GVL.m;
is not counted as it is part of the declaration of the program.
Lack of Cohesion Of Methods - LCOM
Title short form | LCOM |
Categories | Maintainability, reusability |
Definition | Cohesion = pairs of methods without common instance variables minus pairs of methods with common instance variables The metric is calculated using the following formula: MAX(0, <number of object pairs without cohesion> - <number of object pairs with cohesion> ) |
Further information | LCOM: Lack of Cohesion of Methods The cohesion between function blocks, their actions, transitions and methods describes whether they access the same variables. The lack of cohesion of methods describes how strongly the objects of a function block are connected to each other. The lower the lack of cohesion, the stronger the connection between the objects. Function blocks with a high lack of cohesion are likely to be involved in many different tasks and therefore violate the principle of unambiguous responsibility. |
Sample:
Function block FB:
FUNCTION_BLOCK FB
VAR_INPUT
a : BOOL;
END_VAR
VAR
i,b : BOOL;
END_VAR
Action FB.ACT:
i := FALSE;
Method FB.METH:
METHOD METH : BOOL
VAR_INPUT
c : BOOL;
END_VAR
METH := c;
i := TRUE;
Method FB.METH2:
METHOD METH2 : INT
VAR_INPUT
END_VAR
METH2 := SEL(b,3,4);
For the metric Lack of Cohesion Of Methods (LCOM) , the result for FB
:
- Object pairs without cohesion (5 pairs):
FB
,FB.ACT
FB
,FB.METH
FB
,FB.METH2
FB.ACT
,FB.METH2
FB.METH
,FB.METH2
- Object pairs with cohesion (1 pair):
FB.ACT
,FB.METH
(both usei
)- LCOM = number of object pairs without cohesion - number of object pairs with cohesion = 5 - 1 = 4
Number of SFC branches
Title short form | SFC branches |
Categories | Testability, maintainability |
Definition | Number of alternative and parallel branches of a POU of the implementation language SFC (Sequential Function Chart) |
Sample:
The above code snippet in SFC has 4 branches: 3 alternative and 1 parallel branch.
Number of SFC steps
If the function block is implemented in Sequential Function Chart (SFC), this code metric indicates the number of steps in the function block.
Title short form | SFC steps |
Categories | Maintainability |
Definition | Number of steps in a POU of the implementation language SFC (Sequential Function Chart) |
Further information | Only the steps contained in the POU programmed in SFC are counted. Steps in the implementations of actions or transitions called in POUs are not counted. |
Sample:
The above code snippet in SFC has 10 steps.
Metrics that are available in TwinCAT versions < 3.1.4026.14:
Column abbreviation in Standard Metrics view | Description |
---|---|
Prather | |
n1 (Halstead) | |
N1 (Halstead) | |
n2 (Halstead) | |
N2 (Halstead) | |
HL (Halstead) | |
HV (Halstead) | |
D (Halstead) |
Complexity of nesting (Prather)
Nesting weight = statements * nesting depth
Complexity of nesting = nesting weight / number statements
Nesting through IF/ELSEIF or CASE/ELSE statements, for example.
Halstead (n1, N1, n2, N2, HL, HV, D)
The following metrics are part of the "Halstead" range:
- Number of different used operators - Halstead (n1)
- Number of operators - Halstead (N1)
- Number of different used operands - Halstead (n2)
- Number of operands - Halstead (N2)
- Length - Halstead (HL)
- Volume - Halstead (HV)
- Difficulty - Halstead (D)
Background information:
- Relationship between operators and operands (number, complexity, test effort)
- Based on the assumption that executable programs consist of operators and operands.
- Operands in TwinCAT: variables, constants, components, literals and IEC addresses.
- Operators in TwinCAT: keywords, logical and comparison operators, assignments, IF, FOR, BY, ^, ELSE, CASE, Caselabel, BREAK, RETURN, SIN, +, labels, calls, pragmas, conversions, SUPER, THIS, index access, component access, etc.
For each program the following basic parameters are formed:
- Number of different operators used - Halstead (n1),
Number of different operands used - Halstead (n2): - Number of different used operators (h1) and operands (h2); together they form the vocabulary size h.
- Number of operators - Halstead (N1),
Number of operands - Halstead (N2): - Number of total used operators (N1) and operands (N2); together they form the implementation class N.
- (Language complexity = operators/operator occurrences * operands/operand occurrences)
These parameters are used to calculate the Halstead length (HL) and Halstead volume (HV):
- Length - Halstead (HL),
Volume - Halstead (HV): - HL = h1* log2h1 + h2* log2h2
- HV = N* log2h
Various key figures are calculated from the basic parameters:
- Difficulty - Halstead (D):
- Describes the difficulty to write or understand a program (during a code review, for example)
- D = h1/2 *N2/h2
- Effort:
- E = D*V
The key figures usually match the actual measured values very well. The disadvantage is that the method only applies to individual functions and only measures lexical/textual complexity.