Travis CI è un servizio Web distribuito per la creazione e il test di software che utilizza GitHub come hosting del codice sorgente. Oltre agli scenari operativi sopra indicati, puoi aggiungerne di nuovi grazie alle ampie opzioni di configurazione. In questo articolo configureremo Travis CI per funzionare con PVS-Studio utilizzando l'esempio di codice PPSSPP.
Introduzione
Configurazione di Travis CI
Avremo bisogno di un repository su GitHub, dove si trova il progetto di cui abbiamo bisogno, nonché di una chiave per PVS-Studio (puoi ottenerla
Andiamo al sito
Per il test ho effettuato un fork di PPSSPP.
Attiviamo il repository che vogliamo raccogliere:
Al momento, Travis CI non può realizzare il nostro progetto perché non ci sono istruzioni per la costruzione. Quindi è il momento della configurazione.
Durante l'analisi ci saranno utili alcune variabili, ad esempio la chiave per PVS-Studio, che sarebbe indesiderabile specificare nel file di configurazione. Quindi aggiungiamo variabili di ambiente utilizzando le impostazioni di build in Travis CI:
Abbiamo bisogno di:
- PVS_USERNAME - nome utente
- PVS_KEY - chiave
- MAIL_USER - email che verrà utilizzata per inviare il report
- MAIL_PASSWORD: password e-mail
Gli ultimi due sono facoltativi. Questi verranno utilizzati per inviare i risultati tramite posta. Se vuoi distribuire il report in altro modo non è necessario indicarli.
Quindi, abbiamo aggiunto le variabili d'ambiente di cui abbiamo bisogno:
Ora creiamo un file .travis.yml e posizionarlo nella radice del progetto. PPSSPP aveva già un file di configurazione per Travis CI, tuttavia era troppo grande e del tutto inadatto all'esempio, quindi abbiamo dovuto semplificarlo notevolmente e lasciare solo gli elementi di base.
Per prima cosa indichiamo la lingua, la versione di Ubuntu Linux che vogliamo utilizzare nella macchina virtuale e i pacchetti necessari per la build:
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'
Tutti i pacchetti elencati sono necessari esclusivamente per PPSSPP.
Ora indichiamo la matrice di assemblaggio:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Qualcosa in più sulla sezione matrice. In Travis CI, ci sono due modi per creare opzioni di build: il primo è specificare un elenco di compilatori, tipi di sistema operativo, variabili di ambiente, ecc., dopodiché viene generata una matrice di tutte le possibili combinazioni; la seconda è un'indicazione esplicita della matrice. Naturalmente puoi combinare questi due approcci e aggiungere un caso unico o, al contrario, escluderlo utilizzando la sezione escludere. Puoi leggere di più a riguardo in
Non resta che fornire le istruzioni di montaggio specifiche del progetto:
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 ti consente di aggiungere i tuoi comandi per le varie fasi della vita di una macchina virtuale. Sezione prima_installazione eseguito prima di installare i pacchetti. Poi install, che segue l'installazione dei pacchetti dall'elenco componenti aggiuntivi.aptche abbiamo sopra indicato. L'assemblea vera e propria si svolge in copione. Se tutto è andato bene allora ci ritroviamo dentro dopo_successo (è in questa sezione che eseguiremo l'analisi statica). Questi non sono tutti i passaggi che possono essere modificati, se ne hai bisogno di più, dovresti dare un'occhiata
Per facilità di lettura, i comandi sono stati inseriti in uno script separato .travis.sh, che viene posizionato nella radice del progetto.
Quindi abbiamo il seguente file .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
Prima di installare i pacchetti, aggiorneremo i sottomoduli. Ciò è necessario per creare PPSSPP. Aggiungiamo la prima funzione a .travis.sh (notare l'estensione):
travis_before_install() {
git submodule update --init --recursive
}
Ora arriviamo direttamente alla configurazione dell'avvio automatico di PVS-Studio in Travis CI. Per prima cosa dobbiamo installare il pacchetto PVS-Studio sul sistema:
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
}
All'inizio della funzione travis_install installiamo i compilatori di cui abbiamo bisogno utilizzando variabili d'ambiente. Quindi se la variabile $PVS_ANALISI memorizza valore Sì (lo abbiamo indicato nella sezione ENV durante la configurazione della matrice di build), installiamo il pacchetto pvs-studio. Oltre a questo vengono indicati anche i pacchetti libio-socket-ssl-perl и libnet-ssleay-perl, tuttavia, sono necessari per l'invio tramite posta dei risultati, quindi non sono necessari se hai scelto un altro metodo per consegnare il rapporto.
Funzione download_estrai scarica e decomprime l'archivio specificato:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
È il momento di mettere insieme il progetto. Questo accade nella sezione copione:
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
}
In realtà, questa è una configurazione originale semplificata, ad eccezione di queste righe:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
In questa sezione di codice impostiamo for cMake flag per esportare i comandi di compilazione. Ciò è necessario per un analizzatore di codice statico. Puoi leggere di più a riguardo nell’articolo “
Se l'assemblea ha avuto successo, allora arriviamo a dopo_successo, dove eseguiamo l'analisi statica:
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
}
Diamo un'occhiata più da vicino alle seguenti righe:
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
La prima riga genera un file di licenza dal nome utente e dalla chiave che abbiamo specificato all'inizio durante la configurazione delle variabili di ambiente Travis CI.
La seconda riga avvia direttamente l'analisi. Bandiera -J imposta il numero di thread per l'analisi, flag -l indica licenza, bandiera -o definisce il file per l'output dei log e il flag -disableLicenseExpirationCheck richiesto per le versioni di prova, poiché per impostazione predefinita pvs-studio-analizzatore avviserà l'utente che la licenza sta per scadere. Per evitare che ciò accada, è possibile specificare questo flag.
Il file di registro contiene output non elaborato che non può essere letto senza conversione, quindi è necessario prima rendere leggibile il file. Passiamo i log convertitore di ploge l'output è un file html.
In questo esempio, ho deciso di inviare report tramite posta utilizzando il comando invia una email.
Di conseguenza, abbiamo ottenuto il seguente file .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;
Ora è il momento di inviare le modifiche al repository git, dopodiché Travis CI eseguirà automaticamente la build. Fare clic su "ppsspp" per accedere ai rapporti di compilazione:
Vedremo una panoramica della build attuale:
Se la build viene completata con successo, riceveremo un'e-mail con i risultati dell'analisi statica. Naturalmente, l'invio tramite posta non è l'unico modo per ricevere un rapporto. Puoi scegliere qualsiasi metodo di implementazione. Ma è importante ricordare che una volta completata la compilazione non sarà possibile accedere ai file della macchina virtuale.
Riepilogo degli errori
Abbiamo completato con successo la parte più difficile. Ora assicuriamoci che tutti i nostri sforzi siano valsi la pena. Vediamo alcuni punti interessanti del rapporto di analisi statica che mi è arrivato via mail (non per niente lo avevo indicato).
Ottimizzazione pericolosa
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 ) );
}
Avviso PVS-Studio:
Questo pezzo di codice si trova nel modulo di hashing sicuro, tuttavia contiene una grave falla di sicurezza (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Tutto è in ordine e funzionante set di meme viene eseguito, sovrascrivendo così i dati importanti nella RAM, tuttavia, non rallegrarci ancora. Diamo un'occhiata all'elenco degli assembly della versione Release con ottimizzazione:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Come si può vedere dal listato, il compilatore ha ignorato la chiamata set di meme. Ciò è dovuto al fatto che nella funzione sha1 dopo la chiamata set di meme niente più riferimento alla struttura ctx. Pertanto, il compilatore non vede alcun motivo di sprecare tempo del processore sovrascrivendo la memoria che non verrà utilizzata in futuro. Puoi risolvere questo problema utilizzando la funzione RtlSecureZeroMemory o
correggere:
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 ) );
}
Confronto inutile
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);
}
}
Avviso PVS-Studio:
Presta attenzione al ramo else per il primo if. Il codice verrà eseguito solo se tutte le condizioni volsinistra > 0xFFFF || voldestra > 0xFFFF || volsinistra < 0 || voldestra < 0 risulterà falso. Pertanto, otteniamo le seguenti affermazioni, che saranno vere per il ramo else: volsinistra <= 0xFFFF, voldestra <= 0xFFFF, volsinistra >= 0 и voldestra >= 0. Notare le ultime due affermazioni. Ha senso verificare quale sia una condizione necessaria per l'esecuzione di questo pezzo di codice?
Quindi possiamo rimuovere in sicurezza queste istruzioni condizionali:
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);
}
}
Un altro scenario. Dietro queste condizioni ridondanti si nasconde qualche tipo di errore. Forse non hanno controllato cosa fosse richiesto.
Ctrl+C Ctrl+V colpisce ancora
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Presta attenzione al controllo interno if. Non trovi strano che controlliamo se l'indirizzo è valido? psmfData, due volte tanto? Quindi mi sembra strano... In realtà si tratta, ovviamente, di un errore di battitura e l'idea era di controllare entrambi i parametri di input.
Opzione corretta:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Variabile dimenticata
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");
}
....
}
Avviso PVS-Studio:
Questo errore si trova nella cartella ext, quindi non molto rilevante per il progetto, ma il bug è stato trovato prima che me ne accorgessi, quindi ho deciso di lasciarlo. Dopotutto, questo articolo non riguarda la revisione degli errori, ma l'integrazione con Travis CI e non è stata effettuata alcuna configurazione dell'analizzatore.
Переменная Taglia viene inizializzato da una costante, tuttavia non viene utilizzato affatto nel codice, nemmeno dall'operatore if, che, ovviamente, dà falso verificando le condizioni, perché, come ricordiamo, Taglia uguale a zero. Anche i controlli successivi non hanno senso.
Apparentemente l'autore del frammento di codice ha dimenticato di sovrascrivere la variabile Taglia prima di ciò.
Fermare
Qui è dove probabilmente finiremo con gli errori. Lo scopo di questo articolo è dimostrare il lavoro di PVS-Studio insieme a Travis CI e non analizzare il progetto nel modo più approfondito possibile. Se vuoi errori più grandi e più belli, puoi sempre ammirarli
conclusione
L'utilizzo dei servizi Web per creare progetti insieme alla pratica dell'analisi incrementale consente di individuare molti problemi immediatamente dopo l'unione del codice. Tuttavia, una build potrebbe non essere sufficiente, quindi l'impostazione dei test insieme all'analisi statica migliorerà significativamente la qualità del codice.
Link utili
Se desideri condividere questo articolo con un pubblico di lingua inglese, utilizza il link di traduzione: Maxim Zvyagintsev.
Fonte: habr.com