Static Analysis Light

Mit der „Statischen Codeanalyse“ prüft TwinCAT 3 PLC vor dem Laden auf das Zielsystem, ob der Quellcode eines Projekts festgelegten Kodierrichtlinien folgt.

Die lizenzfreie Variante der statischen Codeanalyse ist das „Static Analysis Light“. Die dort konfigurierten Prüfungen werden automatisch im Anschluss an jede erfolgreiche Codeerzeugung durchgeführt. Sie definieren den gewünschten Satz an Regeln in den SPS-Projekteigenschaften in der Kategorie Static Analysis.

Abweichungen von den Regeln werden als Fehlermeldungen in der Fehlerliste ausgegeben. Jede Regel besitzt eine eindeutige Nummer. Wenn während der Statischen Analyse die Verletzung einer Regel festgestellt wird, wird die Nummer zusammen mit einer Fehlerbeschreibung gemäß folgender Syntax in der Fehlerliste ausgegeben. Die Abkürzung „SA“ weist dabei auf „Static Analysis“ hin.

Syntax: "SA<Regelnummer>: <Regelbeschreibung>"

Beispiel für Regelnummer 33 (Nicht verwendete Variablen): "SA0033: Nicht verwendet: Variable 'bSample'"

Static Analysis Light 1:

TwinCAT analysiert nur den Programmcode des aktuellen Projekts, Bibliotheken bleiben unbeachtet!

Static Analysis Light 2:

Beachten Sie, dass das Static Analysis Light automatisch im Anschluss an den erfolgreichen Übersetzungsprozess ausgeführt wird. Falls die Codegenerierung hingegen nicht erfolgreich war, d.h. wenn der Compiler Kompilierfehler festgestellt hat, wird das Static Analysis Light nicht ausgeführt.

Static Analysis Light 3:

Mithilfe von Pragmas können Sie Prüfungen für bestimmte Codeteile ausschalten (siehe unten).

Konfiguration des Regelsatzes

Die Kategorie Static Analysis der SPS-Projekteigenschaften definiert die Prüfungen, die die Light-Version der statischen Codeanalyse bei jeder Codeerzeugung durchführt.

Static Analysis Light 4:

Geltungsbereich der Static-Analysis-Konfiguration

Die Einstellungen, die Sie in der Kategorie Static Analysis der SPS-Projekteigenschaften vornehmen, sind sogenannte Solution options und wirken sich daher nicht nur auf das SPS-Projekt aus, dessen Eigenschaften Sie aktuell bearbeiten. Der konfigurierte Regelsatz wird für alle SPS-Projekte übernommen, die sich in der Entwicklungsumgebung befinden.

Verfügbare Regeln innerhalb des Static Analysis Light:

SA0033: Nicht verwendete Variablen

Funktion

Ermittelt Variablen, die deklariert sind, aber innerhalb des kompilierten Programmcodes nicht verwendet werden.

Begründung

Nicht verwendete Variablen machen ein Programm weniger gut lesbar und wartbar. Nicht verwendete Variablen belegen unnötig Speicher und kosten bei der Initialisierung unnötig Laufzeit.

Wichtigkeit

Mittel

PLCopen-Regel

CP22/CP24

 

SA0028: Überlappende Speicherbereiche

Funktion

Ermittelt die Stellen, durch die zwei oder mehr Variablen denselben Speicherplatz belegen.

Begründung

Wenn zwei Variablen auf dem gleichen Speicherplatz liegen, dann kann sich der Code sehr unerwartet verhalten. Dies ist in jedem Fall zu vermeiden. Wenn es unumgänglich ist, einen Wert in verschiedenen Interpretationen zu verwenden, zum Beispiel einmal als DINT und einmal als REAL, dann sollten Sie eine UNION definieren. Auch über einen Pointer können Sie auf einen Wert anders getypt zugreifen, ohne dass der Wert umgewandelt wird.

Wichtigkeit

Hoch

Beispiel:

In dem folgenden Beispiel verwenden beide Variablen Byte 21, d.h. die Speicherbereiche der Variablen überlappen.

PROGRAM MAIN
VAR
    nVar1 AT%QB21  : INT;        // => SA0028
    nVar2 AT%QD5   : DWORD;      // => SA0028
END_VAR

 

SA0006: Schreibzugriff aus mehreren Tasks

Funktion

Ermittelt Variablen, auf die von mehr als einer Task geschrieben wird.

Begründung

Eine Variable, die in mehreren Tasks geschrieben wird, kann unter Umständen ihren Wert unerwartet ändern. Das kann zu verwirrenden Situationen führen. Stringvariablen und auf einigen 32-Bit-Systemen auch 64-Bit-Integer-Variablen können sogar einen inkonsistenten Zustand bekommen, wenn die Variable gleichzeitig in zwei Tasks geschrieben wird.

Ausnahme

In bestimmten Fällen kann es nötig sein, dass mehrere Tasks eine Variable schreiben. Stellen Sie dann sicher, beispielsweise durch die Verwendung von Semaphoren, dass der Zugriff nicht zu einem inkonsistenten Zustand führt.

Wichtigkeit

Hoch

PLCopen-Regel

CP10

 

Static Analysis Light 5:

Sehen Sie auch die Regel SA0103.

Static Analysis Light 6:

Aufruf entspricht Schreibzugriff

Bitte beachten Sie, dass Aufrufe als Schreibzugriff interpretiert werden. Beispielsweise wird der Aufruf einer Methode für eine Funktionsbausteininstanz als Schreibzugriff auf die Funktionsbausteininstanz angesehen. Eine genauere Analyse der Zugriffe und Aufrufe ist z.B. aufgrund von virtuellen Aufrufen (Zeiger, Interface) nicht möglich.

Wenn Sie die Regel SA0006 für eine Variable (z.B. für eine Funktionsbausteininstanz) deaktivieren möchten, können Sie das folgende Attribut oberhalb der Variablendeklaration einfügen: {attribute 'analysis' := '-6'}

Beispiele:

Die beiden globalen Variablen nVar und bVar werden von zwei Tasks geschrieben.

Globale Variablenliste:

VAR_GLOBAL
    nVar  : INT;
    bVar  : BOOL;
END_VAR

Programm MAIN_Fast, aufgerufen von der Task PlcTaskFast:

nVar := nVar + 1;                // => SA0006 
bVar := (nVar > 10);             // => SA0006

Programm MAIN_Slow, aufgerufen von der Task PlcTaskSlow:

nVar := nVar + 2;                // => SA0006 
bVar := (nVar < -50);            // => SA0006

 

SA0004: Mehrfacher Schreibzugriff auf Ausgang

Funktion

Ermittelt Ausgänge, die an mehr als einer Position geschrieben werden.

Begründung

Die Wartbarkeit leidet, wenn ein Ausgang an verschiedenen Stellen im Code geschrieben wird. Es ist dann unklar, welcher Schreibzugriff derjenige ist, der tatsächlich Auswirkungen im Prozess hat. Gute Praxis ist es, die Berechnung der Ausgangsvariablen in Hilfsvariablen durchzuführen und an einer Stelle am Ende des Zyklus den berechneten Wert zuzuweisen.

Ausnahme

Es wird kein Fehler ausgegeben, wenn eine Ausgangsvariable in verschiedenen Zweigen von IF- bzw. CASE-Anweisungen geschrieben wird.

Wichtigkeit

Hoch

PLCopen-Regel

CP12

 

Static Analysis Light 7:

Diese Regel kann nicht über ein Pragma oder Attribut abgeschaltet werden!
Weitere Informationen zu Attributen finden Sie unter Pragmas und Attribute.

Beispiel:

Globale Variablenliste:

VAR_GLOBAL
    bVar     AT%QX0.0 : BOOL;
    nSample  AT%QW5   : INT;
END_VAR

Programm MAIN:

PROGRAM MAIN
VAR
    nCondition        : INT;
END_VAR
IF nCondition < INT#0 THEN
    bVar    := TRUE;             // => SA0004
    nSample := INT#12;           // => SA0004
END_IF
 
CASE nCondition OF
    INT#1:
        bVar := FALSE;           // => SA0004
 
    INT#2:
        nSample := INT#11;       // => SA0004
 
ELSE
     bVar    := TRUE;            // => SA0004
     nSample := INT#9;           // => SA0004
END_CASE

 

SA0027: Mehrfachverwendung des Namens

Funktion

Ermittelt die Mehrfachverwendung eines Namens/Bezeichners einer Variable oder eines Objekts (POU) innerhalb des Gültigkeitsbereichs eines Projekts. Die folgenden Fälle werden abgedeckt:

  • Der Name einer Enumerationskonstanten ist identisch mit dem Namen in einer anderen Enumeration innerhalb der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name einer Variablen ist identisch mit dem Namen eines anderen Objekts in der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name einer Variablen ist identisch mit dem Namen einer Enumerationskonstanten in einer Enumeration in der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name eines Objekts ist identisch mit dem Namen eines anderen Objekts in der Applikation oder in einer eingebundenen Bibliothek.

Begründung

Gleiche Namen können beim Lesen des Codes verwirrend sein. Sie können zu Fehlern führen, wenn unbeabsichtigt auf das falsche Objekt zugegriffen wird. Definieren und befolgen Sie deshalb Namenskonventionen zur Vermeidung solcher Situationen.

Ausnahme

Enumerationen, die mit dem Attribut 'qualified_only' deklariert sind, sind von der SA0027-Prüfung ausgenommen, da auf ihre Elemente nur qualifiziert zugegriffen werden kann.

Wichtigkeit

Mittel

Beispiel:

Das folgende Beispiel erzeugt Fehler/Warnung SA0027, da die Bibliothek Tc2_Standard im Projekt eingebunden ist, welche den Funktionsbaustein TON zur Verfügung stellt.

PROGRAM MAIN
VAR
    ton  : INT;                  // => SA0027
END_VAR

 

SA0167: Temporäre Funktionsbausteininstanzen

Funktion

Ermittelt Funktionsbausteininstanzen, die als temporäre Variable deklariert sind. Dies betrifft Instanzen, die in einer Methode oder in einer Funktion oder als VAR_TEMP deklariert sind, und die deshalb in jedem Abarbeitungszyklus bzw. bei jedem Bausteinaufruf neu initialisiert werden.

Begründung

Funktionsbausteine haben einen Zustand, der meist über mehrere SPS-Zyklen hinweg erhalten bleibt. Eine Instanz auf dem Stack existiert nur für die Dauer des Funktionsaufrufs. Es ist daher nur selten sinnvoll, eine Instanz als temporäre Variable anzulegen. Zweitens sind Funktionsbausteininstanzen häufig groß und verbrauchen sehr viel Platz auf dem Stack (der auf Steuerungen meist begrenzt ist). Drittens kann die Initialisierung und häufig auch die Terminierung eines Funktionsbausteins ziemlich viel Zeit in Anspruch nehmen.

Wichtigkeit

Mittel

Beispiele:

Methode FB_Sample.SampleMethod:

METHOD SampleMethod : INT
VAR_INPUT
END_VAR
VAR
    fbTrigger : R_TRIG;          // => SA0167
END_VAR

Funktion F_Sample:

FUNCTION F_Sample : INT
VAR_INPUT
END_VAR
VAR
    fbSample  : FB_Sample;       // => SA0167
END_VAR

Programm MAIN:

PROGRAM MAIN
VAR_TEMP
    fbSample  : FB_Sample;       // => SA0167
    nReturn   : INT;
END_VAR
nReturn := F_Sample();

 

SA0175: Verdächtige Operation auf String

Funktion

Ermittelt Codestellen, die bei einer UTF-8-Kodierung verdächtig sind.

Erfasste Konstrukte

  1. Indexzugriff auf einen Single-Byte-String
    • Beispiel: sVar[2]
    • Meldung: Verdächtige Operation auf String: Indexzugriff '<expression>'
  2. Adresszugriff auf einen Single-Byte-String
    • Beispiel: ADR(sVar)
    • Meldung: Verdächtige Operation auf String: Möglicher Indexzugriff '<expression>'
  3. Aufruf einer String-Funktion der Tc2_Standard-Bibliothek außer CONCAT und LEN
    • Beispiel: FIND(sVar, 'a');
    • Meldung: Verdächtige Operation auf String: Möglicher Indexzugriff '<expression>'
  4. Einzelnes Byte-Literal, das Nicht-ASCII-Zeichen enthält
    • Beispiele:
      sVar := '99€';
      sVar := 'Ä';
    • Meldung: Verdächtige Operation auf String: Literal '<literal>' enthält Nicht-ASCII-Zeichen

Wichtigkeit

Mittel

Beispiele:

VAR
    sVar  : STRING;
    pVar  : POINTER TO STRING;
    nVar  : INT;
END_VAR
// 1) SA0175: Suspicious operation on string: Index access
sVar[2];                         // => SA0175
 
// 2) SA0175: Suspicious operation on string: Possible index access
pVar := ADR(sVar);               // => SA0175
 
// 3) SA0175: Suspicious operation on string: Possible index access
nVar := FIND(sVar, 'a');         // => SA0175
 
// 4) SA0175: Suspicious operation on string: Literal '<...>' contains Non-ASCII character
sVar := '99€';                   // => SA0175
sVar := 'Ä';                     // => SA0175

Pragmas und Attribute für Static Analysis Light

Mithilfe eines Pragmas bzw. eines Attributs können Sie Codeteile aus der Prüfung ausklammern. Verwenden Sie das Pragma {analysis …}, um Kodierregeln im Implementierungsteil auszuschalten und das Attribut {attribute 'analysis' := '...'}, um Kodierregeln im Deklarationsteil auszuschalten.

Voraussetzung: Sie haben die Regeln in den Projekteigenschaften aktiviert.

Außerdem können Sie das Attribut {attribute 'no-analysis'} verwenden, um Programmierobjekte von der Statischen Analyse auszuschließen.

Static Analysis Light 8:

Regeln, die in den Projekteigenschaften deaktiviert sind, können Sie auch nicht über Pragma oder Attribut aktivieren.

Static Analysis Light 9:

Regel SA0004 (Mehrfacher Schreibzugriff auf Ausgang) kann nicht über Pragma deaktiviert werden.

Pragma {analysis ...}

Das Pragma {analysis -/+<Regelnummer>} können Sie im Implementierungsteil eines Programmierbausteins verwenden, um einzelne Kodierregeln für die nachfolgenden Codezeilen auszuschalten. Sie deaktivieren Codierregeln durch die Angabe der Regelnummern und einem vorangestellten Minuszeichen (“-”). Zur Aktivierung wird ein Pluszeichen (“+”) vorangestellt. Mit Hilfe einer Kommaseparierung können Sie im Pragma beliebig viele Regeln angeben.

Einfügeort:

  • Deaktivierung von Regeln: Im Implementierungsteil vor der ersten Codezeile, ab der die Codeanalyse deaktiviert wird, mit {analysis - ...}.
  • Aktivierung von Regeln: Nach der letzten Zeile der Deaktivierung mit {analysis + ...}.
  • Für die Regel SA0164 kann das Pragma auch im Deklarationsteil vor einem Kommentar eingefügt werden.

Syntax:

  • Deaktivierung von Regeln:
    • eine Regel: {analysis -<Regelnummer>}
    • mehrere Regeln: {analysis -<Regelnummer>, -<weitere Regelnummer>, -<weitere Regelnummer>}
  • Aktivierung von Regeln:
    • eine Regel: {analysis +<Regelnummer>}
    • mehrere Regeln: {analysis +<Regelnummer>, +<weitere Regelnummer>, +<weitere Regelnummer>}

Beispiele:

Sie möchten Regel 24 (nur getypte Literale erlaubt) für eine Zeile deaktivieren (d.h. es ist in diesen Zeilen nicht nötig, "nTest := DINT#99" zu schreiben) und danach wieder aktivieren:

{analysis -24}
nTest := 99;
{analysis +24}
nVar := INT#2;

Angabe mehrerer Regeln:

{analysis -10, -24, -18}

Attribut {attribute 'analysis' := '...'}

Das Attribut {attribute 'analysis' := '-<Regelnummer>'} können Sie verwenden, um bestimmte Regeln für einzelne Deklarationen oder für ein ganzes Programmierobjekt abzuschalten. Sie deaktivieren die Kodierregel durch die Angabe der Regelnnummer(n) und einem vorangestellten Minuszeichen. Sie können im Attribut beliebig viele Regeln angeben.

Einfügeort:

oberhalb der Deklaration eines Programmierobjekts oder in der Zeile oberhalb einer Variablendeklaration

Syntax:

  • eine Regel: {attribute 'analysis' := '-<Regelnummer>'}
  • mehrere Regeln: {attribute 'analysis' := '-<Regelnummer>, -<weitere Regelnummer>, -<weitere Regelnummer>'}

Beispiele:

Sie möchten Regel 33 (Nicht verwendete Variablen) für alle Variablen der Struktur ausschalten.

{attribute 'analysis' := '-33'}
TYPE ST_Sample :
STRUCT
    bMember  : BOOL;
    nMember  : INT;
END_STRUCT
END_TYPE

 

Sie möchten die Prüfung von Regel 28 (Überlappende Speicherbereiche) und von Regel 33 (Nicht verwendete Variablen) für die Variable nVar1 ausschalten.

PROGRAM MAIN
VAR
    {attribute 'analysis' := '-28, -33'}
    nVar1 AT%QB21  : INT;
    nVar2 AT%QD5   : DWORD;
 
    nVar3 AT%QB41  : INT;
    nVar4 AT%QD10  : DWORD;
END_VAR

 

Sie möchten Regel 6 (Gleichzeitiger Zugriff) für eine globale Variable ausschalten, sodass keine Fehlermeldung generiert wird, wenn die Variable von mehr als einer Task geschrieben wird.

VAR_GLOBAL
    {attribute 'analysis' := '-6'}
    nVar  : INT;
    bVar  : BOOL;
END_VAR

Attribut {attribute 'no-analysis'}

Das Attribut {attribute 'no-analysis'} können Sie verwenden, um ein gesamtes Programmierobjekt von der Prüfung durch die Statische Analyse auszuschließen. Für dieses Programmierobjekt wird die Prüfung der Kodierregeln, der Namenskonventionen und der unzulässigen Symbole nicht durchgeführt.

Einfügeort:

oberhalb der Deklaration eines Programmierobjekts

Syntax:

{attribute 'no-analysis'}

Beispiele:

{attribute 'qualified_only'}
{attribute 'no-analysis'}
VAR_GLOBAL
    …
END_VAR
{attribute 'no-analysis'}
PROGRAM MAIN
VAR
    …
END_VAR