Dieser Artikel setzt die Veröffentlichungsreihe zur Verwendung von PVS-Studio in Cloud-Systemen fort. Dieses Mal schauen wir uns an, wie der Analyzer mit GitLab CI zusammenarbeitet, einem Produkt von GitLab Inc. Die Integration des Static Analyzers in ein CI-System ermöglicht die Erkennung von Fehlern direkt nach der Projekterstellung und ist eine äußerst effektive Methode, um die Kosten für die Fehlersuche zu senken.
Eine Liste unserer anderen Artikel zur Integration in Cloud-CI-Systeme:
Informationen zur verwendeten Software
GitLab ist ein Onlinedienst zur Verwaltung von Repositorys. Sie können es direkt in einem Browser auf der offiziellen Website verwenden, indem Sie Ihr Konto registrieren, oder es auf Ihrem eigenen Server installieren und bereitstellen.
PVS-Studio ist ein Tool zur Erkennung von Fehlern und potenziellen Schwachstellen im Quellcode von Programmen, die in C, C ++, C # und Java geschrieben wurden. Funktioniert in 64-Bit-Systemen unter Windows, Linux und MacOS und kann Code für 32-Bit-, 64-Bit- und Embedded-ARM-Plattformen analysieren. Wenn Sie den Analyzer zum ersten Mal verwenden, um Ihre Projekte zu überprüfen, empfehlen wir Ihnen, den
Artikel zu lesen, in dem
beschrieben wird, wie Sie die interessantesten PVS-Studio-Warnungen schnell überprüfen und die Funktionen des Tools bewerten können.
Das OBS-Projekt wird verwendet, um die Fähigkeiten des statischen Analysators in der Cloud zu demonstrieren.
Open Broadcaster Software ist eine kostenlose und offene Sammlung von Programmen für die Videoaufnahme und das Streaming. OBS bietet Geräte- und Quellenüberwachung in Echtzeit, Szenenkomposition, Dekodierung, Aufnahme und Ausstrahlung. Die Daten werden hauptsächlich über das Real Time Messaging-Protokoll übertragen und können an jede Quelle gesendet werden, die RTMP unterstützt. Das Programm verfügt über fertige Vorinstallationen für die Live-Übertragung auf den beliebtesten Streaming-Plattformen.
Konfiguration
Um mit GitLab zu arbeiten, gehen Sie auf die Website und klicken Sie auf
Registrieren :
Sie können sich registrieren, indem Sie Konten anderer Dienste wie GitHub, Twitter, Google, BitBucket, Saleforce verknüpfen oder einfach das offene Formular ausfüllen. Nach der Autorisierung lädt GitLab uns ein, ein Projekt zu erstellen:
Eine Liste der verfügbaren Plattformen zum Importieren von Dateien:
Lassen Sie uns aus Gründen der Übersichtlichkeit ein leeres Projekt erstellen:
Als nächstes müssen wir unser Projekt in das erstellte Repository hochladen. Verwenden Sie dazu die Hinweise, die im Fenster des erstellten Projekts angezeigt werden.
Wenn Sie die Aufgabe starten, entnimmt GitLab CI Anweisungen aus der Datei
.gitlab-ci.yml . Sie können es hinzufügen, indem Sie entweder
auf CI / CD einrichten klicken oder einfach ein lokales Repository erstellen und auf die Site hochladen. Folgen wir der ersten Option:
Erstellen Sie jetzt einen minimalen Wrapper für das Skript:
image: debian job: script:
Laden Sie den Analyzer und das Sendemail-Dienstprogramm herunter, die wir später benötigen werden:
- apt-get update && apt-get -y install wget gnupg - wget -O - https:
Als Nächstes installieren wir Abhängigkeiten und Dienstprogramme zum Erstellen von OBS:
- apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates
Jetzt müssen wir die Datei mit der Analyzer-Lizenz erstellen (Standardmäßig wird die Datei PVS-Studio.lic im Verzeichnis ../.config/PVS-Studio erstellt). Dabei muss die Lizenzdatei nicht in den laufenden Parametern des Analysators angegeben werden, sie wird automatisch abgerufen.):
- pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY
Hier sind
PVS_NAME und
PVS_KEY die Namen von Variablen, deren Werte wir in den Einstellungen angeben. Sie speichern den PVS-Studio-Login und den Lizenzschlüssel. Gehen Sie wie folgt vor, um die Werte festzulegen: Einstellungen> CI / CD> Variablen.
Erstellen Sie das Projekt mit cmake:
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4
Danach führen Sie den Analyzer aus:
- pvs-studio-analyzer analyze -o PVS-Studio.log
PVS-Studio.log speichert die Analyseergebnisse. Die resultierende Datei mit dem Bericht ist nicht zum Lesen vorgesehen. Um es für ein menschliches Auge zugänglich zu machen, benötigen wir das Dienstprogramm plog-converter. Dieses Programm konvertiert das Protokoll des Analysators in verschiedene Formate. Zum einfachen Lesen konvertieren wir das Protokoll in das HTML-Format:
- plog-converter -t html PVS-Studio.log -o PVS-Studio.html
Sie können den Bericht mit
Artefakten exportieren, aber wir ändern den Kurs und senden die Datei mit den Analyseergebnissen per E-Mail mit dem Dienstprogramm sendemail:
- sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html
Vollständige .gitlab-ci.yml:
image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https:
Klicken Sie auf
Änderungen festschreiben. Wenn wir alles richtig gemacht haben, sehen wir die Ausgabe:
Diese GitLab CI-Konfiguration ist gültig. Um den Fortschritt zu verfolgen, gehen wir zur Registerkarte
CI / CD> Pipelines .
Klicken Sie auf "
Laufen" . Es wird das Terminalfenster der virtuellen Maschine angezeigt, in dem unsere Konfigurationsdatei ausgeführt wird. Nach einer Weile bekommen wir eine Nachricht:
job succeeded.Es ist also Zeit, die HTML-Datei mit Warnungen zu öffnen, die per E-Mail gesendet werden.
Analyseergebnisse
Werfen wir einen Blick auf einige Warnungen aus dem Bericht, in denen Fehler im Open Broadcaster-Softwareprojekt aufgedeckt werden, um das Wesentliche der statischen Codeanalyse zu erfahren. Da der Hauptzweck des Artikels darin besteht, die Prinzipien der Interaktion zwischen PVS-Studio und GitLab CI / CD zu beschreiben, wurden nur einige nicht triviale Beispiele herausgegriffen. Wir sind bereit, den Autoren des Projekts eine zeitlich begrenzte Lizenz zu erteilen. Auf Wunsch können sie auch eine gründlichere Projektanalyse durchführen. Darüber hinaus können sie eine der
Möglichkeiten nutzen, um eine kostenlose PVS-Studio-Lizenz zu erhalten .
Außerdem kann jeder
einen Testschlüssel erhalten , um die PVS-Studio-Funktionen zu erkunden und seine Projekte zu überprüfen.
Schauen wir uns also einige Beispiele für gefundene Fehler in der Open Broadcaster-Software an.
Warnung N1V547 Ausdruck 'back_size' ist immer wahr. circlebuf.h (138)
struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... }
Die Zeile
if (data_end_pos> cb-> capacity) ist auf jeden Fall einen Blick wert. Wenn die Bedingung wahr ist, ist die in der folgenden Zeile definierte Variable
back_size immer größer als Null, da hier die Subtraktion des notorisch kleineren Werts von dem größeren Wert behandelt wird. Am Ende ist die Bedingung, die zwei Zeilen weiter unten steht, immer
wahr . Die redundante Bedingung ist nicht so harmlos, wenn der Code folgt und Daten ändert.
Warnungen N2, N3V629 Betrachten Sie den Ausdruck '1 << Einzug'. Bitverschiebung des 32-Bit-Wertes mit anschließender Erweiterung auf den 64-Bit-Typ. profiler.c (610)
static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... }
Verwirrte Operationen über 32-Bit- und 64-Bit-Typen sehen hier verdächtig aus. Zuerst wertet der Programmierer die Maske unter Verwendung von 32-Bit-Typen aus (Ausdruck
(1 << Einzug) - 1 ), danach wird sie implizit in den 64-Bit-Typ im Ausdruck
active & = ... erweitert. Höchstwahrscheinlich war bei der Auswertung der Maske auch die Verwendung von 64-Bit-Typen erforderlich.
Richtige Codeversion:
active &= ((uint64_t)(1) << indent) - 1;
Oder:
active &= (1ull << indent) - 1;
Übrigens, die Copy-Paste-Version dieses Blocks ist unten, der Analysator hat auch die Warnung dafür ausgegeben:
V629 Betrachten Sie den Ausdruck '1 << Indent'. Bitverschiebung des 32-Bit-Wertes mit anschließender Erweiterung auf den 64-Bit-Typ. profiler.c (719)
Warnung N4V761 Es wurden vier identische Textblöcke gefunden. 'obs-audio-controls.c' (353)
static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... }
Vier identische Blöcke. In fast allen Fällen weist ein solcher Code auf einen Fehler beim
Kopieren und Einfügen hin . Diese Funktionen müssen höchstwahrscheinlich mit unterschiedlichen Argumenten aufgerufen worden sein. Auch wenn nicht, sieht dieser Code seltsam aus. Eine gute Lösung wäre, den Block nur einmal zu schreiben und ihn in eine Schleife zu packen:
for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); }
Warnung n5V560 Ein Teil eines bedingten Ausdrucks ist immer falsch: '! Modifikatoren'. obs-hotkey.c (662)
typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... }
Definition der
load_modifier Funktion:
static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; }
Wie wir sehen können, ist
Modifiers ein Zeiger, der durch die Adresse des
Modifiers- Feldes der
Combo- Struktur initialisiert wird. Da sich sein Wert bis zur Überprüfung nicht ändert, bleibt er ungleich Null. Darüber hinaus wird der Zeiger nach der Initialisierung vor der Prüfung beim Aufrufen der Funktion
load_modifier verwendet, wo er dereferenziert wird. Dementsprechend ist die Prüfung von
! Modifiers sinnlos, da der Operator
&& bei der Auswertung des logischen Ausdrucks immer den Wert
false liefert. Ich denke, der Programmierer wollte einen ganzzahligen Wert anhand der Adresse überprüfen, die im
Modifikatorzeiger gespeichert ist, vergaß jedoch, diesen Zeiger dereferenzieren.
Aus diesem Grund sollte die Prüfung meines Erachtens wie folgt aussehen:
if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....)
Warnung N6V575 Der potenzielle Nullzeiger wird an die Funktion 'strncpy' übergeben. Untersuche das erste Argument. Zeilen prüfen: 2904, 2903. rtmp.c (2904)
static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... }
In den meisten
Fällen ist ein solcher Code nicht sicher, da er ignoriert, dass
malloc einen Nullzeiger zurückgeben kann. Wenn
malloc NULL zurückgibt, tritt in diesem Fall ein undefiniertes Verhalten auf, da das erste Argument der Funktion
strncpy den Wert
NULL hat.
Weitere Informationen dazu, warum es wichtig ist, den Rückgabewert der
Malloc- Funktion zu überprüfen, finden Sie im
entsprechenden Artikel .
Warnhinweise N7, N8, N9Ratet mal, welche Fälle falsche Berechnungen enthalten:
class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:
Analyzer-Warnungen:
- V636 Der Ausdruck 'i / 6' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (330)
- V636 Der Ausdruck 'i / 2' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (334)
- V636 Der Ausdruck 'i / 2' wurde implizit vom Typ 'size_t' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung einer expliziten Typumwandlung, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; Fensterprojektor.cpp (340)
Hier ist die richtige Antwort: In denen, in denen
ich nicht zum Schweben gezwungen bin. Der Analysator zeigt uns Fragmente mit ganzzahliger Teilung. Ein solcher Code funktioniert möglicherweise nicht so, wie es sich der Programmierer erhofft hatte.
Fazit
Wie wir sehen können, ist die Integration des PVS-Studio-Analysators in Ihr Projekt auf GitLab ein recht einfacher Vorgang. Dazu müssen Sie nur eine Konfigurationsdatei schreiben und in Ihrem Cloud-Repository ablegen. Da GitLab über eine eigene integrierte virtuelle Maschine verfügt, müssen wir nicht einmal viel Zeit für die Konfiguration des CI-Systems aufwenden. Mit der Codeüberprüfung können Sie Probleme direkt nach dem Build feststellen. Dies hilft, Probleme in der Phase zu beseitigen, in der ihre Komplexität und Kosten noch gering sind.