Travis CI je distribuirana web usluga za izradu i testiranje softvera koja koristi GitHub kao hosting izvornog koda. Uz gore navedene scenarije rada, možete dodati svoje vlastite zahvaljujući opsežnim mogućnostima konfiguracije. U ovom ćemo članku konfigurirati Travis CI za rad s PVS-Studiom koristeći primjer koda PPSSPP.
Uvod
Postavljanje Travis CI
Trebat će nam repozitorij na GitHubu, gdje se nalazi projekt koji nam je potreban, kao i ključ za PVS-Studio (možete dobiti
Idemo na mjesto
Za test sam forkovao PPSSPP.
Aktiviramo spremište koje želimo prikupiti:
Trenutačno Travis CI ne može izgraditi naš projekt jer nema uputa za izgradnju. Dakle, vrijeme je za konfiguraciju.
Tijekom 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:
Trebat ćemo:
- PVS_USERNAME - korisničko ime
- PVS_KEY - ključ
- MAIL_USER - email koji će se koristiti za slanje izvješća
- MAIL_PASSWORD - lozinka e-pošte
Posljednje dvije su izborne. Oni će se koristiti za slanje rezultata poštom. Ako izvješće želite distribuirati na drugi način, ne morate ih označavati.
Dakle, dodali smo varijable okoline koje su nam potrebne:
Kreirajmo sada datoteku .travis.yml i smjestite ga u korijen projekta. PPSSPP je već imao konfiguracijsku datoteku za Travis CI, međutim, bila je prevelika i potpuno neprikladna za primjer, pa smo je morali jako pojednostaviti i ostaviti samo osnovne elemente.
Prvo, označimo jezik, verziju Ubuntu Linuxa koju želimo koristiti u virtualnom stroju 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
Još malo o rubrici matrica. U Travis CI-u postoje dva načina za kreiranje opcija izgradnje: prvi je određivanje popisa prevoditelja, tipova operativnih sustava, varijabli okruženja itd., nakon čega se generira matrica svih mogućih kombinacija; drugi je eksplicitna indikacija matrice. Naravno, možete kombinirati ova dva pristupa i dodati jedinstveni slučaj ili ga, naprotiv, isključiti pomoću odjeljka isključiti. Više o ovome možete pročitati u
Sve što preostaje je pružiti upute za montažu specifične za 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 vam omogućuje dodavanje vlastitih naredbi za različite faze života virtualnog stroja. Odjeljak prije_instalacije izvršiti prije instaliranja paketa. Zatim instalirati, koji prati instalaciju paketa s liste dodaci.aptkoje smo gore naznačili. Sama skupština odvija se u rukopis. Ako je sve prošlo dobro, onda se nalazimo unutra nakon_uspjeha (u ovom odjeljku ćemo provesti statičku analizu). Ovo nisu svi koraci koji se mogu mijenjati, ako trebate više, trebali biste pogledati
Radi lakšeg čitanja, naredbe su smještene u zasebnu skriptu .travis.sh, koji se nalazi u korijenu projekta.
Dakle, imamo sljedeću datoteku .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-a. Dodajmo prvu funkciju u .travis.sh (obratite pažnju na ekstenziju):
travis_before_install() {
git submodule update --init --recursive
}
Sada dolazimo izravno do postavljanja automatskog pokretanja PVS-Studio u Travis CI. Prvo moramo instalirati PVS-Studio paket na sustav:
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 instaliramo prevoditelje koji su nam potrebni pomoću varijabli okruženja. Tada ako varijabla $PVS_ANALYZE pohranjuje vrijednost Da (naveli smo to u odjeljku eNV tijekom konfiguracije matrice izgradnje), instaliramo paket pvs-studio. Osim ovoga, naznačeni su i paketi libio-utičnica-ssl-perl и libnet-ssleay-perl, međutim, potrebni su za slanje rezultata poštom, tako da nisu potrebni ako ste odabrali drugu metodu za dostavu izvješća.
Funkcija preuzimanje_ekstrakt preuzima i raspakira navedenu arhivu:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Vrijeme je da sastavite projekt. To se događa u odjeljku rukopis:
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 izvorna konfiguracija, osim ovih redaka:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
U ovom dijelu koda za koji smo postavili cmake zastavica za izvoz naredbi kompilacije. Ovo je neophodno za statički analizator koda. Više o tome možete pročitati u članku “
Ako je montaža bila uspješna, onda dolazimo nakon_uspjeha, 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 pobliže sljedeće retke:
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 redak generira datoteku licence iz korisničkog imena i ključa koje smo naveli na samom početku prilikom postavljanja varijabli okruženja Travis CI.
Drugi redak izravno započinje analizu. Zastava -j postavlja broj niti za analizu, zastavica -l označava dozvolu, zastavu -o definira datoteku za izlaz dnevnika i zastavu -disableLicenseExpirationCheck potrebno za probne verzije, jer prema zadanim postavkama pvs-studio-analizator upozorit će korisnika da licenca uskoro ističe. Kako biste spriječili da se to dogodi, možete odrediti ovu zastavicu.
Datoteka dnevnika sadrži neobrađeni izlaz koji se ne može čitati bez konverzije, tako da datoteku prvo morate učiniti čitljivom. Provucimo cjepanice plog-pretvarač, a izlaz je html datoteka.
U ovom sam primjeru odlučio poslati izvješća poštom pomoću naredbe Pošalji e-mail.
Kao rezultat, dobili smo sljedeću datoteku .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 gurnete promjene u git repozitorij, nakon čega će Travis CI automatski pokrenuti izgradnju. Kliknite na "ppsspp" za odlazak na izvješća o izradi:
Vidjet ćemo pregled trenutne verzije:
Ako je izgradnja uspješno dovršena, primit ćemo e-poruku s rezultatima statičke analize. Naravno, slanje poštom nije jedini način da dobijete izvješće. Možete odabrati bilo koji način implementacije. Ali važno je zapamtiti da nakon završetka izgradnje neće biti moguće pristupiti datotekama virtualnog stroja.
Sažetak pogreške
Najteži dio smo uspješno završili. Sada se uvjerimo da se sav naš trud isplati. Pogledajmo neke zanimljive točke iz izvješća o statičkoj analizi koje sam dobio poštom (nisam ga uzalud naveo).
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 ) );
}
Upozorenje PVS-Studio:
Ovaj dio koda nalazi se u modulu za sigurno raspršivanje, ali sadrži ozbiljan sigurnosni propust (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Sve je uredno i u funkciji memeset se izvršava, čime se prepisuju važni podaci u RAM-u, međutim, nemojte se još radovati. Pogledajmo popis sklopova Release verzije s optimizacijom:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Kao što se može vidjeti iz popisa, kompajler je ignorirao poziv memeset. To je zbog činjenice da u funkciji sha1 nakon poziva memeset nema više pozivanja na strukturu ctx. Stoga prevoditelj ne vidi smisao u gubitku procesorskog vremena na prepisivanje memorije koja se u budućnosti ne koristi. To možete popraviti pomoću funkcije RtlSecureZeroMemory ili
ispraviti:
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 ) );
}
Nepotrebna usporedba
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);
}
}
Upozorenje PVS-Studio:
Prvo obratite pažnju na drugu granu if. Kod će se izvršiti samo ako su ispunjeni svi uvjeti levi vol > 0xFFFF || desni vol > 0xFFFF || lijevovol < 0 || desni vol < 0 će se pokazati lažnim. Stoga dobivamo sljedeće izjave, koje će biti istinite za else granu: levi vol <= 0xFFFF, desni vol <= 0xFFFF, levi vol >= 0 и desni vol >= 0. Obratite pažnju na posljednje dvije izjave. Ima li smisla provjeriti koji je nužan uvjet za izvršenje ovog dijela koda?
Stoga možemo sigurno ukloniti ove uvjetne 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 scenarij. Postoji neka vrsta greške skrivena iza ovih suvišnih uvjeta. Možda nisu provjerili što se traži.
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 pozornost na ček iznutra if. Ne mislite li da je čudno da provjeravamo je li adresa važeća? psmfData, dvostruko više? Tako da mi ovo izgleda čudno... Zapravo, ovo je, naravno, tipfeler, a ideja je bila provjeriti 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");
}
....
}
Upozorenje PVS-Studio:
Ova se pogreška nalazi u mapi ext, tako da nije baš relevantno za projekt, ali je greška pronađena prije nego sam je primijetio, pa sam je odlučio ostaviti. Uostalom, ovaj članak nije o pregledu grešaka, već o integraciji s Travis CI, a konfiguracija analizatora nije provedena.
Promjenjiva Veličina se inicijalizira konstantom, međutim, uopće se ne koristi u kodu, sve do operatora if, što, naravno, daje lažan prilikom provjere uvjeta, jer, kako se sjećamo, Veličina jednaka nuli. Naknadne provjere također nemaju smisla.
Očigledno je autor fragmenta koda zaboravio prebrisati varijablu Veličina prije toga.
zaustaviti
Ovdje ćemo vjerojatno završiti s greškama. Svrha ovog članka je pokazati rad PVS-Studia zajedno s Travis CI, a ne analizirati projekt što je moguće temeljitije. Ako želite veće i ljepše pogreške, uvijek im se možete diviti
Zaključak
Korištenje web usluga za izradu projekata zajedno s praksom inkrementalne analize omogućuje vam pronalaženje mnogih problema odmah nakon spajanja koda. Međutim, jedna verzija možda neće biti dovoljna, pa će postavljanje testiranja zajedno sa statičkom analizom značajno poboljšati kvalitetu koda.
korisni linkovi
Ako želite podijeliti ovaj članak s publikom koja govori engleski, upotrijebite vezu za prijevod: Maxim Zvyagintsev.
Izvor: www.habr.com