Uno degli scenari più attuali per l'utilizzo dell'analizzatore PVS-Studio è la sua integrazione con i sistemi CI. E sebbene l'analisi di un progetto PVS-Studio da quasi tutti i sistemi di integrazione continua possa essere integrata in pochi comandi, continuiamo a rendere questo processo ancora più conveniente. PVS-Studio ora supporta la conversione dell'output dell'analizzatore in un formato per TeamCity - Tipo di ispezione TeamCity. Vediamo come funziona.
Informazioni sul software utilizzato
Informazioni sul progetto in studio
Proviamo questa funzionalità su un esempio pratico: analizziamo il progetto OpenRCT2.
registrazione
Per risparmiare tempo, probabilmente salterò il processo di installazione e inizierò dal momento in cui ho il server TeamCity in esecuzione sul mio computer. Dobbiamo andare su: localhost:{porta specificata durante il processo di installazione} (nel mio caso, localhost:9090) e inserire i dati di autorizzazione. Dopo essere entrati verremo accolti da:
Fare clic sul pulsante Crea progetto. Successivamente, seleziona Manualmente e compila i campi.
Dopo aver premuto il pulsante Creare, siamo accolti da una finestra con le impostazioni.
Facciamo clic Crea la configurazione della build.
Compila i campi e clicca Creare. Vediamo una finestra che ti chiede di selezionare un sistema di controllo della versione. Poiché le fonti sono già localizzate localmente, fare clic su Saltare.
Passiamo infine alle impostazioni del progetto.
Aggiungiamo passaggi di assemblaggio, per fare questo clic: Passaggi di creazione -> Aggiungi passaggio di creazione.
Qui scegliamo:
- Tipo di corridore -> Riga di comando
- Esegui -> Script personalizzato
Poiché eseguiremo l'analisi durante la compilazione del progetto, l'assemblaggio e l'analisi dovrebbero essere un unico passaggio, quindi compila il campo Script personalizzato:
Vedremo più avanti i singoli passaggi. È importante che caricare l'analizzatore, assemblare il progetto, analizzarlo, produrre il report e formattarlo richieda solo undici righe di codice.
L'ultima cosa che dobbiamo fare è impostare le variabili d'ambiente, di cui ho delineato alcuni modi per migliorarne la leggibilità. Per fare ciò, andiamo avanti: Parametri -> Aggiungi nuovo parametro e aggiungi tre variabili:
Tutto quello che devi fare è premere il pulsante Correre nell'angolo in alto a destra. Mentre il progetto viene assemblato e analizzato, ti parlerò della sceneggiatura.
Script direttamente
Per prima cosa dobbiamo scaricare l'ultima distribuzione di PVS-Studio. Per questo utilizziamo il gestore pacchetti Chocolatey. Per coloro che vogliono saperne di più, c'è un corrispondente
choco install pvs-studio -y
Successivamente, avviamo l'utilità di monitoraggio della build del progetto CLMonitor.
%CLmon% monitor –-attach
Quindi costruiremo il progetto come variabile di ambiente MSB è il percorso della versione di MSBuild che devo creare
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Inseriamo il login e la chiave di licenza per PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Al termine della compilazione, eseguire nuovamente CLMonitor per generare file preelaborati e analisi statiche:
%CLmon% analyze -l "c:ptest.plog"
Quindi utilizzeremo un'altra utilità dalla nostra distribuzione. PlogConverter converte un report da un formato standard a un formato specifico di TeamCity. Grazie a questo potremo visualizzarlo direttamente nella finestra di build.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
L'ultimo passaggio consiste nel visualizzare il report formattato in stdout, dove verrà prelevato dal parser TeamCity.
type "C:tempptest.plog_TeamCity.txt"
Codice script completo:
choco install pvs-studio -y
%CLmon% monitor --attach
set platform=x64
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
%CLmon% analyze -l "c:ptest.plog"
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
type "C:tempptest.plog_TeamCity.txt"
Nel frattempo l'assemblaggio e l'analisi del progetto sono stati completati con successo, possiamo andare alla scheda Progetti e убедиться в этом.
Ora clicchiamo su Totale ispezioniper andare alla visualizzazione del report dell'analizzatore:
Gli avvisi sono raggruppati in base ai numeri delle regole diagnostiche. Per navigare nel codice è necessario fare clic sul numero di riga con l'avviso. Facendo clic sul punto interrogativo nell'angolo in alto a destra si aprirà una nuova scheda con la documentazione. Puoi anche navigare nel codice facendo clic sul numero di riga con l'avviso dell'analizzatore. Durante l'utilizzo è possibile la navigazione da un computer remoto SourceTreeRoot marcatore. Chiunque sia interessato a questa modalità di funzionamento dell'analizzatore può familiarizzare con la sezione corrispondente
Visualizzazione dei risultati dell'analizzatore
Ora che abbiamo finito di distribuire e configurare la build, diamo un'occhiata ad alcuni avvisi interessanti trovati nel progetto che stiamo esaminando.
Avvertimento N1
Object* CreateObjectFromJson(....)
{
Object* result = nullptr;
....
result = CreateObject(entry);
....
if (readContext.WasError())
{
throw std::runtime_error("Object has errors");
}
....
}
Object* CreateObject(const rct_object_entry& entry)
{
Object* result;
switch (entry.GetType())
{
case OBJECT_TYPE_RIDE:
result = new RideObject(entry);
break;
case OBJECT_TYPE_SMALL_SCENERY:
result = new SmallSceneryObject(entry);
break;
case OBJECT_TYPE_LARGE_SCENERY:
result = new LargeSceneryObject(entry);
break;
....
default:
throw std::runtime_error("Invalid object type");
}
return result;
}
L'analizzatore ha notato un errore che dopo aver allocato dinamicamente la memoria in CreateObject, quando si verifica un'eccezione, la memoria non viene cancellata e si verifica una perdita di memoria.
Avvertimento N2
static uint64_t window_cheats_page_enabled_widgets[] =
{
MAIN_CHEAT_ENABLED_WIDGETS |
(1ULL << WIDX_NO_MONEY) |
(1ULL << WIDX_ADD_SET_MONEY_GROUP) |
(1ULL << WIDX_MONEY_SPINNER) |
(1ULL << WIDX_MONEY_SPINNER_INCREMENT) |
(1ULL << WIDX_MONEY_SPINNER_DECREMENT) |
(1ULL << WIDX_ADD_MONEY) |
(1ULL << WIDX_SET_MONEY) |
(1ULL << WIDX_CLEAR_LOAN) |
(1ULL << WIDX_DATE_SET) |
(1ULL << WIDX_MONTH_BOX) | // <=
(1ULL << WIDX_MONTH_UP) |
(1ULL << WIDX_MONTH_DOWN) |
(1ULL << WIDX_YEAR_BOX) |
(1ULL << WIDX_YEAR_UP) |
(1ULL << WIDX_YEAR_DOWN) |
(1ULL << WIDX_DAY_BOX) |
(1ULL << WIDX_DAY_UP) |
(1ULL << WIDX_DAY_DOWN) |
(1ULL << WIDX_MONTH_BOX) | // <=
(1ULL << WIDX_DATE_GROUP) |
(1ULL << WIDX_DATE_RESET),
....
};
Poche persone oltre ad un analizzatore statico potrebbero superare questo test di attenzione. Questo esempio di copia-incolla è utile proprio per questo motivo.
Avvertenze N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Naturalmente, utilizzare una variabile con lo stesso nome nella classe base e nella discendente non è sempre un errore. Tuttavia, la stessa tecnologia di ereditarietà presuppone che tutti i campi della classe genitore siano presenti nella classe figlia. Dichiarando campi con lo stesso nome nell'erede creiamo confusione.
Avvertimento N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Diamo uno sguardo più da vicino. Espressione Direzioneimmagine/8 sarà falso se imageDirection è compreso tra -7 e 7. Seconda parte: (Direzioneimmagine / 8) != 3 assegni imageDirection per essere fuori intervallo: rispettivamente da -31 a -24 e da 24 a 31. Mi sembra piuttosto strano controllare in questo modo i numeri da includere in un determinato intervallo e, anche se non ci sono errori in questo pezzo di codice, consiglierei di riscrivere queste condizioni per essere più esplicite. Ciò renderebbe la vita molto più semplice per le persone che leggerebbero e manterrebbero questo codice.
Avvertimento N5
void process_mouse_over(....)
{
....
switch (window->widgets[widgetId].type)
{
case WWT_VIEWPORT:
ebx = 0;
edi = cursorId; // <=
// Window event WE_UNKNOWN_0E was called here,
// but no windows actually implemented a handler and
// it's not known what it was for
cursorId = edi; // <=
if ((ebx & 0xFF) != 0)
{
set_cursor(cursorId);
return;
}
break;
....
}
....
}
Questo frammento di codice è stato molto probabilmente ottenuto mediante decompilazione. Quindi, a giudicare dal commento lasciato, parte del codice non funzionante è stata rimossa. Rimangono però ancora un paio di operazioni cursoreId, anche questo non ha molto senso.
Avvertimento N6
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Add new player.
player = AddPlayer("", "");
if (player) // <=
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
}
newPlayers.push_back(player->Id); // <=
}
....
}
Questo codice è abbastanza facile da correggere; devi solo controllarlo una terza volta giocatore a un puntatore nullo o aggiungerlo al corpo dell'istruzione condizionale. Io suggerirei la seconda opzione:
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Add new player.
player = AddPlayer("", "");
if (player)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
newPlayers.push_back(player->Id);
}
}
....
}
Avvertimento N7
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
....
}
else
{
....
entry.name = (name == nullptr ? "" : json_string_value(name));
....
}
....
}
Puoi sbarazzarti di una riga di codice difficile da leggere in un colpo solo e risolvere il problema con il controllo nullptr. Suggerisco di modificare il codice come segue:
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
name = ""
....
}
else
{
....
entry.name = json_string_value(name);
....
}
....
}
Avvertimento N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Il codice sembra piuttosto strano. Mi sembra che ci sia stato un errore di battitura nella condizione o durante la riassegnazione della variabile ColumnHeaderPressedCurrentState senso falso.
conclusione
Come possiamo vedere, integrare l'analizzatore statico PVS-Studio nel tuo progetto TeamCity è abbastanza semplice. Per fare ciò è sufficiente scrivere solo un piccolo file di configurazione. Il controllo del codice consentirà di identificare i problemi subito dopo l'assemblaggio, il che aiuterà ad eliminarli quando la complessità e il costo delle modifiche sono ancora bassi.
Se vuoi condividere questo articolo con un pubblico di lingua inglese, utilizza il link di traduzione: Vladislav Stolyarov.
Fonte: habr.com