Jedan od najaktuelnijih scenarija za korišćenje PVS-Studio analizatora je njegova integracija sa CI sistemima. I iako se analiza PVS-Studio projekta iz gotovo svakog sistema kontinuirane integracije može ugraditi u samo nekoliko naredbi, mi nastavljamo da ovaj proces činimo još praktičnijim. PVS-Studio sada ima podršku za pretvaranje izlaza analizatora u format za TeamCity - TeamCity Inspections Type. Hajde da vidimo kako to radi.
Informacije o korištenom softveru
Informacije o projektu koji se proučava
Isprobajmo ovu funkcionalnost na praktičnom primjeru - analizirajmo OpenRCT2 projekat.
podešavanje
Da bih uštedio vrijeme, vjerovatno ću preskočiti proces instalacije i početi od trenutka kada na svom računaru bude pokrenut TeamCity server. Moramo otići na: localhost:{port specificiran tokom procesa instalacije} (u mom slučaju, localhost:9090) i unijeti autorizacijske podatke. Nakon ulaska dočekaće nas:
Kliknite na dugme Kreiraj projekat. Zatim odaberite Ručno i popunite polja.
Nakon pritiska na dugme stvoriti, dočeka nas prozor sa postavkama.
Hajde da kliknemo Kreirajte konfiguraciju izgradnje.
Popunite polja i kliknite stvoriti. Vidimo prozor u kojem se traži da odaberete sistem kontrole verzija. Pošto se izvori već nalaze lokalno, kliknite preskočiti.
Na kraju prelazimo na postavke projekta.
Dodajmo korake sastavljanja, da biste to uradili kliknite: Koraci izgradnje -> Dodaj korak izgradnje.
Ovdje biramo:
- Tip pokretača -> Komandna linija
- Pokreni -> Prilagođena skripta
Budući da ćemo analizu vršiti tokom kompilacije projekta, montaža i analiza treba da budu jedan korak, pa popunite polje Prilagođeni scenario:
Kasnije ćemo pogledati pojedinačne korake. Važno je da učitavanje analizatora, sklapanje projekta, njegova analiza, izlaz izvještaja i formatiranje traje samo jedanaest linija koda.
Poslednja stvar koju treba da uradimo je da postavimo varijable okruženja, koje sam naveo neke načine da poboljšamo njihovu čitljivost. Da bismo to uradili, idemo dalje: Parametri -> Dodaj novi parametar i dodajte tri varijable:
Sve što treba da uradite je da pritisnete dugme trčanje u gornjem desnom uglu. Dok se projekat sklapa i analizira, pričat ću vam o scenariju.
Direktno skripta
Prvo, moramo preuzeti najnoviju PVS-Studio distribuciju. Za to koristimo Chocolatey paket menadžer. Za one koji žele da saznaju više o ovome, postoji dopisnik
choco install pvs-studio -y
Zatim, pokrenimo uslužni program za praćenje izgradnje projekta CLMonitor.
%CLmon% monitor –-attach
Zatim ćemo izgraditi projekat kao varijablu okruženja MSB je put do verzije MSBuild-a koju trebam napraviti
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Unesite login i licencni ključ za PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Nakon što je gradnja završena, ponovo pokrenite CLMonitor da generišete prethodno obrađene datoteke i statičku analizu:
%CLmon% analyze -l "c:ptest.plog"
Zatim ćemo koristiti drugi uslužni program iz naše distribucije. PlogConverter konvertuje izveštaj iz standardnog formata u format specifičan za TeamCity. Zahvaljujući tome, moći ćemo je vidjeti direktno u prozoru za izgradnju.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Posljednji korak je da prikažete formatirani izvještaj stdout, gdje će ga pokupiti TeamCity parser.
type "C:tempptest.plog_TeamCity.txt"
Cijeli kod skripte:
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"
U međuvremenu je montaža i analiza projekta uspješno završena, možemo prijeći na karticu projekata i ubeditʹsâ v étom.
Sada kliknimo Pregledi Totalda pređete na pregled izvještaja analizatora:
Upozorenja su grupirana prema brojevima dijagnostičkih pravila. Da biste se kretali kroz kod, morate kliknuti na broj reda s upozorenjem. Klikom na upitnik u gornjem desnom kutu otvara se nova kartica sa dokumentacijom. Možete se kretati i kroz kod klikom na broj reda s upozorenjem analizatora. Navigacija sa udaljenog računara je moguća kada se koristi SourceTreeRoot marker. Svi koji su zainteresovani za ovaj način rada analizatora mogu se upoznati sa odgovarajućim odeljkom
Pregled rezultata analizatora
Sada kada smo završili sa implementacijom i konfiguracijom build-a, pogledajmo neka zanimljiva upozorenja koja se nalaze u projektu koji gledamo.
Upozorenje 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;
}
Analizator je uočio grešku koja nakon dinamičke dodjele memorije u CreateObject, kada se dogodi izuzetak, memorija se ne briše i dolazi do curenja memorije.
Upozorenje 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),
....
};
Nekoliko ljudi osim statičkog analizatora moglo bi proći ovaj test pažnje. Ovaj primjer kopiranja i lijepljenja dobar je upravo iz tog razloga.
Upozorenja N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Naravno, korištenje varijable s istim imenom u osnovnoj klasi i u potomku nije uvijek greška. Međutim, sama tehnologija nasljeđivanja pretpostavlja da su sva polja roditeljske klase prisutna u podređenoj klasi. Deklarisanjem polja sa istim imenom u nasledniku stvaramo zabunu.
Upozorenje N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Pogledajmo izbliza. Izraz imageDirection/8 biće lažno ako imageDirection je u rasponu od -7 do 7. Drugi dio: (imageDirection / 8) != 3 čekovi imageDirection za izvan opsega: od -31 do -24 i od 24 do 31, respektivno. Čini mi se prilično čudnim da na ovaj način provjeravam brojeve da li su uključeni u određeni raspon i, čak i ako nema greške u ovom dijelu koda, preporučio bih prepisivanje ovih uvjeta kako bi bili eksplicitniji. Ovo bi znatno olakšalo život ljudima koji bi čitali i održavali ovaj kod.
Upozorenje 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;
....
}
....
}
Ovaj fragment koda je najvjerovatnije dobijen dekompilacijom. Tada je, sudeći po ostavljenom komentaru, uklonjen dio nefunkcionalnog koda. Međutim, ostalo je još nekoliko operacija cursorId, što takođe nema mnogo smisla.
Upozorenje 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); // <=
}
....
}
Ovaj kod je prilično lako ispraviti; samo ga trebate provjeriti treći put igrač na nulti pokazivač ili ga dodajte u tijelo uslovne izjave. Predlažem drugu opciju:
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);
}
}
....
}
Upozorenje 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));
....
}
....
}
Možete se riješiti teško čitljive linije koda jednim potezom i riješiti problem provjerom nullptr. Predlažem da promijenite kod na sljedeći način:
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);
....
}
....
}
Upozorenje N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Kod izgleda prilično čudno. Čini mi se da je došlo do greške u kucanju ili u uslovu ili pri ponovnom dodeljivanju varijable ColumnHeaderPressedCurrentState vrijednosti lažan.
zaključak
Kao što vidimo, integracija statičkog analizatora PVS-Studio u vaš TeamCity projekat je prilično jednostavna. Da biste to učinili, dovoljno je napisati samo jednu malu konfiguracijsku datoteku. Provjera koda će vam omogućiti da identificirate probleme odmah nakon sastavljanja, što će vam pomoći da ih eliminišete kada su složenost i cijena promjena još uvijek niska.
Ako želite da podelite ovaj članak sa publikom koja govori engleski, koristite link za prevod: Vladislav Stolyarov.
izvor: www.habr.com