Travis CI ist ein verteilter Webdienst zum Erstellen und Testen von Software, der GitHub als Quellcode-Hosting verwendet. Zusätzlich zu den oben genannten Betriebsszenarien können Sie dank der umfangreichen Konfigurationsmöglichkeiten Ihre eigenen hinzufügen. In diesem Artikel konfigurieren wir Travis CI mithilfe des PPSSPP-Codebeispiels für die Arbeit mit PVS-Studio.
Einführung
Travis CI einrichten
Wir benötigen ein Repository auf GitHub, in dem sich das von uns benötigte Projekt befindet, sowie einen Schlüssel für PVS-Studio (den Sie erhalten können).
Gehen wir zur Website
Für den Test habe ich PPSSPP geforkt.
Wir aktivieren das Repository, das wir sammeln möchten:
Im Moment kann Travis CI unser Projekt nicht bauen, da keine Bauanleitung vorliegt. Es ist also Zeit für die Konfiguration.
Während der Analyse werden uns einige Variablen nützlich sein, beispielsweise der Schlüssel für PVS-Studio, dessen Angabe in der Konfigurationsdatei unerwünscht wäre. Fügen wir also Umgebungsvariablen mithilfe der Build-Einstellungen in Travis CI hinzu:
Wir brauchen:
- PVS_USERNAME – Benutzername
- PVS_KEY – Schlüssel
- MAIL_USER – E-Mail, die zum Senden des Berichts verwendet wird
- MAIL_PASSWORD – E-Mail-Passwort
Die letzten beiden sind optional. Diese werden für den Versand der Ergebnisse per Post verwendet. Wenn Sie den Bericht auf andere Weise verbreiten möchten, müssen Sie dies nicht angeben.
Daher haben wir die benötigten Umgebungsvariablen hinzugefügt:
Jetzt erstellen wir eine Datei .travis.yml und platzieren Sie es im Stammverzeichnis des Projekts. PPSSPP hatte bereits eine Konfigurationsdatei für Travis CI, diese war jedoch zu groß und für das Beispiel völlig ungeeignet, sodass wir sie stark vereinfachen und nur die Grundelemente belassen mussten.
Geben wir zunächst die Sprache, die Version von Ubuntu Linux, die wir in der virtuellen Maschine verwenden möchten, und die für den Build erforderlichen Pakete an:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
Alle aufgeführten Pakete werden ausschließlich für PPSSPP benötigt.
Jetzt geben wir die Montagematrix an:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Etwas mehr über den Abschnitt Matrix. In Travis CI gibt es zwei Möglichkeiten, Build-Optionen zu erstellen: Die erste besteht darin, eine Liste von Compilern, Betriebssystemtypen, Umgebungsvariablen usw. anzugeben, woraufhin eine Matrix aller möglichen Kombinationen generiert wird; der zweite ist ein expliziter Hinweis auf die Matrix. Natürlich können Sie diese beiden Ansätze kombinieren und einen Einzelfall hinzufügen oder ihn im Gegenteil mithilfe des Abschnitts ausschließen ausschließen. Mehr dazu können Sie hier lesen
Es bleibt nur noch die Bereitstellung projektspezifischer Montageanleitungen:
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
Mit Travis CI können Sie Ihre eigenen Befehle für verschiedene Lebensphasen einer virtuellen Maschine hinzufügen. Abschnitt before_install wird vor der Installation von Paketen ausgeführt. Dann installieren, was auf die Installation von Paketen aus der Liste folgt addons.aptwas wir oben angedeutet haben. Die Montage selbst erfolgt in Skript. Wenn alles gut gelaufen ist, dann sind wir dabei nach_erfolg (In diesem Abschnitt führen wir eine statische Analyse durch.) Dies sind nicht alle Schritte, die geändert werden können. Wenn Sie mehr benötigen, sollten Sie einen Blick darauf werfen
Zur besseren Lesbarkeit wurden die Befehle in einem separaten Skript platziert .travis.sh, die im Projektstammverzeichnis platziert wird.
Wir haben also die folgende Datei .travis.yml:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
matrix:
include:
- os: linux
compiler: "gcc"
env: PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
Vor der Installation der Pakete aktualisieren wir die Submodule. Dies wird zum Erstellen von PPSSPP benötigt. Fügen wir die erste Funktion hinzu .travis.sh (Beachten Sie die Erweiterung):
travis_before_install() {
git submodule update --init --recursive
}
Nun kommen wir direkt zum Einrichten des automatischen Starts von PVS-Studio in Travis CI. Zuerst müssen wir das PVS-Studio-Paket auf dem System installieren:
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
Zu Beginn der Funktion travis_install Wir installieren die benötigten Compiler mithilfe von Umgebungsvariablen. Dann wenn die Variable $PVS_ANALYZE speichert Wert Ja (wir haben es im Abschnitt angegeben env während der Build-Matrix-Konfiguration) installieren wir das Paket pvs-studio. Darüber hinaus werden auch Pakete angezeigt libio-socket-ssl-perl и libnet-ssleay-perlSie sind jedoch für den Versand der Ergebnisse erforderlich. Sie sind also nicht erforderlich, wenn Sie eine andere Methode zur Übermittlung Ihres Berichts gewählt haben.
Funktion download_extract lädt das angegebene Archiv herunter und entpackt es:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Es ist Zeit, das Projekt zusammenzustellen. Dies geschieht im Abschnitt Skript:
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
Tatsächlich handelt es sich hierbei um eine vereinfachte Originalkonfiguration, mit Ausnahme dieser Zeilen:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
In diesem Codeabschnitt legen wir fest cmake Flag zum Exportieren von Kompilierungsbefehlen. Dies ist für einen statischen Code-Analysator erforderlich. Mehr dazu lesen Sie im Artikel „
Wenn die Montage erfolgreich war, dann geht es weiter nach_erfolg, wo wir statische Analysen durchführen:
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
Schauen wir uns die folgenden Zeilen genauer an:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
Die erste Zeile generiert eine Lizenzdatei aus dem Benutzernamen und Schlüssel, die wir ganz am Anfang beim Einrichten der Travis CI-Umgebungsvariablen angegeben haben.
Die zweite Zeile startet direkt die Analyse. Flagge -J Legt die Anzahl der Threads für die Analyse fest, Flag -l zeigt Lizenz, Flagge an -Ö definiert die Datei zur Ausgabe von Protokollen und das Flag -disableLicenseExpirationCheck für Testversionen erforderlich, da standardmäßig pvs-studio-analyzer warnt den Benutzer, dass die Lizenz bald abläuft. Um dies zu verhindern, können Sie dieses Flag angeben.
Die Protokolldatei enthält Rohausgaben, die ohne Konvertierung nicht gelesen werden können. Daher müssen Sie die Datei zunächst lesbar machen. Lassen Sie uns die Protokolle weiterleiten Plog-Konverter, und die Ausgabe ist eine HTML-Datei.
In diesem Beispiel habe ich mich entschieden, Berichte per E-Mail mit dem Befehl zu versenden E-Mail senden.
Als Ergebnis haben wir die folgende Datei erhalten .travis.sh:
#/bin/bash
travis_before_install() {
git submodule update --init --recursive
}
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
set -e
set -x
$1;
Jetzt ist es an der Zeit, die Änderungen in das Git-Repository zu übertragen, woraufhin Travis CI den Build automatisch ausführt. Klicken Sie auf „ppsspp“, um zu den Build-Berichten zu gelangen:
Wir sehen einen Überblick über den aktuellen Build:
Wenn der Build erfolgreich abgeschlossen ist, erhalten wir eine E-Mail mit den Ergebnissen der statischen Analyse. Natürlich ist der Versand per Post nicht die einzige Möglichkeit, einen Bericht zu erhalten. Sie können eine beliebige Implementierungsmethode wählen. Es ist jedoch wichtig zu bedenken, dass nach Abschluss des Builds kein Zugriff mehr auf die Dateien der virtuellen Maschine möglich ist.
Fehlerzusammenfassung
Den schwierigsten Teil haben wir erfolgreich gemeistert. Stellen wir nun sicher, dass sich all unsere Bemühungen lohnen. Schauen wir uns einige interessante Punkte aus dem statischen Analysebericht an, der mir per Post zugegangen ist (nicht umsonst habe ich darauf hingewiesen).
Gefährliche Optimierung
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
memset( &ctx, 0, sizeof( sha1_context ) );
}
PVS-Studio-Warnung:
Dieser Code befindet sich im sicheren Hashing-Modul, weist jedoch eine schwerwiegende Sicherheitslücke auf (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Alles ist in Ordnung und die Funktion Memset ausgeführt wird und dabei wichtige Daten im RAM überschreibt, freuen Sie sich aber noch nicht. Schauen wir uns die Assemblyliste der Release-Version mit Optimierung an:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Wie aus der Auflistung hervorgeht, hat der Compiler den Aufruf ignoriert Memset. Dies liegt daran, dass in der Funktion sha1 nach dem Anruf Memset kein Bezug mehr zur Struktur ctx. Daher sieht der Compiler keinen Sinn darin, Prozessorzeit damit zu verschwenden, Speicher zu überschreiben, der in Zukunft nicht mehr verwendet wird. Sie können dies beheben, indem Sie die Funktion verwenden RtlSecureZeroMemory oder
Richtig:
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) );
}
Unnötiger Vergleich
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
if (leftvol >= 0) {
chans[chan].leftVolume = leftvol;
}
if (rightvol >= 0) {
chans[chan].rightVolume = rightvol;
}
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
PVS-Studio-Warnung:
Achten Sie zunächst auf den else-Zweig if. Der Code wird nur ausgeführt, wenn alle Bedingungen erfüllt sind leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0 wird sich als falsch herausstellen. Daher erhalten wir die folgenden Aussagen, die für den else-Zweig gelten: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol >= 0. Beachten Sie die letzten beiden Aussagen. Ist es sinnvoll zu prüfen, was eine notwendige Bedingung für die Ausführung dieses Codeabschnitts ist?
Daher können wir diese bedingten Anweisungen sicher entfernen:
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
chans[chan].leftVolume = leftvol;
chans[chan].rightVolume = rightvol;
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
Ein anderes Szenario. Hinter diesen redundanten Bedingungen verbirgt sich ein Fehler. Vielleicht haben sie nicht geprüft, was erforderlich war.
Strg+C Strg+V schlägt zurück
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Achten Sie auf den Scheck im Inneren if. Finden Sie es nicht seltsam, dass wir prüfen, ob die Adresse gültig ist? psmfData, doppelt so viel? Das kommt mir also seltsam vor ... Tatsächlich handelt es sich hierbei natürlich um einen Tippfehler, und die Idee bestand darin, beide Eingabeparameter zu überprüfen.
Korrekte Möglichkeit:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Variable vergessen
extern void ud_translate_att(
int size = 0;
....
if (size == 8) {
ud_asmprintf(u, "b");
} else if (size == 16) {
ud_asmprintf(u, "w");
} else if (size == 64) {
ud_asmprintf(u, "q");
}
....
}
PVS-Studio-Warnung:
Dieser Fehler befindet sich im Ordner ext, also nicht wirklich relevant für das Projekt, aber der Fehler wurde gefunden, bevor ich ihn bemerkte, also beschloss ich, ihn zu belassen. Schließlich geht es in diesem Artikel nicht um die Überprüfung von Fehlern, sondern um die Integration mit Travis CI, und es wurde keine Konfiguration des Analysators vorgenommen.
Variable Größe wird durch eine Konstante initialisiert, wird jedoch im Code überhaupt nicht verwendet, bis hin zum Operator if, was natürlich gibt falsch bei der Überprüfung der Bedingungen, denn wie wir uns erinnern, Größe gleich Null. Auch nachträgliche Kontrollen machen keinen Sinn.
Offenbar hat der Autor des Codefragments vergessen, die Variable zu überschreiben Größe davor.
Stoppen
Hier werden wir wahrscheinlich mit den Fehlern aufhören. Der Zweck dieses Artikels besteht darin, die Arbeit von PVS-Studio zusammen mit Travis CI zu demonstrieren und nicht das Projekt so gründlich wie möglich zu analysieren. Wenn Sie größere und schönere Fehler wollen, können Sie diese immer bewundern
Abschluss
Durch die Verwendung von Webdiensten zum Erstellen von Projekten zusammen mit der Praxis der inkrementellen Analyse können Sie viele Probleme sofort nach dem Zusammenführen von Code finden. Allerdings reicht ein Build möglicherweise nicht aus, sodass die Einrichtung von Tests zusammen mit statischer Analyse die Qualität des Codes erheblich verbessern wird.
Nützliche Links
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Maxim Zvyagintsev.
Source: habr.com