Jedan od najnovijih scenarija za korištenje PVS-Studio analizatora je njegova integracija sa CI sustavima. I premda se analiza PVS-Studio projekta iz gotovo bilo kojeg kontinuiranog integracijskog sustava može ugraditi u samo nekoliko naredbi, nastavljamo ovaj proces činiti još praktičnijim. PVS-Studio sada ima podršku za pretvaranje izlaza analizatora u format za TeamCity - TeamCity Inspections Type. Pogledajmo kako radi.
Informacije o korištenom softveru
Informacije o projektu koji se proučava
Isprobajmo ovu funkcionalnost na praktičnom primjeru – analizirajmo projekt OpenRCT2.
podešavanje
Kako bih uštedio vrijeme, vjerojatno ću preskočiti proces instalacije i započeti od trenutka kada imam pokrenut TeamCity server na mom računalu. Moramo otići na: localhost:{port naveden tijekom postupka instalacije} (u mom slučaju, localhost:9090) i unijeti podatke za autorizaciju. Nakon ulaska pozdravit će nas:
Pritisnite gumb Kreiraj projekt. Zatim odaberite Ručno i ispunite polja.
Nakon pritiska na tipku stvoriti, dočekuje nas prozor s postavkama.
Kliknimo Stvorite konfiguraciju gradnje.
Ispunite polja i kliknite stvoriti. Vidimo prozor koji traži da odaberete sustav kontrole verzija. Budući da se izvori već nalaze lokalno, kliknite Preskočiti.
Konačno, prelazimo na postavke projekta.
Dodajmo korake sastavljanja, za ovo kliknite: Koraci izrade -> Dodaj korak izrade.
Ovdje biramo:
- Vrsta trkača -> Naredbeni redak
- Pokreni -> Prilagođena skripta
Budući da ćemo tijekom kompilacije projekta izvršiti analizu, montaža i analiza trebaju biti jedan korak, stoga ispunite polje Prilagođena skripta:
Kasnije ćemo pogledati pojedinačne korake. Važno je da učitavanje analizatora, sastavljanje projekta, njegova analiza, ispisivanje izvješća i formatiranje zahtijeva samo jedanaest redaka koda.
Zadnje što trebamo učiniti je postaviti varijable okruženja, za koje sam naveo neke načine za poboljšanje njihove čitljivosti. Da bismo to učinili, idemo dalje: Parametri -> Dodaj novi parametar i dodajte tri varijable:
Sve što trebate učiniti je pritisnuti gumb trčanje u gornjem desnom kutu. Dok se projekt sastavlja i analizira, pričat ću vam o scenariju.
Izravno scenarij
Prvo moramo preuzeti najnoviju PVS-Studio distribuciju. Za to koristimo upravitelj paketa Chocolatey. Za one koji žele znati više o ovome, postoji odgovarajući
choco install pvs-studio -y
Zatim, pokrenimo uslužni program za praćenje izgradnje projekta CLMonitor.
%CLmon% monitor –-attach
Zatim ćemo izgraditi projekt kao varijablu okruženja MSB je put do verzije MSBuild-a koju trebam izgraditi
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Unesite prijavu i licencni ključ za PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Nakon završetka izgradnje, ponovno pokrenite CLMonitor za generiranje prethodno obrađenih datoteka i statičku analizu:
%CLmon% analyze -l "c:ptest.plog"
Zatim ćemo koristiti drugi pomoćni program iz naše distribucije. PlogConverter pretvara izvješće iz standardnog formata u format specifičan za TeamCity. Zahvaljujući tome, moći ćemo ga vidjeti izravno u prozoru za izradu.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Zadnji korak je prikazati formatirano izvješće stdout, gdje će ga pokupiti parser TeamCity.
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, montaža i analiza projekta je uspješno završena, možemo prijeći na karticu Projekti i uvjeriti se u ovome.
Sada kliknimo na Inspekcije Ukupnoza odlazak na pregled izvješća analizatora:
Upozorenja su grupirana prema brojevima dijagnostičkih pravila. Za navigaciju kodom morate kliknuti na broj retka s upozorenjem. Klikom na upitnik u gornjem desnom kutu otvorit će vam se novi tab s dokumentacijom. Također se možete kretati kroz kod klikom na broj retka s upozorenjem analizatora. Prilikom korištenja moguća je navigacija s udaljenog računala SourceTreeRoot marker. Svatko tko je zainteresiran za ovaj način rada analizatora može se upoznati s odgovarajućim odjeljkom
Pregled rezultata analizatora
Sada kada smo završili s implementacijom i konfiguracijom izgradnje, 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 pogrešku koja je nakon dinamičke dodjele memorije u CreateObject, kada se dogodi iznimka, 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),
....
};
Malo je ljudi osim statičkog analizatora moglo proći ovaj test pažnje. Ovaj copy-paste primjer je dobar 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 iu potomku nije uvijek pogreška. Međutim, sama tehnologija nasljeđivanja pretpostavlja da su sva polja roditeljske klase prisutna u podređenoj klasi. Proglašavanjem istoimenih polja u nasljedniku unosimo zabunu.
Upozorenje N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Pogledajmo pobliže. Izraz imageDirection/8 bit će lažno ako imageDirection je u rasponu od -7 do 7. Drugi dio: (imageDirection / 8) != 3 provjere imageDirection jer su izvan raspona: od -31 do -24 odnosno od 24 do 31. Čini mi se prilično čudnim provjeravati brojeve za uključivanje u određeni raspon na ovaj način i, čak i ako nema pogreške u ovom dijelu koda, preporučio bih da se ti uvjeti ponovno napišu kako bi bili eksplicitniji. To 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 najvjerojatnije je dobiven dekompilacijom. Tada je, sudeći po ostavljenom komentaru, uklonjen dio neradnog koda. No, ostalo je još par operacija cursorId, što također nema previše 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 uvjetne naredbe. Ja bih predložio 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 čitljivog retka koda jednim potezom i riješiti problem s provjerom nullptr. Predlažem promjenu koda 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 tipfelera ili u uvjetu ili kod ponovnog dodjeljivanja varijable ColumnHeaderPressedCurrentState smisao lažan.
Izlaz
Kao što vidimo, integracija PVS-Studio statičkog analizatora u vaš TeamCity projekt prilično je jednostavna. Da biste to učinili, dovoljno je napisati samo jednu malu konfiguracijsku datoteku. Provjera koda omogućit će vam da identificirate probleme odmah nakon sklapanja, što će pomoći u njihovom otklanjanju kada su složenost i cijena promjena još uvijek niski.
Ako želite podijeliti ovaj članak s publikom koja govori engleski, upotrijebite vezu za prijevod: Vladislav Stolyarov.
Izvor: www.habr.com