Travis CI er en distribueret webservice til at bygge og teste software, der bruger GitHub som kildekodehosting. Ud over ovenstående driftsscenarier kan du tilføje dine egne takket være de omfattende konfigurationsmuligheder. I denne artikel vil vi konfigurere Travis CI til at arbejde med PVS-Studio ved hjælp af PPSSPP-kodeeksemplet.
Indledning
Opsætning af Travis CI
Vi skal bruge et depot på GitHub, hvor det projekt vi skal bruge er placeret, samt en nøgle til PVS-Studio (du kan få
Lad os gå til siden
Til testen gaflede jeg PPSSPP.
Vi aktiverer det depot, som vi ønsker at indsamle:
I øjeblikket kan Travis CI ikke bygge vores projekt, fordi der ikke er nogen instruktioner til at bygge. Så det er tid til konfiguration.
Under analysen vil nogle variabler være nyttige for os, for eksempel nøglen til PVS-Studio, som ville være uønsket at specificere i konfigurationsfilen. Så lad os tilføje miljøvariabler ved hjælp af byggeindstillingerne i Travis CI:
Vi har brug for:
- PVS_USERNAME - brugernavn
- PVS_KEY - nøgle
- MAIL_USER - e-mail, der vil blive brugt til at sende rapporten
- MAIL_PASSWORD - e-mail-adgangskode
De sidste to er valgfrie. Disse vil blive brugt til at sende resultater med posten. Hvis du ønsker at distribuere rapporten på anden måde, behøver du ikke angive dem.
Så vi har tilføjet de miljøvariabler, vi har brug for:
Lad os nu oprette en fil .travis.yml og placere det i roden af projektet. PPSSPP havde allerede en konfigurationsfil til Travis CI, men den var for stor og fuldstændig uegnet til eksemplet, så vi var nødt til at forenkle den kraftigt og kun lade de grundlæggende elementer stå.
Lad os først angive sproget, versionen af Ubuntu Linux, som vi ønsker at bruge i den virtuelle maskine, og de nødvendige pakker til build:
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 pakker, der er anført, er udelukkende nødvendige for PPSSPP.
Nu angiver vi monteringsmatrixen:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Lidt mere om afsnittet matrix. I Travis CI er der to måder at oprette byggemuligheder på: Den første er at specificere en liste over compilere, operativsystemtyper, miljøvariabler osv., hvorefter en matrix med alle mulige kombinationer genereres; den anden er en eksplicit indikation af matrixen. Selvfølgelig kan du kombinere disse to tilgange og tilføje et unikt tilfælde, eller tværtimod udelukke det ved hjælp af afsnittet udelukke. Det kan du læse mere om i
Tilbage er blot at give projektspecifikke monteringsvejledninger:
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 giver dig mulighed for at tilføje dine egne kommandoer til forskellige stadier af livet på en virtuel maskine. Afsnit før_installer udført før installation af pakker. Derefter installere, som følger installationen af pakker fra listen addons.aptsom vi har nævnt ovenfor. Selve montagen foregår i script. Hvis alt gik godt, så befinder vi os i efter_succes (det er i dette afsnit, vi vil køre statisk analyse). Det er ikke alle de trin, der kan ændres, hvis du har brug for flere, så bør du kigge ind
For at lette læsningen blev kommandoerne placeret i et separat script .travis.sh, som er placeret ved projektroden.
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 pakkerne, opdaterer vi undermodulerne. Dette er nødvendigt for at bygge PPSSPP. Lad os tilføje den første funktion til .travis.sh (bemærk udvidelsen):
travis_before_install() {
git submodule update --init --recursive
}
Nu kommer vi direkte til at opsætte den automatiske lancering af PVS-Studio i Travis CI. Først skal 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 begyndelsen af funktionen travis_install vi installerer de compilere, vi har brug for, ved hjælp af miljøvariabler. Så hvis variablen $PVS_ANALYZE gemmer værdi Ja (vi angav det i afsnittet env under build-matrix-konfiguration), installerer vi pakken pvs-studie. Udover dette er pakker også angivet libio-socket-ssl-perl и libnet-ssleay-perl, de er dog nødvendige for at sende resultater, så de er ikke nødvendige, hvis du har valgt en anden metode til at levere din rapport.
Funktion download_extract downloader og pakker det angivne arkiv ud:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Det er tid til at sætte projektet sammen. Dette sker i afsnittet 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 konfiguration, bortset fra disse linjer:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
I denne sektion af kode sætter vi for cmake flag til eksport af kompileringskommandoer. Dette er nødvendigt for en statisk kodeanalysator. Det kan du læse mere om i artiklen "
Hvis samlingen lykkedes, så kommer vi til efter_succes, hvor vi udfø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
}
Lad os 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 linje genererer en licensfil fra brugernavnet og nøglen, som vi specificerede helt i begyndelsen, da vi satte Travis CI-miljøvariablerne op.
Den anden linje starter analysen direkte. Flag -j indstiller antallet af tråde til analyse, flag -l angiver licens, flag -o definerer filen til udlæsning af logfiler og flaget -disableLicenseExpirationCheck påkrævet for prøveversioner, da som standard pvs-studio-analyzer vil advare brugeren om, at licensen er ved at udløbe. For at forhindre dette i at ske, kan du angive dette flag.
Logfilen indeholder rå output, der ikke kan læses uden konvertering, så du skal først gøre filen læsbar. Lad os sende loggene igennem plog-konverter, og outputtet er en html-fil.
I dette eksempel besluttede jeg at sende rapporter med post ved hjælp af kommandoen Send e-mail.
Som et resultat fik 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;
Nu er det tid til at skubbe ændringerne til git-lageret, hvorefter Travis CI automatisk vil køre buildet. Klik på "ppsspp" for at gå til byggerapporterne:
Vi vil se en oversigt over den nuværende bygning:
Hvis opbygningen er fuldført, modtager vi en e-mail med resultaterne af den statiske analyse. Selvfølgelig er mailing ikke den eneste måde at modtage en rapport på. Du kan vælge enhver implementeringsmetode. Men det er vigtigt at huske, at efter at build er afsluttet, vil det ikke være muligt at få adgang til de virtuelle maskinfiler.
Fejloversigt
Vi har med succes afsluttet den sværeste del. Lad os nu sikre os, at alle vores anstrengelser er det værd. Lad os se på nogle interessante punkter fra den statiske analyserapport, der kom til mig med posten (det var ikke for ingenting, jeg angav det).
Farlig optimering
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:
Dette stykke kode er placeret i det sikre hashing-modul, men det indeholder en alvorlig sikkerhedsfejl (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Alt er i orden og funktion memeset udføres, og derved overskrive vigtige data i RAM, glæd dig dog ikke lige foreløbig. Lad os se på samlingslisten for udgivelsesversionen med optimering:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Som det fremgår af listen, ignorerede compileren opkaldet memeset. Dette skyldes, at der i funktionen sha1 efter opkaldet memeset ikke længere reference til struktur ctx. Derfor ser compileren ingen mening i at spilde processortid på at overskrive hukommelse, der ikke bruges i fremtiden. Du kan rette dette ved at bruge funktionen 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 opmærksom på den anden gren for den første if. Koden vil kun blive udført, hvis alle betingelser leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || højrevol < 0 vil vise sig at være falsk. Derfor får vi følgende udsagn, som vil være sande for den anden gren: leftvol <= 0xFFFF, rightvol <= 0xFFFF, venstrevol. >= 0 и højrevolum >= 0. Læg mærke til de to sidste udsagn. Giver det mening at tjekke, hvad der er en nødvendig betingelse for at udføre dette stykke kode?
Så vi kan roligt fjerne disse betingede erklæringer:
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 andet scenarie. Der er en form for fejl gemt bag disse redundante forhold. Måske tjekkede de ikke, hvad der krævedes.
Ctrl+C Ctrl+V slår tilbage
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 opmærksom på checken indeni if. Synes du ikke, det er mærkeligt, at vi tjekker, om adressen er gyldig? psmfData, dobbelt så meget? Så det virker mærkeligt for mig... Faktisk er dette selvfølgelig en tastefejl, og tanken var at tjekke begge inputparametre.
Korrekt mulighed:
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 fejl er placeret i mappen ext, så ikke rigtig relevant for projektet, men fejlen blev fundet før jeg lagde mærke til den, så jeg besluttede at forlade den. Denne artikel handler jo ikke om gennemgang af fejl, men om integration med Travis CI, og der blev ikke foretaget nogen konfiguration af analysatoren.
Variabel størrelse initialiseres af en konstant, men den bruges slet ikke i koden, helt ned til operatøren if, hvilket selvfølgelig giver falsk mens vi tjekker forholdene, fordi, som vi husker, størrelse lig med nul. Efterfølgende kontroller giver heller ingen mening.
Tilsyneladende glemte forfatteren af kodefragmentet at overskrive variablen størrelse inden da.
Stands
Det er her, vi nok ender med fejlene. Formålet med denne artikel er at demonstrere arbejdet i PVS-Studio sammen med Travis CI, og ikke at analysere projektet så grundigt som muligt. Hvis du vil have større og smukkere fejl, kan du altid beundre dem
Konklusion
Brug af webtjenester til at bygge projekter sammen med praksis med trinvis analyse giver dig mulighed for at finde mange problemer umiddelbart efter sammenlægning af kode. Men én build er muligvis ikke nok, så opsætning af test sammen med statisk analyse vil forbedre kvaliteten af koden markant.
Nyttige links
Hvis du vil dele denne artikel med et engelsktalende publikum, så brug venligst oversættelseslinket: Maxim Zvyagintsev.
Kilde: www.habr.com