Travis CI, GitHub'u kaynak kodu barındırma olarak kullanan yazılım oluşturmaya ve test etmeye yönelik dağıtılmış bir web hizmetidir. Yukarıdaki çalışma senaryolarına ek olarak, kapsamlı konfigürasyon seçenekleri sayesinde kendinizinkini de ekleyebilirsiniz. Bu yazıda Travis CI'yı PPSSPP kod örneğini kullanarak PVS-Studio ile çalışacak şekilde yapılandıracağız.
Giriş
Travis CI'yi kurma
İhtiyacımız olan projenin bulunduğu GitHub'da bir depoya ve ayrıca PVS-Studio anahtarına ihtiyacımız olacak (alabilirsiniz)
Haydi siteye gidelim
Test için PPSSPP'yi çatalladım.
Toplamak istediğimiz depoyu etkinleştiriyoruz:
Şu anda Travis CI projemizi inşa edemiyor çünkü inşa etme talimatı yok. Yani konfigürasyon zamanı.
Analiz sırasında, bazı değişkenler bizim için yararlı olacaktır; örneğin, yapılandırma dosyasında belirtilmesi istenmeyen PVS-Studio anahtarı. Travis CI'daki derleme ayarlarını kullanarak ortam değişkenlerini ekleyelim:
İhtiyacımız:
- PVS_USERNAME - kullanıcı adı
- PVS_KEY - anahtar
- MAIL_USER - raporu göndermek için kullanılacak e-posta
- MAIL_PASSWORD - e-posta şifresi
Son ikisi isteğe bağlıdır. Bunlar sonuçları postayla göndermek için kullanılacaktır. Raporu başka bir şekilde dağıtmak istiyorsanız bunları belirtmenize gerek yoktur.
Böylece ihtiyacımız olan ortam değişkenlerini ekledik:
Şimdi bir dosya oluşturalım .travis.yml ve bunu projenin köküne yerleştirin. PPSSPP'nin Travis CI için zaten bir yapılandırma dosyası vardı, ancak bu çok büyüktü ve örnek için tamamen uygun değildi, bu yüzden onu büyük ölçüde basitleştirmek ve yalnızca temel öğeleri bırakmak zorunda kaldık.
Öncelikle sanal makinede kullanmak istediğimiz Ubuntu Linux sürümünü, dilini ve build için gerekli paketleri belirtelim:
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'
Listelenen tüm paketler yalnızca PPSSPP için gereklidir.
Şimdi montaj matrisini belirtiyoruz:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Bölüm hakkında biraz daha matris. Travis CI'da derleme seçenekleri oluşturmanın iki yolu vardır: birincisi, derleyicilerin, işletim sistemi türlerinin, ortam değişkenlerinin vb. listesini belirlemek ve ardından tüm olası kombinasyonların bir matrisini oluşturmaktır; ikincisi matrisin açık bir göstergesidir. Tabii ki, bu iki yaklaşımı birleştirebilir ve benzersiz bir durum ekleyebilir veya tam tersine, bölümü kullanarak onu hariç tutabilirsiniz. dışlamak. Bununla ilgili daha fazlasını şurada okuyabilirsiniz
Geriye kalan tek şey projeye özel montaj talimatlarını sağlamaktır:
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, bir sanal makinenin ömrünün çeşitli aşamaları için kendi komutlarınızı eklemenizi sağlar. Bölüm before_install paketleri kurmadan önce yürütülür. Daha sonra kurmaklisteden paketlerin kurulumunu takip eden addons.aptyukarıda da belirttiğimiz gibi. Montajın kendisi gerçekleşir senaryo. Her şey yolunda giderse, kendimizi içinde buluruz. başarı sonrası (Bu bölümde statik analiz yapacağız). Değiştirilebilecek adımların hepsi bunlar değil, daha fazlasına ihtiyacınız varsa o zaman şuraya bakmalısınız:
Okumayı kolaylaştırmak için komutlar ayrı bir komut dosyasına yerleştirildi .travis.sh, proje köküne yerleştirilir.
Yani aşağıdaki dosyaya sahibiz .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
Paketleri kurmadan önce alt modülleri güncelleyeceğiz. PPSSPP'yi oluşturmak için bu gereklidir. İlk fonksiyonu ekleyelim .travis.sh (uzantıya dikkat edin):
travis_before_install() {
git submodule update --init --recursive
}
Şimdi doğrudan Travis CI'da PVS-Studio'nun otomatik başlatılmasını ayarlamaya geliyoruz. Öncelikle PVS-Studio paketini sisteme kurmamız gerekiyor:
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
}
Fonksiyonun başında travis_install ihtiyaç duyduğumuz derleyicileri ortam değişkenlerini kullanarak kuruyoruz. O zaman eğer değişken $PVS_ANALYZE değeri saklar Evet (Bölümde belirttik) env derleme matrisi yapılandırması sırasında), paketi yükleriz pvs-studio. Buna ek olarak paketler de belirtilmiştir. libio-soket-ssl-perl и libnet-ssleay-perlancak sonuçların postalanması için gereklidirler, dolayısıyla raporunuzu iletmek için başka bir yöntem seçtiyseniz bunlar gerekli değildir.
Fonksiyon indirme_ekstresi belirtilen arşivi indirir ve paketini açar:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Projeyi bir araya getirmenin zamanı geldi. Bu bölümde oluyor senaryo:
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
}
Aslında bu, şu satırlar dışında basitleştirilmiş orijinal bir konfigürasyondur:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
Kodun bu bölümünde ayarladığımız yapmak derleme komutlarını dışa aktarmak için bayrak. Bu, statik kod analizörü için gereklidir. Bununla ilgili daha fazla bilgiyi "
Montaj başarılı olursa, o zaman başarı sonrasıStatik analiz yaptığımız 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
}
Şimdi aşağıdaki satırlara daha yakından bakalı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
İlk satır, Travis CI ortam değişkenlerini ayarlarken en başta belirttiğimiz kullanıcı adı ve anahtardan bir lisans dosyası oluşturur.
İkinci satır analizi doğrudan başlatır. Bayrak -J analiz için iş parçacığı sayısını ayarlar, bayrak -ben lisansı, bayrağı belirtir -Ö günlüklerin çıktısı için dosyayı ve bayrağı tanımlar -disableLicenseExpirationCheck deneme sürümleri için gereklidir, çünkü varsayılan olarak pvs-studio-analizörü kullanıcıyı lisansın süresinin dolmak üzere olduğu konusunda uyaracaktır. Bunun olmasını önlemek için bu bayrağı belirtebilirsiniz.
Günlük dosyası, dönüştürme olmadan okunamayan ham çıktı içerdiğinden, öncelikle dosyayı okunabilir hale getirmelisiniz. Günlükleri aktaralım plog-dönüştürücüve çıktı bir html dosyasıdır.
Bu örnekte, şu komutu kullanarak raporları postayla göndermeye karar verdim: eposta gönder.
Sonuç olarak aşağıdaki dosyayı elde ettik .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;
Şimdi değişiklikleri git deposuna aktarmanın zamanı geldi, ardından Travis CI derlemeyi otomatik olarak çalıştıracak. Yapı raporlarına gitmek için “ppsspp”e tıklayın:
Mevcut yapıya genel bir bakış göreceğiz:
Derleme başarıyla tamamlanırsa statik analizin sonuçlarını içeren bir e-posta alacağız. Elbette rapor almanın tek yolu posta göndermek değildir. Herhangi bir uygulama yöntemini seçebilirsiniz. Ancak derleme tamamlandıktan sonra sanal makine dosyalarına erişmenin imkansız olacağını unutmamak önemlidir.
Hata özeti
En zor kısmı başarıyla tamamladık. Şimdi tüm çabalarımızın buna değdiğinden emin olalım. Bana posta yoluyla gelen statik analiz raporundan bazı ilginç noktalara bakalım (bunu belirtmem boşuna değildi).
Tehlikeli optimizasyon
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 uyarısı:
Bu kod parçası güvenli karma modülünde bulunur ancak ciddi bir güvenlik açığı içerir (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Her şey yolunda ve işlev meme seti yürütülür, böylece RAM'deki önemli verilerin üzerine yazılır, ancak henüz sevinmeyin. Optimizasyonlu Sürüm versiyonunun montaj listesine bakalım:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Listeden görülebileceği gibi derleyici çağrıyı görmezden geldi meme seti. Bunun nedeni, fonksiyonda sha1 aramadan sonra meme seti artık yapıya referans yok ctx. Bu nedenle derleyici, gelecekte kullanılmayan belleğin üzerine yazarak işlemci zamanını boşa harcamanın bir anlamı görmez. Bu işlevi kullanarak bunu düzeltebilirsiniz. RtlSecureZeroBellek veya
düzeltin:
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 ) );
}
Gereksiz karşılaştırma
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 uyarısı:
İlk olarak else şubesine dikkat edin if. Kod yalnızca tüm koşullar sağlandığında yürütülecektir solvol > 0xFFFF || sağvol > 0xFFFF || solvol < 0 || sağvol < 0 sahte olduğu ortaya çıkacak. Bu nedenle else dalı için geçerli olan aşağıdaki ifadeleri elde ederiz: solvol <= 0xFFFF, sağvol <= 0xFFFF, solvol >= 0 и sağvol >= 0. Son iki ifadeye dikkat edin. Bu kod parçasının yürütülmesi için gerekli koşulun ne olduğunu kontrol etmek mantıklı mı?
Böylece bu koşullu ifadeleri güvenle kaldırabiliriz:
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şka bir senaryo. Bu gereksiz koşulların arkasında bir tür hata gizlidir. Belki de neyin gerekli olduğunu kontrol etmediler.
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");
}
....
}
İçerideki çeke dikkat edin if. Adresin geçerli olup olmadığını kontrol etmemiz sizce de tuhaf değil mi? psmfVerileri, iki katı kadar mı? Yani bu bana tuhaf geliyor... Aslında bu elbette bir yazım hatası ve amaç her iki giriş parametresini de kontrol etmekti.
Doğru seçenek:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Unutulan değişken
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 uyarısı:
Bu hata klasörde bulunur extyani projeyle pek alakalı değil ama hata ben farkına varmadan bulundu, bu yüzden onu bırakmaya karar verdim. Sonuçta bu makale hataların gözden geçirilmesiyle ilgili değil, Travis CI ile entegrasyonla ilgili ve analizörün herhangi bir yapılandırması yapılmadı.
değişken boyut bir sabit tarafından başlatılır, ancak operatöre kadar kodda hiç kullanılmaz ifki bu da elbette şunu sağlıyor yanlış koşulları kontrol ederken, çünkü hatırladığımız gibi, boyut sıfıra eşittir. Daha sonraki kontrollerin de hiçbir anlamı yok.
Görünüşe göre kod parçasının yazarı değişkenin üzerine yazmayı unutmuş boyut bundan önce.
dur
Muhtemelen hatalarla son bulacağımız yer burasıdır. Bu makalenin amacı, projeyi mümkün olduğunca ayrıntılı bir şekilde analiz etmek değil, PVS-Studio'nun Travis CI ile birlikte çalışmasını göstermektir. Daha büyük ve daha güzel hatalar istiyorsanız onlara her zaman hayran olabilirsiniz
Sonuç
Artımlı analiz uygulamasıyla birlikte projeler oluşturmak için web hizmetlerini kullanmak, kodu birleştirdikten hemen sonra birçok sorunu bulmanızı sağlar. Ancak tek bir derleme yeterli olmayabilir, bu nedenle testi statik analizle birlikte ayarlamak kodun kalitesini önemli ölçüde artıracaktır.
Faydalı linkler
Bu makaleyi İngilizce konuşan bir kitleyle paylaşmak istiyorsanız lütfen çeviri bağlantısını kullanın: Maxim Zvyagintsev.
Kaynak: habr.com