Travis CI är en distribuerad webbtjänst för att bygga och testa programvara som använder GitHub som källkodsvärd. Förutom ovanstående driftscenarier kan du lägga till dina egna tack vare de omfattande konfigurationsalternativen. I den här artikeln kommer vi att konfigurera Travis CI att arbeta med PVS-Studio med hjälp av PPSSPP-kodexemplet.
Inledning
Konfigurera Travis CI
Vi kommer att behöva ett arkiv på GitHub, där projektet vi behöver finns, samt en nyckel för PVS-Studio (du kan få
Låt oss gå till webbplatsen
För testet gaffla jag PPSSPP.
Vi aktiverar arkivet som vi vill samla in:
För tillfället kan Travis CI inte bygga vårt projekt eftersom det inte finns några instruktioner för att bygga. Så det är dags för konfiguration.
Under analysen kommer vissa variabler att vara användbara för oss, till exempel nyckeln för PVS-Studio, vilket skulle vara oönskat att specificera i konfigurationsfilen. Så låt oss lägga till miljövariabler med hjälp av bygginställningarna i Travis CI:
Vi behöver:
- PVS_USERNAME - användarnamn
- PVS_KEY - nyckel
- MAIL_USER - e-post som kommer att användas för att skicka rapporten
- MAIL_PASSWORD - e-postlösenord
De två sista är valfria. Dessa kommer att användas för att skicka resultat via post. Om du vill distribuera rapporten på annat sätt behöver du inte ange dem.
Så vi har lagt till de miljövariabler vi behöver:
Låt oss nu skapa en fil .travis.yml och placera den i roten av projektet. PPSSPP hade redan en konfigurationsfil för Travis CI, men den var för stor och helt olämplig för exemplet, så vi var tvungna att förenkla den kraftigt och bara lämna de grundläggande elementen.
Låt oss först ange språket, versionen av Ubuntu Linux som vi vill använda i den virtuella maskinen och de nödvändiga paketen för bygget:
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'
Alla paket som är listade behövs exklusivt för PPSSPP.
Nu anger 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
Lite mer om avsnittet matris. I Travis CI finns det två sätt att skapa byggalternativ: det första är att specificera en lista med kompilatorer, operativsystemtyper, miljövariabler etc., varefter en matris med alla möjliga kombinationer genereras; den andra är en explicit indikation på matrisen. Naturligtvis kan du kombinera dessa två tillvägagångssätt och lägga till ett unikt fall, eller tvärtom, utesluta det med hjälp av avsnittet utesluta. Du kan läsa mer om detta i
Allt som återstår är att tillhandahålla projektspecifika monteringsanvisningar:
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 låter dig lägga till dina egna kommandon för olika stadier av en virtuell maskins liv. Sektion före_installera körs innan du installerar paket. Sedan installera, som följer installationen av paket från listan addons.aptsom vi angav ovan. Själva monteringen sker i skript. Om allt gick bra, då befinner vi oss i efter_framgång (det är i detta avsnitt som vi kommer att köra statisk analys). Det här är inte alla steg som kan modifieras, behöver du fler så ska du titta in
För att underlätta läsningen placerades kommandona i ett separat skript .travis.sh, som placeras vid projektroten.
Så vi har följande 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
Innan vi installerar paketen kommer vi att uppdatera undermodulerna. Detta behövs för att bygga PPSSPP. Låt oss lägga till den första funktionen till .travis.sh (observera tillägget):
travis_before_install() {
git submodule update --init --recursive
}
Nu kommer vi direkt till att sätta upp den automatiska lanseringen av PVS-Studio i Travis CI. Först måste vi installera PVS-Studio-paketet 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 början av funktionen travis_install vi installerar kompilatorerna vi behöver med hjälp av miljövariabler. Sedan om variabeln $PVS_ANALYZE lagrar värde Ja (vi angav det i avsnittet env under byggmatriskonfiguration) installerar vi paketet pvs-studio. Utöver detta anges även paket libio-socket-ssl-perl и libnet-ssleay-perl, men de krävs för att skicka resultat, så de är inte nödvändiga om du har valt en annan metod för att leverera din rapport.
Funktion download_extract laddar ner och packar upp det angivna arkivet:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Det är dags att sätta ihop projektet. Detta händer i avsnittet 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
}
Faktum är att detta är en förenklad originalkonfiguration, förutom dessa rader:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
I det här avsnittet av koden ställer vi in för cmmake flagga för export av kompileringskommandon. Detta är nödvändigt för en statisk kodanalysator. Du kan läsa mer om detta i artikeln "
Om monteringen lyckades, då kommer vi till efter_framgång, där vi utför statisk analys:
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
}
Låt oss titta närmare på följande rader:
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örsta raden genererar en licensfil från användarnamnet och nyckeln som vi angav i början när vi satte upp Travis CI-miljövariablerna.
Den andra raden startar analysen direkt. Flagga -j ställer in antalet trådar för analys, flagga -l anger licens, flagga -o definierar filen för utmatning av loggar och flaggan -disableLicenseExpirationCheck krävs för testversioner, eftersom som standard pvs-studio-analysator kommer att varna användaren om att licensen är på väg att löpa ut. För att förhindra att detta händer kan du ange denna flagga.
Loggfilen innehåller råutdata som inte kan läsas utan konvertering, så du måste först göra filen läsbar. Låt oss passera stockarna plog-omvandlare, och utdata är en html-fil.
I det här exemplet bestämde jag mig för att skicka rapporter per post med kommandot skicka epost.
Som ett resultat fick vi följande 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 är det dags att driva ändringarna i git-förvaret, varefter Travis CI kommer att köra bygget automatiskt. Klicka på "ppsspp" för att gå till byggrapporterna:
Vi kommer att se en översikt över den nuvarande konstruktionen:
Om bygget slutförs framgångsrikt kommer vi att få ett e-postmeddelande med resultaten av den statiska analysen. Utskick är naturligtvis inte det enda sättet att få en rapport. Du kan välja vilken implementeringsmetod som helst. Men det är viktigt att komma ihåg att efter att konstruktionen är klar kommer det inte att vara möjligt att komma åt de virtuella maskinfilerna.
Felsammanfattning
Vi har framgångsrikt slutfört den svåraste delen. Låt oss nu se till att alla våra ansträngningar är värda det. Låt oss titta på några intressanta punkter från den statiska analysrapporten som kom till mig per post (det var inte för inte som jag 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 varning:
Denna kodbit finns i den säkra hashmodulen, men den innehåller ett allvarligt säkerhetsfel (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Allt är i sin ordning och fungerar memeset exekveras och skrivs därmed över viktig data i RAM-minnet, men gläd dig inte ännu. Låt oss titta på monteringslistan för releaseversionen med optimering:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Som framgår av listan ignorerade kompilatorn anropet memeset. Detta beror på det faktum att i funktionen sha1 efter samtalet memeset ingen mer hänvisning till struktur ctx. Därför ser kompilatorn ingen mening med att slösa bort processortid på att skriva över minne som inte används i framtiden. Du kan fixa detta genom att använda funktionen RtlSecureZeroMemory eller
korrigera:
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 ) );
}
Onödig jämförelse
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 varning:
Var uppmärksam på den andra grenen för den första if. Koden kommer endast att köras om alla villkor leftvol > 0xFFFF || högervolym > 0xFFFF || leftvol < 0 || högervolym < 0 kommer att visa sig vara falskt. Därför får vi följande påståenden, vilket kommer att vara sant för den andra grenen: leftvol <= 0xFFFF, rightvol <= 0xFFFF, vänstervolym >= 0 и högervolym >= 0. Lägg märke till de två sista påståendena. Är det vettigt att kontrollera vad som är ett nödvändigt villkor för exekvering av denna kod?
Så vi kan säkert ta bort dessa villkorliga uttalanden:
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);
}
}
Ett annat scenario. Det finns något slags fel gömt bakom dessa redundanta villkor. Kanske kollade de inte vad som krävdes.
Ctrl+C Ctrl+V Slår tillbaka
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Var uppmärksam på checken inuti if. Tycker du inte att det är konstigt att vi kollar om adressen är giltig? psmfData, dubbelt så mycket? Så det här verkar konstigt för mig... I själva verket är detta naturligtvis ett stavfel, och tanken var att kontrollera båda inmatningsparametrarna.
Rätt 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");
}
....
}
Glömd 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 varning:
Detta fel finns i mappen ext, så inte riktigt relevant för projektet, men buggen hittades innan jag märkte den, så jag bestämde mig för att lämna den. När allt kommer omkring handlar den här artikeln inte om granskning av fel, utan om integration med Travis CI, och ingen konfiguration av analysatorn utfördes.
Variabel Storlek initieras av en konstant, men den används inte alls i koden, ända ner till operatören if, vilket naturligtvis ger falsk medan vi kontrollerar villkoren, eftersom, som vi minns, Storlek lika med noll. Efterföljande kontroller är inte heller meningsfulla.
Tydligen glömde författaren till kodfragmentet att skriva över variabeln Storlek före det.
Sluta
Det är här vi förmodligen kommer att sluta med misstagen. Syftet med denna artikel är att demonstrera PVS-Studios arbete tillsammans med Travis CI, och inte att analysera projektet så noggrant som möjligt. Vill du ha större och vackrare misstag kan du alltid beundra dem
Slutsats
Genom att använda webbtjänster för att bygga projekt tillsammans med övningen av inkrementell analys kan du hitta många problem omedelbart efter sammanslagning av kod. Det kan dock hända att en konstruktion inte räcker, så att sätta upp testning tillsammans med statisk analys kommer att avsevärt förbättra kodens kvalitet.
Användbara länkar
Om du vill dela den här artikeln med en engelsktalande publik, använd gärna översättningslänken: Maxim Zvyagintsev.
Källa: will.com