Travis CI je distribuirani web servis za izradu i testiranje softvera koji koristi GitHub kao hosting izvornog koda. Pored gore navedenih operativnih scenarija, možete dodati svoje vlastite zahvaljujući opsežnim opcijama konfiguracije. U ovom članku ćemo konfigurirati Travis CI za rad sa PVS-Studio koristeći primjer PPSSPP koda.
Uvod
Postavljanje Travis CI
Trebat će nam spremište na GitHubu, gdje se nalazi projekt koji nam je potreban, kao i ključ za PVS-Studio (možete nabaviti
Idemo na stranicu
Za test sam račvao PPSSPP.
Aktiviramo spremište koje želimo prikupiti:
Trenutačno, Travis CI ne može izgraditi naš projekt jer ne postoje upute za izgradnju. Dakle, vrijeme je za konfiguraciju.
Tokom analize, neke varijable će nam biti korisne, na primjer, ključ za PVS-Studio, koji bi bilo nepoželjno navesti u konfiguracijskoj datoteci. Dakle, dodajmo varijable okruženja koristeći postavke izgradnje u Travis CI:
Trebaće nam:
- PVS_USERNAME - korisničko ime
- PVS_KEY - ključ
- MAIL_USER - email koji će se koristiti za slanje izvještaja
- MAIL_PASSWORD - lozinka e-pošte
Posljednja dva su opciona. Oni će se koristiti za slanje rezultata poštom. Ako želite da distribuirate izvještaj na drugi način, ne morate ih naznačiti.
Dakle, dodali smo varijable okruženja koje su nam potrebne:
Sada kreirajmo fajl .travis.yml i stavite ga u korijen projekta. PPSSPP je već imao konfiguracionu datoteku za Travis CI, međutim, ona je bila prevelika i potpuno neprikladna za primjer, pa smo je morali uvelike pojednostaviti i ostaviti samo osnovne elemente.
Prvo, naznačimo jezik, verziju Ubuntu Linuxa koju želimo da koristimo u virtuelnoj mašini i potrebne pakete za izgradnju:
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'
Svi navedeni paketi su potrebni isključivo za PPSSPP.
Sada označavamo matricu sklopa:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Malo više o sekciji matrica. U Travis CI-ju postoje dva načina za kreiranje opcija izgradnje: prvi je specificiranje liste kompajlera, tipova operativnog sistema, varijabli okruženja, itd., nakon čega se generiše matrica svih mogućih kombinacija; drugi je eksplicitna indikacija matrice. Naravno, možete kombinirati ova dva pristupa i dodati jedinstveni slučaj, ili, naprotiv, isključiti ga pomoću odjeljka isključi. Više o ovome možete pročitati u
Sve što preostaje je pružiti upute za montažu specifične za projekat:
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 vam omogućava da dodate sopstvene komande za različite faze života virtuelne mašine. Odjeljak before_install izvršava se prije instaliranja paketa. Onda instalirajte, koji prati instalaciju paketa sa liste addons.aptkoje smo gore naznačili. Sama montaža se odvija u skripta. Ako je sve prošlo kako treba, onda se nalazimo u njemu after_success (u ovom odeljku ćemo pokrenuti statičku analizu). Ovo nisu svi koraci koji se mogu mijenjati, ako vam je potrebno više, onda biste trebali pogledati
Radi lakšeg čitanja, komande su stavljene u posebnu skriptu .travis.sh, koji se nalazi u korijenu projekta.
Dakle, imamo sljedeći fajl .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
Prije instaliranja paketa, ažurirat ćemo podmodule. Ovo je potrebno za izgradnju PPSSPP. Dodajmo prvu funkciju .travis.sh (obratite pažnju na ekstenziju):
travis_before_install() {
git submodule update --init --recursive
}
Sada dolazimo direktno do podešavanja automatskog pokretanja PVS-Studio u Travis CI. Prvo moramo da instaliramo PVS-Studio paket na sistem:
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 početku funkcije travis_install mi instaliramo kompajlere koji su nam potrebni koristeći varijable okruženja. Zatim ako je varijabla $PVS_ANALYZE skladišti vrijednost Da (naznačili smo to u odjeljku env tokom konfiguracije matrice izgradnje), instaliramo paket pvs-studio. Pored toga, naznačeni su i paketi libio-socket-ssl-perl и libnet-ssleay-perl, međutim, oni su potrebni za slanje rezultata poštom, tako da nisu potrebni ako ste odabrali drugi način za dostavu izvještaja.
funkcija download_extract preuzima i raspakuje navedenu arhivu:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Vrijeme je da sastavite projekat. Ovo se dešava u sekciji skripta:
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
}
Zapravo, ovo je pojednostavljena originalna konfiguracija, osim ovih linija:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
U ovom dijelu koda smo postavili za cmake zastavicu za izvoz naredbi kompilacije. Ovo je neophodno za statički analizator koda. Više o tome možete pročitati u članku “
Ako je skupština bila uspješna, dolazimo do toga after_success, gdje vršimo statičku analizu:
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
}
Pogledajmo bliže sljedeće redove:
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
Prvi red generiše licencni fajl od korisničkog imena i ključa koje smo naveli na samom početku prilikom podešavanja varijabli okruženja Travis CI.
Drugi red direktno započinje analizu. Zastava -j postavlja broj niti za analizu, zastavicu -l označava dozvolu, zastavu -o definira datoteku za izlaz dnevnika i zastavicu -disableLicenseExpirationCheck potrebno za probne verzije, jer je po defaultu pvs-studio-analyzer će upozoriti korisnika da će licenca uskoro isteći. Da biste spriječili da se to dogodi, možete odrediti ovu zastavicu.
Datoteka evidencije sadrži sirovi izlaz koji se ne može pročitati bez konverzije, tako da prvo morate učiniti datoteku čitljivom. Hajde da prođemo kroz trupce plog-konverter, a izlaz je html datoteka.
U ovom primjeru, odlučio sam da šaljem izvještaje poštom koristeći naredbu pošaljite e-mail.
Kao rezultat, dobili smo sljedeći fajl .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;
Sada je vrijeme da se promjene uguraju u git spremište, nakon čega će Travis CI automatski pokrenuti build. Kliknite na “ppsspp” da biste otišli na izvještaje o izgradnji:
Vidjet ćemo pregled trenutne gradnje:
Ako je gradnja uspješno završena, primit ćemo e-poštu s rezultatima statičke analize. Naravno, slanje putem pošte nije jedini način za primanje izvještaja. Možete odabrati bilo koju metodu implementacije. Ali važno je zapamtiti da nakon završetka izgradnje neće biti moguće pristupiti datotekama virtuelne mašine.
Sažetak greške
Uspješno smo završili najteži dio. Sada se uvjerimo da je sav naš trud vrijedan toga. Pogledajmo neke zanimljive tačke iz izvještaja o statičkoj analizi koji mi je stigao poštom (nisam uzalud to naznačio).
Opasna optimizacija
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 upozorenje:
Ovaj dio koda nalazi se u sigurnom heš modulu, međutim, sadrži ozbiljnu sigurnosnu grešku (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Sve je u redu i funkcija memeset se izvršava, čime se prepisuju važni podaci u RAM, međutim, nemojte se još radovati. Pogledajmo popis sklopa verzije Release s optimizacijom:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Kao što se vidi iz listinga, kompajler je ignorisao poziv memeset. To je zbog činjenice da je u funkciji sha1 nakon poziva memeset nema više pozivanja na strukturu ctx. Stoga, kompajler ne vidi smisao u trošenju procesorskog vremena na prepisivanje memorije koja se neće koristiti u budućnosti. Ovo možete popraviti korištenjem funkcije RtlSecureZeroMemory ili
Tačno:
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 ) );
}
Nepotrebno poređenje
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 upozorenje:
Prvo obratite pažnju na granu else if. Kod će biti izvršen samo ako su ispunjeni svi uslovi leftvol > 0xFFFF || desni vol > 0xFFFF || leftvol < 0 || desni vol < 0 ispostaviće se da je lažno. Dakle, dobijamo sledeće izjave, koje će važiti za granu else: leftvol <= 0xFFFF, desni vol <= 0xFFFF, levi vol >= 0 и desni vol >= 0. Obratite pažnju na poslednje dve izjave. Ima li smisla provjeriti koji je neophodan uslov za izvršenje ovog dijela koda?
Dakle, možemo bezbedno ukloniti ove uslovne izjave:
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);
}
}
Drugi scenario. Iza ovih suvišnih uslova krije se neka vrsta greške. Možda nisu provjerili šta je potrebno.
Ctrl+C Ctrl+V uzvraća udarac
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Obratite pažnju na ček iznutra if. Ne mislite li da je čudno što provjeravamo da li je adresa važeća? psmfData, duplo više? Tako da mi se ovo čini čudnim... U stvari, ovo je, naravno, greška u kucanju, a ideja je bila da se provere oba ulazna parametra.
Ispravna opcija:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Zaboravljena varijabla
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 upozorenje:
Ova greška se nalazi u folderu ext, tako da nije baš relevantno za projekat, ali je greška pronađena prije nego što sam je primijetio, pa sam odlučio da je ostavim. Uostalom, ovaj članak nije o pregledu grešaka, već o integraciji sa Travis CI, a konfiguracija analizatora nije izvršena.
Promjenjivo veličina je inicijaliziran konstantom, međutim, uopće se ne koristi u kodu, sve do operatora if, što, naravno, daje lažan pri provjeravanju uslova, jer, kako se sjećamo, veličina jednaka nuli. Naknadne provjere također nemaju smisla.
Očigledno je autor fragmenta koda zaboravio da prepiše promenljivu veličina pre toga.
Stop
Tu ćemo vjerovatno završiti sa greškama. Svrha ovog članka je da demonstrira rad PVS-Studio zajedno sa Travis CI, a ne da što detaljnije analizira projekat. Ako želite veće i ljepše greške, uvijek im se možete diviti
zaključak
Korištenje web servisa za izgradnju projekata zajedno s praksom inkrementalne analize omogućava vam da pronađete mnoge probleme odmah nakon spajanja koda. Međutim, jedna gradnja možda neće biti dovoljna, pa će postavljanje testiranja zajedno sa statičkom analizom značajno poboljšati kvalitet koda.
korisni linkovi
Ako želite da podijelite ovaj članak sa publikom koja govori engleski, koristite link za prijevod: Maxim Zvyagintsev.
izvor: www.habr.com