ProHoster > Blog > administracja > Jak skonfigurować PVS-Studio w Travis CI na przykładzie emulatora konsoli do gier PSP
Jak skonfigurować PVS-Studio w Travis CI na przykładzie emulatora konsoli do gier PSP
Travis CI to rozproszona usługa internetowa służąca do tworzenia i testowania oprogramowania, która wykorzystuje GitHub jako hosting kodu źródłowego. Oprócz powyższych scenariuszy działania, dzięki rozbudowanym możliwościom konfiguracyjnym możesz dodać własne. W tym artykule skonfigurujemy Travis CI do współpracy z PVS-Studio na przykładzie kodu PPSSPP.
Wprowadzenie
Travis CI to usługa internetowa służąca do tworzenia i testowania oprogramowania. Zwykle stosuje się go w połączeniu z praktykami ciągłej integracji.
PPSSPP - Emulator konsoli do gier PSP. Program jest w stanie emulować uruchamianie dowolnych gier z obrazów dysków przeznaczonych dla Sony PSP. Program został wyemitowany 1 listopada 2012 roku. PPSSPP jest objęty licencją GPL v2. Każdy może dokonać ulepszeń kod źródłowy projektu.
Studio PVS — statyczny analizator kodu służący do wyszukiwania błędów i potencjalnych luk w kodzie programu. W tym artykule dla odmiany uruchomimy PVS-Studio nie lokalnie na komputerze dewelopera, a w chmurze i poszukamy błędów w PPSSPP.
Przejdźmy do serwisu Travis CI. Po autoryzacji przy użyciu konta GitHub wyświetli nam się lista repozytoriów:
Na potrzeby testu rozwidliłem PPSSPP.
Aktywujemy repozytorium, które chcemy zebrać:
W tej chwili Travis CI nie może zbudować naszego projektu, ponieważ nie ma instrukcji budowania. Czas więc na konfigurację.
Podczas analizy przydadzą nam się niektóre zmienne, np. klucz do PVS-Studio, którego określenie w pliku konfiguracyjnym byłoby niepożądane. Dodajmy więc zmienne środowiskowe, korzystając z ustawień kompilacji w Travis CI:
Potrzebujemy:
PVS_USERNAME — nazwa użytkownika
PVS_KEY - klucz
MAIL_USER - adres e-mail, na który zostanie przesłany raport
MAIL_PASSWORD – hasło e-mail
Dwa ostatnie są opcjonalne. Zostaną one wykorzystane do przesłania wyników pocztą. Jeśli chcesz rozpowszechnić raport w inny sposób, nie musisz tego wskazywać.
Dodaliśmy więc zmienne środowiskowe, których potrzebujemy:
Teraz utwórzmy plik .travis.yml i umieść go w katalogu głównym projektu. PPSSPP posiadało już plik konfiguracyjny dla Travis CI, jednak był on za duży i zupełnie nie nadawał się do przykładu, więc musieliśmy go znacznie uprościć i pozostawić jedynie podstawowe elementy.
Na początek wskażmy język, wersję Ubuntu Linux, której chcemy używać na maszynie wirtualnej oraz niezbędne pakiety do kompilacji:
Wszystkie wymienione pakiety są potrzebne wyłącznie dla PPSSPP.
Teraz wskazujemy macierz montażu:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Trochę więcej o dziale matryca. W Travis CI istnieją dwa sposoby tworzenia opcji kompilacji: pierwszy polega na określeniu listy kompilatorów, typów systemów operacyjnych, zmiennych środowiskowych itp., po czym generowana jest macierz wszystkich możliwych kombinacji; drugi to wyraźne wskazanie macierzy. Oczywiście można połączyć te dwa podejścia i dodać unikalny przypadek lub wręcz przeciwnie, wykluczyć go za pomocą sekcji wykluczać. Więcej na ten temat można przeczytać w Dokumentacja Travisa CI.
Pozostaje tylko dostarczyć instrukcję montażu dostosowaną do projektu:
Travis CI umożliwia dodawanie własnych poleceń dla różnych etapów życia maszyny wirtualnej. Sekcja przed_instalacją wykonywane przed instalacją pakietów. Następnie zainstalować, co następuje po instalacji pakietów z listy dodatki.aptco wskazaliśmy powyżej. Sam montaż odbywa się w scenariusz. Jeśli wszystko poszło dobrze, znajdziemy się w środku po_sukcesu (w tej sekcji przeprowadzimy analizę statyczną). To nie wszystkie kroki, które można zmodyfikować, jeśli potrzebujesz więcej, powinieneś zajrzeć Dokumentacja Travisa CI.
Dla ułatwienia czytania polecenia zostały umieszczone w osobnym skrypcie .travis.sh, który jest umieszczony w katalogu głównym projektu.
Przed instalacją pakietów zaktualizujemy podmoduły. Jest to potrzebne do zbudowania PPSSPP. Dodajmy pierwszą funkcję do .travis.sh (zwróć uwagę na rozszerzenie):
Teraz przechodzimy bezpośrednio do konfiguracji automatycznego uruchamiania PVS-Studio w Travis CI. Najpierw musimy zainstalować w systemie pakiet PVS-Studio:
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
}
Na początku funkcji travis_install instalujemy potrzebne kompilatory, używając zmiennych środowiskowych. Następnie, jeśli zmienna $PVS_ANALYZE przechowuje wartość Tak (wskazaliśmy to w sekcji env podczas konfiguracji build matrix) instalujemy pakiet studio pvs. Oprócz tego wskazane są również pakiety libio-socket-ssl-perl и libnet-ssley-perl, są one jednak wymagane do wyników wysyłki, więc nie są konieczne, jeśli wybrałeś inną metodę dostarczenia raportu.
Funkcja pobierz_wyciąg pobiera i rozpakowuje określone archiwum:
Czas złożyć projekt w całość. Dzieje się to w sekcji scenariusz:
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
}
W rzeczywistości jest to uproszczona oryginalna konfiguracja, z wyjątkiem tych linii:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
W tej części kodu ustawiliśmy cmake flaga do eksportowania poleceń kompilacji. Jest to konieczne w przypadku statycznego analizatora kodu. Więcej na ten temat przeczytasz w artykule „Jak uruchomić PVS-Studio na systemach Linux i macOS".
Jeśli montaż przebiegł pomyślnie, to przechodzimy do po_sukcesu, gdzie wykonujemy analizę statyczną:
Pierwsza linia generuje plik licencji na podstawie nazwy użytkownika i klucza, które podaliśmy na samym początku podczas konfigurowania zmiennych środowiskowych Travis CI.
Druga linia bezpośrednio rozpoczyna analizę. Flaga -J ustawia liczbę wątków do analizy, flaga -l wskazuje licencję, flagę -o definiuje plik do wysyłania dzienników i flagę -wyłącz kontrolę wygaśnięcia licencji wymagane w przypadku wersji próbnych, ponieważ domyślnie analizator pvs-studio ostrzeże użytkownika, że licencja wkrótce wygaśnie. Aby temu zapobiec, możesz określić tę flagę.
Plik dziennika zawiera surowe dane wyjściowe, których nie można odczytać bez konwersji, dlatego należy najpierw zapewnić możliwość odczytu pliku. Przepuśćmy logi konwerter plogów, a wynikiem jest plik HTML.
W tym przykładzie zdecydowałem się na wysyłanie raportów pocztą za pomocą polecenia wysłać email.
W rezultacie otrzymaliśmy następujący plik .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;
Teraz nadszedł czas, aby wypchnąć zmiany do repozytorium git, po czym Travis CI automatycznie uruchomi kompilację. Kliknij „ppsspp”, aby przejść do raportów kompilacji:
Zobaczymy przegląd bieżącej kompilacji:
Jeśli kompilacja zakończy się pomyślnie, otrzymamy wiadomość e-mail z wynikami analizy statycznej. Oczywiście mailing nie jest jedyną metodą otrzymania raportu. Możesz wybrać dowolną metodę realizacji. Należy jednak pamiętać, że po zakończeniu kompilacji dostęp do plików maszyny wirtualnej nie będzie możliwy.
Podsumowanie błędów
Udało nam się pomyślnie przejść najtrudniejszą część. Teraz upewnijmy się, że wszystkie nasze wysiłki są tego warte. Przyjrzyjmy się kilku ciekawym punktom z raportu analizy statycznej, który przyszedł do mnie pocztą (nie bez powodu to wskazałem).
Ostrzeżenie PVS-Studio: V597 Kompilator mógłby usunąć wywołanie funkcji „memset”, która służy do opróżniania bufora „sumy”. Do usunięcia prywatnych danych należy użyć funkcji RtlSecureZeroMemory(). sha1.cpp 325
Ten fragment kodu znajduje się w bezpiecznym module mieszającym, jednak zawiera poważną lukę w zabezpieczeniach (CWE-14). Przyjrzyjmy się liście zestawu generowanej podczas kompilacji wersji debugującej:
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Wszystko jest w porządku i działa zestaw memów jest wykonywany, zastępując w ten sposób ważne dane w pamięci RAM, jednak jeszcze się nie ciesz. Spójrzmy na listę zestawów wersji Release z optymalizacją:
Jak widać z listy, kompilator zignorował wywołanie zestaw memów. Dzieje się tak dlatego, że w funkcji sha1 po rozmowie zestaw memów nie ma już odniesień do struktury ctx. Dlatego kompilator nie widzi sensu marnowania czasu procesora na nadpisywanie pamięci, która nie będzie używana w przyszłości. Możesz to naprawić za pomocą funkcji Pamięć RtlSecureZero lub podobne do niej.
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);
}
}
Ostrzeżenie PVS-Studio: V547 Wyrażenie „leftvol >= 0” jest zawsze prawdziwe. sceAudio.cpp 120
Najpierw zwróć uwagę na gałąź else if. Kod zostanie wykonany tylko wtedy, gdy zostaną spełnione wszystkie warunki lewy tom > 0xFFFF || prawa część > 0xFFFF || lewy tom < 0 || prawa część < 0 okaże się fałszywe. Otrzymujemy zatem następujące stwierdzenia, które będą prawdziwe dla gałęzi else: lewy tom <= 0xFFFF, prawy tom <= 0xFFFF, lewa objętość >= 0 и prawa część >= 0. Zwróć uwagę na dwa ostatnie stwierdzenia. Czy jest sens sprawdzać jaki jest warunek konieczny wykonania tego fragmentu kodu?
Możemy więc bezpiecznie usunąć te instrukcje warunkowe:
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);
}
}
Inny scenariusz. Za tymi zbędnymi warunkami kryje się jakiś błąd. Być może nie sprawdzili, co było wymagane.
V501 Istnieją identyczne podwyrażenia „!Memory::IsValidAddress(psmfData)” po lewej i prawej stronie „||” operator. scePsmf.cpp 703
Zwróć uwagę na czek w środku if. Nie sądzisz, że to dziwne, że sprawdzamy, czy adres jest ważny? dane psmf, dwa razy więcej? Więc wydaje mi się to dziwne... W rzeczywistości jest to oczywiście literówka, a pomysł był taki, aby sprawdzić oba parametry wejściowe.
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");
}
....
}
Ostrzeżenie PVS-Studio: V547 Wyrażenie „rozmiar == 8” jest zawsze fałszywe. syn-att.c 195
Ten błąd znajduje się w folderze ext, więc nie ma to większego związku z projektem, ale błąd został znaleziony, zanim go zauważyłem, więc zdecydowałem się go zostawić. Przecież ten artykuł nie jest o przeglądaniu błędów, ale o integracji z Travis CI, a nie była przeprowadzana żadna konfiguracja analizatora.
Zmienna rozmiar jest inicjowany przez stałą, jednakże nie jest w ogóle używany w kodzie, aż do operatora if, co oczywiście daje fałszywy sprawdzając warunki, bo jak pamiętamy, rozmiar równy zeru. Późniejsze kontrole też nie mają sensu.
Najwyraźniej autor fragmentu kodu zapomniał nadpisać zmienną rozmiar przed tym.
Stop
Na tym prawdopodobnie zakończymy na błędach. Celem tego artykułu jest pokazanie pracy PVS-Studio wspólnie z Travisem CI, a nie możliwie najdokładniejsza analiza projektu. Jeśli chcesz większych i piękniejszych błędów, zawsze możesz je podziwiać tutaj :).
wniosek
Wykorzystanie usług sieciowych do budowy projektów w połączeniu z praktyką analizy przyrostowej pozwala znaleźć wiele problemów zaraz po połączeniu kodu. Jednak jedna kompilacja może nie wystarczyć, dlatego skonfigurowanie testów wraz z analizą statyczną znacząco poprawi jakość kodu.