Travis CI er en distribuert webtjeneste for å bygge og teste programvare som bruker GitHub som hosting for kildekode. I tillegg til de ovennevnte driftsscenariene, kan du legge til dine egne takket være de omfattende konfigurasjonsalternativene. I denne artikkelen vil vi konfigurere Travis CI til å fungere med PVS-Studio ved å bruke PPSSPP-kodeeksemplet.
Innledning
Sette opp Travis CI
Vi trenger et depot på GitHub, hvor prosjektet vi trenger er lokalisert, samt en nøkkel for PVS-Studio (du kan få
La oss gå til siden
For testen gaffel jeg PPSSPP.
Vi aktiverer depotet som vi ønsker å samle inn:
For øyeblikket kan ikke Travis CI bygge prosjektet vårt fordi det ikke er instruksjoner for bygging. Så det er tid for konfigurasjon.
Under analysen vil noen variabler være nyttige for oss, for eksempel nøkkelen til PVS-Studio, som ville være uønsket å spesifisere i konfigurasjonsfilen. Så la oss legge til miljøvariabler ved å bruke byggeinnstillingene i Travis CI:
Vi trenger:
- PVS_USERNAME - brukernavn
- PVS_KEY - nøkkel
- MAIL_USER - e-post som vil bli brukt til å sende rapporten
- MAIL_PASSWORD - e-postpassord
De to siste er valgfrie. Disse vil bli brukt til å sende resultater per post. Dersom du ønsker å distribuere rapporten på annen måte, trenger du ikke angi dem.
Så vi har lagt til miljøvariablene vi trenger:
La oss nå lage en fil .travis.yml og plassere den i roten av prosjektet. PPSSPP hadde allerede en konfigurasjonsfil for Travis CI, men den var for stor og helt uegnet for eksempelet, så vi måtte forenkle den betraktelig og la bare de grunnleggende elementene være igjen.
La oss først angi språket, versjonen av Ubuntu Linux som vi ønsker å bruke i den virtuelle maskinen, og de nødvendige pakkene for byggingen:
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 pakkene som er oppført er utelukkende nødvendig for PPSSPP.
Nå indikerer vi monteringsmatrisen:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Litt mer om avsnittet matrise. I Travis CI er det to måter å lage byggealternativer på: den første er å spesifisere en liste over kompilatorer, operativsystemtyper, miljøvariabler osv., hvoretter en matrise med alle mulige kombinasjoner genereres; den andre er en eksplisitt indikasjon på matrisen. Selvfølgelig kan du kombinere disse to tilnærmingene og legge til en unik sak, eller tvert imot, ekskludere den ved å bruke delen ekskludere. Du kan lese mer om dette i
Alt som gjenstår er å gi prosjektspesifikke monteringsinstruksjoner:
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 lar deg legge til dine egne kommandoer for ulike stadier av livet til en virtuell maskin. Seksjon før_installer utført før du installerer pakker. Deretter installere, som følger installasjonen av pakker fra listen addons.aptsom vi indikerte ovenfor. Selve monteringen foregår i script. Hvis alt gikk bra, så befinner vi oss i etter_suksess (det er i denne delen vi skal kjøre statisk analyse). Dette er ikke alle trinnene som kan endres, hvis du trenger flere, bør du se inn
For å lette lesingen ble kommandoene plassert i et eget skript .travis.sh, som er plassert ved prosjektroten.
Så vi har følgende fil .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
Før vi installerer pakkene, vil vi oppdatere undermodulene. Dette er nødvendig for å bygge PPSSPP. La oss legge til den første funksjonen til .travis.sh (merk utvidelsen):
travis_before_install() {
git submodule update --init --recursive
}
Nå kommer vi direkte til å sette opp den automatiske lanseringen av PVS-Studio i Travis CI. Først må vi installere PVS-Studio-pakken på systemet:
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
}
I begynnelsen av funksjonen travis_install vi installerer kompilatorene vi trenger ved å bruke miljøvariabler. Så hvis variabelen $PVS_ANALYZE lagrer verdi Ja (vi indikerte det i delen env under byggematrisekonfigurasjon), installerer vi pakken pvs-studio. I tillegg til dette er det også angitt pakker libio-socket-ssl-perl и libnet-ssleay-perl, men de kreves for å sende resultater, så de er ikke nødvendige hvis du har valgt en annen metode for å levere rapporten.
Funksjon download_extract laster ned og pakker ut det angitte arkivet:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Det er på tide å sette sammen prosjektet. Dette skjer i seksjonen script:
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
}
Faktisk er dette en forenklet original konfigurasjon, bortsett fra disse linjene:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
I denne delen av koden setter vi for cmake flagg for eksport av kompileringskommandoer. Dette er nødvendig for en statisk kodeanalysator. Du kan lese mer om dette i artikkelen "
Hvis monteringen var vellykket, så kommer vi til etter_suksess, hvor vi utfører statisk analyse:
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
}
La oss se nærmere på følgende linjer:
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
Den første linjen genererer en lisensfil fra brukernavnet og nøkkelen som vi spesifiserte helt i begynnelsen da vi satte opp Travis CI-miljøvariablene.
Den andre linjen starter analysen direkte. Flagg -j setter antall tråder for analyse, flagg -l indikerer lisens, flagg -o definerer filen for å skrive ut logger, og flagget -disableLicenseExpirationCheck kreves for prøveversjoner, siden som standard pvs-studio-analyzer vil advare brukeren om at lisensen er i ferd med å utløpe. For å forhindre at dette skjer, kan du spesifisere dette flagget.
Loggfilen inneholder råutdata som ikke kan leses uten konvertering, så du må først gjøre filen lesbar. La oss føre tømmerstokkene plog-omformer, og utdataene er en html-fil.
I dette eksemplet bestemte jeg meg for å sende rapporter per post ved å bruke kommandoen Send e-post.
Som et resultat fikk vi følgende fil .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;
Nå er det på tide å presse endringene til git-depotet, hvoretter Travis CI automatisk kjører byggingen. Klikk på "ppsspp" for å gå til byggerapportene:
Vi vil se en oversikt over den nåværende konstruksjonen:
Hvis byggingen er fullført, vil vi motta en e-post med resultatene av den statiske analysen. Sending er selvfølgelig ikke den eneste måten å motta en rapport på. Du kan velge hvilken som helst implementeringsmetode. Men det er viktig å huske at etter at byggingen er fullført, vil det ikke være mulig å få tilgang til de virtuelle maskinfilene.
Feilsammendrag
Vi har fullført den vanskeligste delen. La oss nå sørge for at all vår innsats er verdt det. La oss se på noen interessante punkter fra den statiske analyserapporten som kom til meg per post (det var ikke for ingenting jeg indikerte det).
Farlig optimalisering
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 advarsel:
Denne kodebiten er plassert i den sikre hashing-modulen, men den inneholder en alvorlig sikkerhetsfeil (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Alt er i orden og funksjonen memeset blir utført, og overskriver dermed viktige data i RAM, men gled deg ikke ennå. La oss se på monteringslisten til utgivelsesversjonen med optimalisering:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Som det fremgår av oppføringen, ignorerte kompilatoren samtalen memeset. Dette skyldes det faktum at i funksjonen sha1 etter samtalen memeset ingen mer henvisning til struktur ctx. Derfor ser kompilatoren ikke noe poeng i å kaste bort prosessortid på å overskrive minne som ikke brukes i fremtiden. Du kan fikse dette ved å bruke funksjonen RtlSecureZeroMemory eller
korrigere:
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 ) );
}
Unødvendig sammenligning
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 advarsel:
Vær oppmerksom på den andre grenen for den første if. Koden vil kun utføres under alle forhold leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || høyrevolum < 0 vil vise seg å være falsk. Derfor får vi følgende utsagn, som vil være sant for den andre grenen: leftvol <= 0xFFFF, rightvol <= 0xFFFF, venstrevolum >= 0 и høyrevolum >= 0. Legg merke til de to siste utsagnene. Er det fornuftig å sjekke hva som er en nødvendig betingelse for utførelse av denne kodebiten?
Så vi kan trygt fjerne disse betingede uttalelsene:
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);
}
}
Et annet scenario. Det er en slags feil skjult bak disse redundante forholdene. Kanskje de ikke sjekket hva som kreves.
Ctrl+C Ctrl+V slår tilbake
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ær oppmerksom på sjekken inni if. Synes du ikke det er rart at vi sjekker om adressen er gyldig? psmfData, Dobbelt så mye? Så dette virker rart for meg... Faktisk er dette selvfølgelig en skrivefeil, og tanken var å sjekke begge inngangsparametrene.
Riktig alternativ:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Glemt variabel
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 advarsel:
Denne feilen ligger i mappen ext, så egentlig ikke relevant for prosjektet, men feilen ble funnet før jeg la merke til den, så jeg bestemte meg for å forlate den. Denne artikkelen handler tross alt ikke om gjennomgang av feil, men om integrasjon med Travis CI, og det ble ikke utført noen konfigurasjon av analysatoren.
variabel størrelse initialiseres av en konstant, men den brukes ikke i det hele tatt i koden, helt ned til operatøren if, som selvfølgelig gir falsk mens vi sjekker forholdene, fordi, som vi husker, størrelse lik null. Etterfølgende kontroller gir heller ingen mening.
Tilsynelatende glemte forfatteren av kodefragmentet å overskrive variabelen størrelse før det.
Stopp
Det er her vi sannsynligvis ender med feilene. Hensikten med denne artikkelen er å demonstrere arbeidet til PVS-Studio sammen med Travis CI, og ikke å analysere prosjektet så grundig som mulig. Hvis du vil ha større og vakrere feil, kan du alltid beundre dem
Konklusjon
Ved å bruke webtjenester til å bygge prosjekter sammen med praksisen med inkrementell analyse kan du finne mange problemer umiddelbart etter sammenslåing av kode. Det er imidlertid ikke sikkert at ett bygg er nok, så å sette opp testing sammen med statisk analyse vil forbedre kvaliteten på koden betydelig.
Nyttige lenker
Hvis du vil dele denne artikkelen med et engelsktalende publikum, vennligst bruk oversettelseslenken: Maxim Zvyagintsev.
Kilde: www.habr.com