Travis CI është një shërbim ueb i shpërndarë për ndërtimin dhe testimin e softuerit që përdor GitHub si host të kodit burimor. Përveç skenarëve të mësipërm të funksionimit, ju mund të shtoni tuajin falë opsioneve të gjera të konfigurimit. Në këtë artikull ne do të konfigurojmë Travis CI për të punuar me PVS-Studio duke përdorur shembullin e kodit PPSSPP.
Paraqitje
Vendosja e Travis CI
Do të na duhet një depo në GitHub, ku ndodhet projekti që na nevojitet, si dhe një çelës për PVS-Studio (mund të merrni
Le të shkojmë në sit
Për provën, kam pirun PPSSPP.
Ne aktivizojmë depon që duam të mbledhim:
Për momentin, Travis CI nuk mund të ndërtojë projektin tonë sepse nuk ka udhëzime për ndërtimin. Pra, është koha për konfigurim.
Gjatë analizës, disa variabla do të jenë të dobishëm për ne, për shembull, çelësi për PVS-Studio, i cili do të ishte i padëshirueshëm të specifikohej në skedarin e konfigurimit. Pra, le të shtojmë variablat e mjedisit duke përdorur cilësimet e ndërtimit në Travis CI:
Ne do të duhet:
- PVS_USERNAME - emri i përdoruesit
- PVS_KEY - çelësi
- MAIL_USER - email që do të përdoret për të dërguar raportin
- MAIL_PASSWORD - fjalëkalimi i emailit
Dy të fundit janë opsionale. Këto do të përdoren për të dërguar rezultatet me postë. Nëse dëshironi ta shpërndani raportin në një mënyrë tjetër, nuk keni nevojë t'i tregoni ato.
Pra, ne kemi shtuar variablat e mjedisit që na duhen:
Tani le të krijojmë një skedar .travis.yml dhe vendoseni në rrënjën e projektit. PPSSPP tashmë kishte një skedar konfigurimi për Travis CI, megjithatë, ai ishte shumë i madh dhe plotësisht i papërshtatshëm për shembullin, kështu që na duhej ta thjeshtonim shumë dhe të linim vetëm elementët bazë.
Së pari, le të tregojmë gjuhën, versionin e Ubuntu Linux që duam të përdorim në makinën virtuale dhe paketat e nevojshme për ndërtimin:
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'
Të gjitha paketat e listuara nevojiten ekskluzivisht për PPSSPP.
Tani ne tregojmë matricën e montimit:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Pak më shumë rreth seksionit matricë. Në Travis CI, ekzistojnë dy mënyra për të krijuar opsione ndërtimi: e para është të specifikoni një listë të përpiluesve, llojeve të sistemit operativ, variablave të mjedisit, etj., pas së cilës krijohet një matricë e të gjitha kombinimeve të mundshme; i dyti është një tregues i qartë i matricës. Sigurisht, ju mund t'i kombinoni këto dy qasje dhe të shtoni një rast unik, ose, përkundrazi, ta përjashtoni atë duke përdorur seksionin përjashtoj. Ju mund të lexoni më shumë rreth kësaj në
Gjithçka që mbetet është të jepni udhëzime të montimit të projektit specifik:
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 ju lejon të shtoni komandat tuaja për faza të ndryshme të jetës së një makine virtuale. Seksioni para_instalimit ekzekutuar përpara instalimit të paketave. Pastaj instaloj, e cila pason instalimin e paketave nga lista shtesa.apttë cilën e treguam më lart. Vetë asambleja zhvillohet në dorëshkrim. Nëse gjithçka shkoi mirë, atëherë ne e gjejmë veten brenda pas_suksesit (është në këtë seksion që ne do të kryejmë analizën statike). Këto nuk janë të gjitha hapat që mund të modifikohen, nëse keni nevojë për më shumë, atëherë duhet të shikoni
Për lehtësinë e leximit, komandat u vendosën në një skenar të veçantë .travis.sh, i cili vendoset në rrënjën e projektit.
Pra kemi skedarin e mëposhtëm .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
Përpara se të instalojmë paketat, ne do të përditësojmë nënmodulet. Kjo është e nevojshme për të ndërtuar PPSSPP. Le të shtojmë funksionin e parë në .travis.sh (vini re zgjerimin):
travis_before_install() {
git submodule update --init --recursive
}
Tani kemi ardhur drejtpërdrejt te konfigurimi i lëshimit automatik të PVS-Studio në Travis CI. Së pari duhet të instalojmë paketën PVS-Studio në 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
}
Në fillim të funksionit travis_install ne instalojmë kompajlerët që na duhen duke përdorur variablat e mjedisit. Atëherë nëse ndryshorja $PVS_ANALYZE ruan vlerën Po (e kemi treguar në seksion zili gjatë konfigurimit të matricës së ndërtimit), ne instalojmë paketën pvs-studio. Përveç kësaj, tregohen edhe paketat libio-socket-ssl-perl и libnet-ssleay-perl, megjithatë, ato kërkohen për rezultatet e postimit, kështu që nuk janë të nevojshme nëse keni zgjedhur një metodë tjetër për dorëzimin e raportit tuaj.
Funksion shkarkim_ekstrakt shkarkon dhe shpaketon arkivin e specifikuar:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Është koha për të bashkuar projektin. Kjo ndodh në seksion dorëshkrim:
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
}
Në fakt, ky është një konfigurim origjinal i thjeshtuar, me përjashtim të këtyre rreshtave:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
Në këtë seksion të kodit kemi vendosur për cmake flamur për eksportimin e komandave të përpilimit. Kjo është e nevojshme për një analizues të kodit statik. Mund të lexoni më shumë rreth kësaj në artikullin "
Nëse asambleja ishte e suksesshme, atëherë arrijmë pas_suksesit, ku kryejmë analiza statike:
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
}
Le të hedhim një vështrim më të afërt në rreshtat e mëposhtëm:
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
Rreshti i parë gjeneron një skedar licence nga emri i përdoruesit dhe çelësi që specifikuam që në fillim kur konfiguruam variablat e mjedisit Travis CI.
Rreshti i dytë fillon drejtpërdrejt analizën. Flamuri -j cakton numrin e fijeve për analizë, flamur -l tregon licencën, flamurin -o përcakton skedarin për nxjerrjen e regjistrave dhe flamurin -çaktivizoni kontrollin e skadimit të licencës kërkohet për versionet e provës, pasi si parazgjedhje pvs-studio-analizator do të paralajmërojë përdoruesin se licenca është gati të skadojë. Për të parandaluar që kjo të ndodhë, mund të specifikoni këtë flamur.
Skedari i regjistrit përmban rezultate të papërpunuara që nuk mund të lexohen pa konvertim, kështu që së pari duhet ta bëni skedarin të lexueshëm. Le t'i kalojmë shkrimet plog-konverter, dhe dalja është një skedar html.
Në këtë shembull, vendosa të dërgoj raporte me postë duke përdorur komandën Dërgoni një email.
Si rezultat, morëm skedarin e mëposhtëm .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;
Tani është koha për të shtyrë ndryshimet në depo git, pas së cilës Travis CI do të ekzekutojë automatikisht ndërtimin. Klikoni në "ppsspp" për të shkuar te raportet e ndërtimit:
Ne do të shohim një përmbledhje të ndërtimit aktual:
Nëse ndërtimi përfundon me sukses, do të marrim një email me rezultatet e analizës statike. Sigurisht, dërgimi me postë nuk është mënyra e vetme për të marrë një raport. Ju mund të zgjidhni çdo metodë zbatimi. Por është e rëndësishme të mbani mend se pasi të përfundojë ndërtimi, nuk do të jetë e mundur të aksesoni skedarët e makinës virtuale.
Përmbledhja e gabimit
Kemi përfunduar me sukses pjesën më të vështirë. Tani le të sigurohemi që të gjitha përpjekjet tona ia vlejnë. Le të shohim disa pika interesante nga raporti i analizës statike që më erdhi me postë (jo më kot e tregova).
Optimizimi i rrezikshëm
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 ) );
}
Paralajmërim PVS-Studio:
Kjo pjesë e kodit ndodhet në modulin e hashimit të sigurt, megjithatë, ai përmban një defekt serioz sigurie (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Gjithçka është në rregull dhe funksioni memeset është ekzekutuar, duke mbishkruar kështu të dhëna të rëndësishme në RAM, megjithatë, mos u gëzoni ende. Le të shohim listën e montimit të versionit të lëshimit me optimizim:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Siç mund të shihet nga lista, përpiluesi e injoroi thirrjen memeset. Kjo për faktin se në funksion sha1 pas thirrjes memeset nuk ka më referencë për strukturën CTX. Prandaj, përpiluesi nuk sheh asnjë pikë në humbjen e kohës së procesorit duke mbishkruar kujtesën që nuk përdoret në të ardhmen. Ju mund ta rregulloni këtë duke përdorur funksionin RtlSecureZeroMemory ose
saktë:
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 ) );
}
Krahasimi i panevojshëm
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);
}
}
Paralajmërim PVS-Studio:
Kushtojini vëmendje degës tjetër për të parën if. Kodi do të ekzekutohet vetëm nëse të gjitha kushtet leftvol > 0xFFFF || rightvol > 0xFFFF || volum i majtë < 0 || volum i drejtë < 0 do të rezultojë e rreme. Prandaj, marrim pohimet e mëposhtme, të cilat do të jenë të vërteta për degën tjetër: volum i majtë <= 0xFFFF, rightvol <= 0xFFFF, volum i majtë >= 0 и volum i drejtë >= 0. Vini re dy deklaratat e fundit. A ka kuptim të kontrollohet se cili është kushti i nevojshëm për ekzekutimin e kësaj pjese të kodit?
Kështu që ne mund t'i heqim me siguri këto deklarata të kushtëzuara:
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);
}
}
Një tjetër skenar. Ekziston një lloj gabimi i fshehur pas këtyre kushteve të tepërta. Ndoshta ata nuk kontrolluan atë që kërkohej.
Ctrl+C Ctrl+V Kundërshton
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Kushtojini vëmendje çekut brenda if. A nuk mendoni se është e çuditshme që ne kontrollojmë nëse adresa është e vlefshme? psmfData, dy herë më shumë? Pra, kjo më duket e çuditshme... Në fakt, kjo është, sigurisht, një gabim shtypi dhe ideja ishte të kontrolloheshin të dy parametrat e hyrjes.
Opsioni i duhur:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Ndryshore e harruar
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");
}
....
}
Paralajmërim PVS-Studio:
Ky gabim ndodhet në dosje ext, kështu që nuk është vërtet e rëndësishme për projektin, por defekti u gjet përpara se ta vura re, kështu që vendosa ta lë. Në fund të fundit, ky artikull nuk ka të bëjë me rishikimin e gabimeve, por me integrimin me Travis CI, dhe nuk u krye asnjë konfigurim i analizuesit.
Ndryshore madhësi inicializohet nga një konstante, megjithatë, ajo nuk përdoret fare në kod, deri te operatori if, e cila, natyrisht, jep i rremë gjatë kontrollit të kushteve, sepse, siç kujtojmë, madhësi e barabartë me zero. Kontrollet e mëvonshme gjithashtu nuk kanë kuptim.
Me sa duket, autori i fragmentit të kodit ka harruar të mbishkruajë variablin madhësi përpara se.
Stop
Këtu ndoshta do të përfundojmë me gabimet. Qëllimi i këtij artikulli është të demonstrojë punën e PVS-Studio së bashku me Travis CI, dhe jo të analizojë projektin sa më plotësisht të jetë e mundur. Nëse doni gabime më të mëdha dhe më të bukura, gjithmonë mund t'i admironi ato
Përfundim
Përdorimi i shërbimeve në internet për të ndërtuar projekte së bashku me praktikën e analizës në rritje ju lejon të gjeni shumë probleme menjëherë pas bashkimit të kodit. Megjithatë, një ndërtim mund të mos jetë i mjaftueshëm, kështu që vendosja e testimit së bashku me analizën statike do të përmirësojë ndjeshëm cilësinë e kodit.
Lidhje të dobishme
Nëse dëshironi ta ndani këtë artikull me një audiencë anglishtfolëse, ju lutemi përdorni lidhjen e përkthimit: Maxim Zvyagintsev.
Burimi: www.habr.com