Travis CI je distribuovaná webová služba pro vytváření a testování softwaru, která využívá GitHub jako hosting zdrojového kódu. Kromě výše uvedených provozních scénářů můžete díky rozsáhlým konfiguračním možnostem přidat vlastní. V tomto článku nakonfigurujeme Travis CI pro práci s PVS-Studio pomocí příkladu kódu PPSSPP.
úvod
Nastavení Travis CI
Budeme potřebovat úložiště na GitHubu, kde se nachází projekt, který potřebujeme, a také klíč pro PVS-Studio (můžete získat
Pojďme na web
Pro test jsem forknul PPSSPP.
Aktivujeme úložiště, které chceme shromáždit:
V tuto chvíli Travis CI nemůže postavit náš projekt, protože neexistují žádné pokyny pro stavbu. Je tedy čas na konfiguraci.
Při analýze se nám budou hodit některé proměnné, například klíč pro PVS-Studio, který by bylo nežádoucí uvádět v konfiguračním souboru. Pojďme tedy přidat proměnné prostředí pomocí nastavení sestavení v Travis CI:
Potřebujeme:
- PVS_USERNAME – uživatelské jméno
- PVS_KEY - klíč
- MAIL_USER - e-mail, který bude použit k odeslání zprávy
- MAIL_PASSWORD - heslo k e-mailu
Poslední dva jsou volitelné. Ty budou použity k odeslání výsledků poštou. Pokud chcete zprávu distribuovat jiným způsobem, nemusíte je uvádět.
Takže jsme přidali proměnné prostředí, které potřebujeme:
Nyní vytvoříme soubor .travis.yml a umístěte jej do kořenového adresáře projektu. PPSSPP již měl konfigurační soubor pro Travis CI, nicméně byl příliš velký a pro příklad zcela nevhodný, takže jsme jej museli značně zjednodušit a ponechat pouze základní prvky.
Nejprve označme jazyk, verzi Ubuntu Linux, kterou chceme ve virtuálním počítači používat, a potřebné balíčky pro sestavení:
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'
Všechny balíčky, které jsou uvedeny, jsou potřeba výhradně pro PPSSPP.
Nyní naznačíme montážní matici:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Trochu více o sekci matice. V Travis CI existují dva způsoby, jak vytvořit možnosti sestavení: první je specifikovat seznam kompilátorů, typů operačního systému, proměnných prostředí atd., načež se vygeneruje matice všech možných kombinací; druhá je explicitní označení matice. Tyto dva přístupy můžete samozřejmě kombinovat a přidat jedinečný případ, nebo jej naopak pomocí sekce vyloučit vyloučit. Více si o tom můžete přečíst v
Zbývá pouze poskytnout návod k montáži pro konkrétní projekt:
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
Travis CI vám umožňuje přidávat vlastní příkazy pro různé fáze života virtuálního stroje. Sekce před_instalací provedeny před instalací balíčků. Pak instalovat, který následuje po instalaci balíčků ze seznamu addons.aptkteré jsme naznačili výše. Samotná montáž probíhá v skript. Pokud vše půjde dobře, ocitneme se uvnitř po_úspěchu (v této části spustíme statickou analýzu). Toto nejsou všechny kroky, které lze upravit, pokud potřebujete více, měli byste se podívat
Pro usnadnění čtení byly příkazy umístěny v samostatném skriptu .travis.sh, který je umístěn v kořenovém adresáři projektu.
Máme tedy následující soubor .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
Před instalací balíčků provedeme aktualizaci submodulů. To je potřeba k vybudování PPSSPP. Přidejme první funkci do .travis.sh (všimněte si rozšíření):
travis_before_install() {
git submodule update --init --recursive
}
Nyní přejdeme přímo k nastavení automatického spuštění PVS-Studio v Travis CI. Nejprve musíme do systému nainstalovat balíček PVS-Studio:
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
}
Na začátku funkce travis_install nainstalujeme kompilátory, které potřebujeme, pomocí proměnných prostředí. Pak pokud proměnná $PVS_ANALYZE ukládá hodnotu Ano (uvedli jsme to v sekci env během konfigurace matice sestavení), nainstalujeme balíček pvs-studio. Kromě toho jsou uvedeny také balíčky libio-socket-ssl-perl и libnet-ssleay-perljsou však vyžadovány pro zasílání výsledků poštou, takže nejsou nutné, pokud jste zvolili jiný způsob doručení zprávy.
Funkce download_extract stáhne a rozbalí zadaný archiv:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Je čas dát projekt dohromady. To se děje v sekci 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
}
Ve skutečnosti se jedná o zjednodušenou původní konfiguraci, s výjimkou těchto řádků:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
V této části kódu jsme stanovili pro cmake příznak pro export příkazů kompilace. To je nezbytné pro analyzátor statického kódu. Více si o tom můžete přečíst v článku „
Pokud byla montáž úspěšná, tak se dostáváme k po_úspěchu, kde provádíme statickou analýzu:
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
}
Pojďme se blíže podívat na následující řádky:
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
První řádek vygeneruje licenční soubor z uživatelského jména a klíče, které jsme zadali úplně na začátku při nastavování proměnných prostředí Travis CI.
Druhý řádek přímo spustí analýzu. Vlajka -j nastavuje počet vláken pro analýzu, příznak -l označuje licenci, příznak -Ó definuje soubor pro výstup protokolů a příznak -disableLicenseExpirationCheck vyžadováno pro zkušební verze, protože ve výchozím nastavení pvs-studio-analyzer upozorní uživatele, že licence brzy vyprší. Chcete-li tomu zabránit, můžete zadat tento příznak.
Soubor protokolu obsahuje nezpracovaný výstup, který nelze číst bez konverze, takže nejprve musíte soubor nastavit jako čitelný. Projdeme protokoly plog-konvertora výstupem je soubor html.
V tomto příkladu jsem se rozhodl odesílat zprávy poštou pomocí příkazu poslat e-mailem.
V důsledku toho jsme dostali následující soubor .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;
Nyní je čas přesunout změny do úložiště git, poté Travis CI automaticky spustí sestavení. Kliknutím na „ppsspp“ přejdete na sestavy sestav:
Uvidíme přehled aktuálního sestavení:
Pokud je sestavení úspěšně dokončeno, obdržíme e-mail s výsledky statické analýzy. Mailing samozřejmě není jediný způsob, jak zprávu obdržet. Můžete si vybrat jakýkoli způsob implementace. Je však důležité si uvědomit, že po dokončení sestavení nebude možné přistupovat k souborům virtuálního počítače.
Shrnutí chyb
Nejtěžší část máme úspěšně za sebou. Nyní se přesvědčme, že veškeré naše úsilí stojí za to. Podívejme se na některé zajímavé body ze zprávy statické analýzy, která mi přišla poštou (ne nadarmo jsem to naznačil).
Nebezpečná optimalizace
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 ) );
}
Upozornění PVS-Studio:
Tento kus kódu se nachází v zabezpečeném hashovacím modulu, obsahuje však závažnou bezpečnostní chybu (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Vše v pořádku a funkce pamětová sada se provede, čímž dojde k přepsání důležitých dat v RAM, ale zatím se neradujte. Podívejme se na výpis sestavy verze Release s optimalizací:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Jak je vidět z výpisu, kompilátor volání ignoroval pamětová sada. To je způsobeno tím, že ve funkci sha1 po hovoru pamětová sada žádné další odkazy na strukturu CTX. Kompilátor proto nevidí žádný smysl v plýtvání časem procesoru přepisováním paměti, která se v budoucnu nevyužije. Můžete to opravit pomocí funkce RtlSecureZeroMemory nebo
Správně:
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 ) );
}
Zbytečné srovnání
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);
}
}
Upozornění PVS-Studio:
Nejprve věnujte pozornost větvi else if. Kód bude proveden pouze při splnění všech podmínek leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0 se ukáže jako nepravdivé. Dostaneme tedy následující tvrzení, která budou platit pro větev else: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol >= 0. Všimněte si posledních dvou tvrzení. Má smysl kontrolovat, co je nezbytnou podmínkou pro provedení tohoto kusu kódu?
Takže můžeme bezpečně odstranit tyto podmíněné příkazy:
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);
}
}
Jiný scénář. Za těmito nadbytečnými podmínkami se skrývá nějaký druh chyby. Možná nezkontrolovali, co bylo požadováno.
Ctrl+C Ctrl+V vrací úder
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Věnujte pozornost kontrole uvnitř if. Nepřipadá vám divné, že kontrolujeme, zda je adresa platná? psmfData, dvakrát tolik? Tak tohle mi připadá divné... Ve skutečnosti jde samozřejmě o překlep a záměrem bylo zkontrolovat oba vstupní parametry.
Správná možnost:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Zapomenutá proměnná
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");
}
....
}
Upozornění PVS-Studio:
Tato chyba se nachází ve složce ext, takže to není pro projekt opravdu relevantní, ale chyba byla nalezena dříve, než jsem si jí všiml, takže jsem se rozhodl ji opustit. Koneckonců, tento článek není o kontrole chyb, ale o integraci s Travis CI a nebyla provedena žádná konfigurace analyzátoru.
Proměnná velikost je inicializováno konstantou, ta se však v kódu vůbec nepoužívá, až na operátora if, což samozřejmě dává nepravdivý při kontrole podmínek, protože, jak si pamatujeme, velikost rovna nule. Následné kontroly také nemají smysl.
Autor fragmentu kódu zřejmě zapomněl přepsat proměnnou velikost před tím.
Stop
Tady asi s chybami skončíme. Účelem tohoto článku je demonstrovat práci PVS-Studio společně s Travisem CI, nikoli co nejdůkladněji analyzovat projekt. Pokud chcete větší a krásnější chyby, můžete je vždy obdivovat
Závěr
Používání webových služeb k vytváření projektů spolu s praxí inkrementální analýzy vám umožňuje najít mnoho problémů ihned po sloučení kódu. Jedno sestavení však nemusí stačit, takže nastavení testování spolu se statickou analýzou výrazně zlepší kvalitu kódu.
Užitečné odkazy
Pokud chcete tento článek sdílet s anglicky mluvícím publikem, použijte prosím odkaz na překlad: Maxim Zvjagincev.
Zdroj: www.habr.com