Travis CI, GitHub-dan mənbə kodu hostinqi kimi istifadə edən proqram təminatının yaradılması və sınaqdan keçirilməsi üçün paylanmış veb xidmətidir. Yuxarıda göstərilən əməliyyat ssenarilərinə əlavə olaraq, geniş konfiqurasiya seçimləri sayəsində özünüz də əlavə edə bilərsiniz. Bu yazıda biz PPSSPP kod nümunəsindən istifadə edərək Travis CI-ni PVS-Studio ilə işləmək üçün konfiqurasiya edəcəyik.
Giriş
Travis CI-nin qurulması
Bizə lazım olan layihənin yerləşdiyi GitHub-da depoya, həmçinin PVS-Studio üçün açara ehtiyacımız olacaq (əldə edə bilərsiniz)
Sayta gedək
Test üçün PPSSPP-ni çəngəllədim.
Biz toplamaq istədiyimiz deponu aktivləşdiririk:
Hazırda Travis CI layihəmizi qura bilmir, çünki tikinti üçün təlimat yoxdur. Beləliklə, konfiqurasiya vaxtıdır.
Təhlil zamanı bəzi dəyişənlər bizim üçün faydalı olacaq, məsələn, konfiqurasiya faylında qeyd edilməsi arzuolunmaz olan PVS-Studio açarı. Beləliklə, Travis CI-də qurma parametrlərindən istifadə edərək ətraf mühit dəyişənlərini əlavə edək:
Bizə lazımdır:
- PVS_USERNAME - istifadəçi adı
- PVS_KEY - açar
- MAIL_USER - hesabatı göndərmək üçün istifadə olunacaq e-poçt
- MAIL_PASSWORD - e-poçt parolu
Son ikisi isteğe bağlıdır. Bunlar nəticələri poçtla göndərmək üçün istifadə olunacaq. Hesabatı başqa bir şəkildə yaymaq istəyirsinizsə, onları göstərməyə ehtiyac yoxdur.
Beləliklə, bizə lazım olan mühit dəyişənlərini əlavə etdik:
İndi bir fayl yaradaq .travis.yml və onu layihənin kökünə yerləşdirin. PPSSPP-də artıq Travis CI üçün konfiqurasiya faylı var idi, lakin o, çox böyük idi və nümunə üçün tamamilə yararsız idi, ona görə də biz onu çox sadələşdirməli və yalnız əsas elementləri buraxmalı olduq.
Əvvəlcə virtual maşında istifadə etmək istədiyimiz dili, Ubuntu Linux versiyasını və qurmaq üçün lazım olan paketləri göstərək:
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'
Sadalanan bütün paketlər yalnız PPSSPP üçün lazımdır.
İndi montaj matrisini göstəririk:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Bölmə haqqında bir az daha matris. Travis CI-də qurma variantlarını yaratmağın iki yolu var: birincisi kompilyatorların siyahısını, əməliyyat sistemi növlərini, mühit dəyişənlərini və s. müəyyən etməkdir, bundan sonra bütün mümkün birləşmələrin matrisi yaradılır; ikincisi matrisin açıq göstəricisidir. Əlbəttə ki, bu iki yanaşmanı birləşdirə və unikal bir hal əlavə edə bilərsiniz və ya əksinə bölmədən istifadə edərək onu istisna edə bilərsiniz. istisna. Bu barədə ətraflı oxuya bilərsiniz
Qalan yalnız layihəyə uyğun montaj təlimatlarını təmin etməkdir:
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 sizə virtual maşının ömrünün müxtəlif mərhələləri üçün öz əmrlərinizi əlavə etməyə imkan verir. Bölmə quraşdırmadan əvvəl paketləri quraşdırmadan əvvəl yerinə yetirilir. Sonra qurmaq, siyahıdan paketlərin quraşdırılmasını izləyir addons.aptyuxarıda qeyd etdiyimiz. Məclis özü baş verir yazı. Hər şey yaxşı gedirsə, deməli özümüzü içəridə tapırıq uğurdan sonra (bu bölmədə statik təhlil aparacağıq). Bunlar dəyişdirilə bilən bütün addımlar deyil, daha çox ehtiyacınız varsa, baxmalısınız
Oxumağın asanlığı üçün əmrlər ayrıca bir skriptdə yerləşdirildi .travis.sh, layihənin kökündə yerləşdirilir.
Beləliklə, aşağıdakı faylımız var .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
Paketləri quraşdırmadan əvvəl biz alt modulları yeniləyəcəyik. Bu PPSSPP qurmaq üçün lazımdır. Birinci funksiyanı əlavə edək .travis.sh (uzatmaya diqqət yetirin):
travis_before_install() {
git submodule update --init --recursive
}
İndi biz birbaşa Travis CI-də PVS-Studio-nun avtomatik işə salınmasının qurulmasına gəlirik. Əvvəlcə sistemə PVS-Studio paketini quraşdırmalıyıq:
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
}
Funksiyanın əvvəlində travis_install mühit dəyişənlərindən istifadə edərək bizə lazım olan kompilyatorları quraşdırırıq. Sonra dəyişən əgər $PVS_ANALYZE dəyər saxlayır bəli (bölmədə qeyd etdik env matrix konfiqurasiyası zamanı) paketi quraşdırırıq pvs-studio. Bundan əlavə, paketlər də göstərilir libio-socket-ssl-perl и libnet-ssleay-perl, lakin onlar nəticələrin göndərilməsi üçün tələb olunur, ona görə də hesabatınızı çatdırmaq üçün başqa üsul seçmisinizsə, onlar lazım deyil.
Function endirmə_çıxarışı göstərilən arxivi yükləyir və paketdən çıxarır:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Layihəni bir araya gətirməyin vaxtı gəldi. Bu bölmədə baş verir yazı:
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
}
Əslində, bu, bu sətirlər istisna olmaqla, sadələşdirilmiş orijinal konfiqurasiyadır:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
Kodun bu hissəsində biz təyin etdik smake kompilyasiya əmrlərinin ixracı üçün bayraq. Bu, statik kod analizatoru üçün lazımdır. Bu barədə daha çox məqalədə oxuya bilərsiniz "
Əgər məclis uğurlu olarsa, deməli, çatırıq uğurdan sonra, statik analiz etdiyimiz yer:
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
}
Aşağıdakı sətirlərə daha yaxından nəzər salaq:
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
Birinci sətir Travis CI mühit dəyişənlərini qurarkən ən əvvəldə qeyd etdiyimiz istifadəçi adı və açardan lisenziya faylı yaradır.
İkinci sətir birbaşa təhlilə başlayır. Bayraq -j təhlil üçün mövzuların sayını təyin edir, bayraq edir -l lisenziyanı, bayrağı göstərir -o logların çıxarılması üçün faylı və bayrağı müəyyən edir -LicenseExpirationCheck-i söndürün sınaq versiyaları üçün tələb olunur, çünki standart olaraq pvs-studio-analizator istifadəçiyə lisenziyanın bitmək üzrə olduğu barədə xəbərdarlıq edəcək. Bunun qarşısını almaq üçün bu bayrağı göstərə bilərsiniz.
Jurnal faylında konvertasiya olmadan oxuna bilməyən xam çıxış var, ona görə də əvvəlcə faylı oxunaqlı etmək lazımdır. Gəlin qeydləri keçirək plog çevirici, və çıxış bir html faylıdır.
Bu nümunədə, əmrdən istifadə edərək hesabatları poçtla göndərmək qərarına gəldim e-poçt göndər.
Nəticədə aşağıdakı faylı əldə etdik .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;
İndi dəyişiklikləri git repozitoriyasına köçürməyin vaxtıdır, bundan sonra Travis CI qurmanı avtomatik işə salacaq. Quraşdırma hesabatlarına getmək üçün "ppsspp" üzərinə klikləyin:
Mövcud quruluşun icmalını görəcəyik:
Quraşdırma uğurla tamamlanarsa, biz statik analizin nəticələri ilə bir e-poçt alacağıq. Əlbəttə ki, poçt göndərişi hesabat almaq üçün yeganə yol deyil. İstənilən icra üsulunu seçə bilərsiniz. Ancaq yadda saxlamaq lazımdır ki, qurma tamamlandıqdan sonra virtual maşın fayllarına daxil olmaq mümkün olmayacaq.
Xəta xülasəsi
Ən çətin hissəni uğurla başa vurduq. İndi gəlin əmin olaq ki, bütün səylərimiz buna dəyər. Mənə poçtla gələn statik analiz hesabatından bir neçə maraqlı məqama nəzər salaq (əbəs yerə qeyd etməmişəm).
Təhlükəli optimallaşdırma
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 xəbərdarlığı:
Bu kod parçası təhlükəsiz hashing modulunda yerləşir, lakin ciddi təhlükəsizlik qüsurunu ehtiva edir (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Hər şey qaydasındadır və funksiyası var xatirə icra edilir, bununla da RAM-da vacib məlumatların üzərinə yazılır, lakin hələ də sevinməyin. Optimallaşdırma ilə Release versiyasının montaj siyahısına baxaq:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Siyahıdan göründüyü kimi, tərtibçi zəngə məhəl qoymadı xatirə. Bu, funksiyada olması ilə əlaqədardır sha1 zəngdən sonra xatirə struktura daha çox istinad yoxdur ctx. Buna görə də, kompilyator gələcəkdə istifadə edilməyən yaddaşın üzərinə yazmaq üçün prosessor vaxtını itirməyin mənasını görmür. Funksiyadan istifadə edərək bunu düzəldə bilərsiniz RtlSecureZeroMemory və ya
Düzgün:
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 ) );
}
Lazımsız müqayisə
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 xəbərdarlığı:
Birincisi üçün başqa filiala diqqət yetirin if. Kod yalnız bütün şərtlər olduqda icra ediləcək leftvol > 0xFFFF || rightvol > 0xFFFF || sol tərəf < 0 || sağ səs < 0 yalan çıxacaq. Beləliklə, başqa filial üçün doğru olacaq aşağıdakı ifadələri alırıq: sol səs <= 0xFFFF, sağ səs <= 0xFFFF, sol vol >= 0 и sağ həcm >= 0. Son iki ifadəyə diqqət yetirin. Bu kod parçasının icrası üçün zəruri şərtin nə olduğunu yoxlamağın mənası varmı?
Beləliklə, bu şərti ifadələri təhlükəsiz şəkildə silə bilərik:
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);
}
}
Başqa bir ssenari. Bu lazımsız şərtlərin arxasında bir növ səhv gizlənir. Ola bilsin ki, nə tələb olunduğunu yoxlamayıblar.
Ctrl+C Ctrl+V Geri vurur
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Daxili çekə diqqət yetirin if. Sizə qəribə gəlmir ki, ünvanı etibarlı olub-olmadığını yoxlayırıq? psmfData, iki dəfə çox? Ona görə də bu mənə qəribə görünür... Əslində bu, təbii ki, hərf səhvidir və ideya hər iki giriş parametrini yoxlamaq idi.
Düzgün seçim:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Unudulmuş dəyişən
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 xəbərdarlığı:
Bu səhv qovluqda yerləşir ext, odur ki, layihəyə həqiqətən də aid deyil, amma səhv mən bunu fərq etməmişdən əvvəl tapıldı, ona görə də onu tərk etmək qərarına gəldim. Axı, bu məqalə səhvləri nəzərdən keçirmək deyil, Travis CI ilə inteqrasiya haqqındadır və analizatorun heç bir konfiqurasiyası aparılmayıb.
Dəyişən boy sabit tərəfindən işə salınır, lakin o, operatora qədər kodda ümumiyyətlə istifadə edilmir if, bu, əlbəttə ki, verir saxta şərtləri yoxlayarkən, çünki xatırladığımız kimi, boy sıfıra bərabərdir. Sonrakı yoxlamaların da heç bir mənası yoxdur.
Görünür, kod fraqmentinin müəllifi dəyişənin üzərinə yazmağı unudub boy bundan əvvəl.
Dayandırmaq
Yəqin ki, səhvlərimizlə burada sona çatacağıq. Bu məqalənin məqsədi PVS-Studio-nun Travis CI ilə birlikdə işini nümayiş etdirməkdir və layihəni mümkün qədər hərtərəfli təhlil etmək deyil. Daha böyük və daha gözəl səhvlər istəyirsinizsə, onlara həmişə heyran ola bilərsiniz
Nəticə
Artan təhlil təcrübəsi ilə birlikdə layihələr qurmaq üçün veb xidmətlərdən istifadə kodu birləşdirdikdən dərhal sonra bir çox problemləri tapmağa imkan verir. Bununla belə, bir qurma kifayət olmaya bilər, buna görə də statik analizlə birlikdə testin qurulması kodun keyfiyyətini əhəmiyyətli dərəcədə yaxşılaşdıracaq.
Faydalı linklər
Bu məqaləni ingilisdilli auditoriya ilə bölüşmək istəyirsinizsə, tərcümə linkindən istifadə edin: Maksim Zvyagintsev.
Mənbə: www.habr.com