Dieser Artikel ist eine Fortsetzung einer Reihe von Veröffentlichungen zum Einsatz von PVS-Studio in Cloud-Systemen. Dieses Mal werden wir uns den Analyzer in Verbindung mit GitLab CI, einem Produkt von GitLab Inc., ansehen. Durch die Integration eines statischen Analysators in das CI-System können Sie Fehler unmittelbar nach der Erstellungsphase des Projekts identifizieren und die Kosten für das Erkennen von Fehlern auf effektive Weise senken.
Liste unserer anderen Artikel zur Integration in Cloud-CI-Systeme:
Informationen zur verwendeten Software
GitLab ist ein Onlinedienst zum Verwalten von Repositorys. Es kann direkt im Browser auf der offiziellen Website durch Registrieren eines Kontos verwendet oder auf Ihrem eigenen Server installiert und bereitgestellt werden.
PVS-Studio ist ein Tool zur statischen Code-Analyse, mit dem Fehler und potenzielle Schwachstellen in Programmen erkannt werden können, die in C, C ++, C # und Java geschrieben wurden. Es funktioniert auf 64-Bit-Systemen unter Windows, Linux und macOS und kann Code analysieren, der für 32-Bit-, 64-Bit- und eingebettete ARM-Plattformen entwickelt wurde. Wenn Sie zum ersten Mal statischen Code analysieren, um Ihre Projekte zu testen, empfehlen wir Ihnen, den
Artikel zu lesen, in dem Sie erfahren, wie Sie die interessantesten PVS-Studio-Warnungen schnell erkennen und die Funktionen dieses Tools bewerten können.
Das OBS-Projekt wird verwendet, um die Funktionsweise eines statischen Analysators in der Cloud zu demonstrieren.
Open Broadcaster Software ist eine kostenlose Open-Source-Software-Suite für Videoaufzeichnung und Streaming. OBS bietet die Möglichkeit, Geräte und Quellen in Echtzeit abzufangen, Szenen zu komponieren, zu dekodieren, aufzuzeichnen und zu senden. Die Datenübertragung erfolgt hauptsächlich über das Real Time Messaging-Protokoll. Die Daten können an jede Quelle übertragen werden, die RTMP unterstützt. Das Programm verfügt über vorgefertigte Voreinstellungen für die direkte Übertragung an die beliebtesten Streaming-Plattformen.
Anpassung
Um mit GitLab zu beginnen, gehen Sie zur Site und klicken Sie auf die Schaltfläche
Registrieren :
Sie können sich registrieren, indem Sie die Konten von Diensten wie GitHub, Twitter, Google, BitBucket, Saleforce verknüpfen oder einfach das sich öffnende Formular ausfüllen. Nach der Autorisierung trifft GitLab uns mit einem Vorschlag, ein Projekt zu erstellen:
Liste der Plattformen, von denen Sie importieren können:
Erstellen Sie aus Gründen der Übersichtlichkeit ein leeres Projekt:
Als nächstes müssen wir unser Projekt in das erstellte Repository hochladen. Dies erfolgt über die Eingabeaufforderungen, die im Fenster des erstellten Projekts angezeigt werden.
Wenn der Task
gestartet wird, entnimmt GitLab CI Anweisungen aus der
Datei .gitlab-ci.yml . Sie können es hinzufügen, indem Sie entweder auf die Schaltfläche
CI / CD einrichten klicken oder es einfach im lokalen Repository erstellen und auf die Site hochladen. Wir werden die erste Option verwenden:
Machen wir einen minimalen Wrapper für das Skript:
image: debian job: script:
Laden Sie den Analyzer und das Sendemail-Dienstprogramm herunter, die wir in Zukunft benötigen werden:
- apt-get update && apt-get -y install wget gnupg - wget -O - https:
Installieren Sie als Nächstes die 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 eine Datei mit der Analyzer-Lizenz erstellen. Standardmäßig wird die Datei PVS-Studio.lic im Verzeichnis ../.config/PVS-Studio erstellt. In diesem Fall kann die Lizenzdatei in den Startparametern des Analysators weggelassen werden. Sie wird automatisch abgerufen:
- pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY
Hier sind
PVS_NAME und
PVS_KEY die Namen der Variablen, deren Werte wir in den Einstellungen angeben. Sie speichern den PVS-Studio-Login und den Lizenzschlüssel. Um ihre Werte festzulegen, gehen Sie zu: Einstellungen> CI / CD> Variablen.
Erstellen Sie das Projekt mit cmake:
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4
Führen Sie dann den Analysator aus:
- pvs-studio-analyzer analyze -o PVS-Studio.log
PVS-Studio.log speichert die Analyseergebnisse. Die resultierende Berichtsdatei ist nicht zum Lesen gedacht. Um sie in eine für das menschliche Auge zugängliche Ansicht zu bringen, benötigen wir das Dienstprogramm plog-converter. Dieses Programm konvertiert das Analyseprotokoll in verschiedene Formate. Zur besseren Lesbarkeit werden wir in das HTML-Format konvertieren:
- plog-converter -t html PVS-Studio.log -o PVS-Studio.html
Der Bericht kann mithilfe von
Artefakten heruntergeladen werden. Wir werden jedoch eine alternative Methode verwenden und die Datei mit den Ergebnissen des Analysators mithilfe des Dienstprogramms sendemail an die E-Mail senden:
- 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 die Schaltfläche
Änderungen übernehmen . Wenn wir alles richtig gemacht haben, sehen wir die Aufschrift:
Diese GitLab CI-Konfiguration ist gültig. Um den Fortschritt zu verfolgen, wechseln Sie zur Registerkarte
CI / CD> Pipelines.Klicken Sie auf
Laufen . Wir werden das Terminalfenster der virtuellen Maschine sehen, auf der unsere Konfigurationsdatei läuft. Nach einiger Zeit erhalten wir die Meldung:
job succeeded.Also, Zeit, um auf die Mail zu gehen und die HTML-Datei mit Warnungen zu öffnen.
Validierungsergebnisse
Schauen wir uns nun einige Warnungen aus dem Bericht an, die auf Fehler im Open Broadcaster Software-Projekt hinweisen, um das Wesentliche der statischen Code-Analyse zu demonstrieren. Der Artikel soll die Prinzipien der Interaktion zwischen PVS-Studio und GitLab CI / CD beschreiben, sodass nur einige interessante Codefragmente mit Fehlern ausgeschrieben wurden. Wir sind bereit, den Autoren des Projekts eine temporäre Lizenz zu erteilen, und wenn sie dies wünschen, können sie eine gründlichere Analyse des Projekts durchführen. Oder sie können eine der
Optionen für die kostenlose Lizenzierung von PVS-Studio nutzen.
Jeder kann auch unabhängig
einen Testschlüssel erhalten, um die Funktionen von PVS-Studio zu erkunden und seine Projekte zu überprüfen.
Schauen wir uns also einige Beispiele für Fehler an, die in Open Broadcaster Software gefunden wurden.
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); } .... }
Beachten Sie die Zeile:
Wenn (data_end_pos> cb-> capacity) , bedeutet die Erfüllung der Bedingung, dass die Variable
back_size , die in der
unteren Zeile definiert ist, immer größer als Null ist, da die offensichtlich kleinere von der offensichtlich größeren subtrahiert wird, was bedeutet, dass die Bedingung ist Eine weitere Zeile darunter wird immer
wahr sein . Eine Überschussbedingung ist nicht so harmlos, wenn sich darunter ein Code befindet, der 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; .... }
Die Mischung der Operationen auf 32-Bit- und 64-Bit-Typen sieht hier verdächtig aus. Zuerst wird die Maske mit 32-Bit-Typen berechnet (Ausdruck
(1 << Einzug) - 1 ), und dann wird sie im Ausdruck
active & = ... implizit auf den 64-Bit-Typ erweitert
. Höchstwahrscheinlich wurde bei der Berechnung der Maske auch die Verwendung von 64-Bit-Typen angenommen.
Die richtige Version des Codes:
active &= ((uint64_t)(1) << indent) - 1;
Oder:
active &= (1ull << indent) - 1;
Kopieren und Einfügen dieses Codeblocks ist auch im Folgenden aufgeführt, zu dem der Analysator ebenfalls eine Warnung ausgegeben hat:
V629 Prüfen Sie den Ausdruck '1 << Einzug'. 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. Fast immer weist ein solcher Code auf einen Fehler beim
Kopieren und Einfügen hin . Höchstwahrscheinlich sollten diese Funktionen mit unterschiedlichen Argumenten aufgerufen worden sein. Auch wenn nicht, sieht dieser Code komisch aus. Eine gute Lösung wäre, einen Block 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)) { .... } .... }
Die 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 mit der Adresse des
Modifiers- Feldes der
Combo- Struktur initialisiert wird. Da sich sein Wert nicht zum Ort der Überprüfung ändert, bleibt er ungleich Null. Außerdem wird der Zeiger zwischen dem Ort der Initialisierung und der Überprüfung beim Aufrufen der Funktion
load_modifier verwendet, wo er dereferenziert wird. Dementsprechend macht das Überprüfen von
! Modifiers keinen Sinn, da der Operator
&& dazu führt , dass bei der Auswertung eines logischen Ausdrucks immer
Falsch ausgegeben wird. Ich denke, dass der Programmierer den ganzzahligen Wert überprüfen wollte, der sich an der Adresse befindet, die im
Modifikatorzeiger gespeichert ist, aber vergaß, diesen Zeiger dereferenzieren.
Das heißt Mir scheint, dass der Scheck so aussehen sollte:
if (!*modifiers && ....) : 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); .... }
Dieser Code ist häufig unsicher, da er nicht berücksichtigt, dass
malloc einen Nullzeiger zurückgeben kann. Wenn
malloc NULL zurückgibt,
tritt in diesem Fall
ein undefiniertes Verhalten auf , da das erste Argument für
strncpy NULL ist .
Weitere
Informationen darüber, warum es wichtig ist, den Rückgabewert der
Malloc- Funktion zu überprüfen, finden Sie im
entsprechenden Artikel.Warnhinweise N7, N8, N9Versuchen wir zu erraten, in welchen Fällen die falschen Berechnungen auftreten können:
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)
Die richtige Antwort: In denen, in denen
ich nicht zum
Schweben gezwungen
bin . In den Ausdrücken, die der Analysator uns zeigt, tritt eine ganzzahlige Division auf. Dieser Code funktioniert möglicherweise nicht genau wie vom Programmierer erwartet.
Fazit
Wie wir sehen können, ist die Integration des statischen Analysators PVS-Studio in Ihr GitLab-Projekt ganz einfach. Dazu schreiben Sie einfach eine Konfigurationsdatei und legen sie in Ihrem Cloud-Repository ab. 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. Wenn Sie den Code überprüfen, können Sie Probleme unmittelbar nach dem Build identifizieren, um sie zu beheben, wenn die Komplexität und die Kosten für die Bearbeitung noch gering sind.

Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Vladislav Stolyarov.
PVS-Studio in den Wolken: GitLab CI / CD .