Üks kõige kaasaegsemaid stsenaariume PVS-Studio analüsaatori kasutamiseks on selle integreerimine CI-süsteemidega. Ja kuigi peaaegu iga pideva integratsioonisüsteemi PVS-Studio projekti analüüsi saab sisse ehitada vaid mõne käsuga, muudame selle protsessi jätkuvalt veelgi mugavamaks. PVS-Studio toetab nüüd analüsaatori väljundi teisendamist TeamCity - TeamCity Inspections Type -vormingusse. Vaatame, kuidas see toimib.
Teave kasutatud tarkvara kohta
Info uuritava projekti kohta
Proovime seda funktsionaalsust praktilisel näitel – analüüsime OpenRCT2 projekti.
reguleerimine
Aja säästmiseks jätan tõenäoliselt installimise vahele ja alustan hetkest, mil minu arvutis töötab TeamCity server. Peame minema aadressile localhost:{installiprotsessi käigus määratud port} (minu puhul localhost:9090) ja sisestama autoriseerimisandmed. Pärast sisenemist tervitavad meid:
Klõpsake nuppu Loo projekt. Järgmisena valige Käsitsi ja täitke väljad.
Pärast nupu vajutamist Looma, tervitab meid seadetega aken.
Klõpsame Loo ehituse konfiguratsioon.
Täitke väljad ja klõpsake nuppu Looma. Näeme akent, mis palub teil valida versioonihaldussüsteem. Kuna allikad asuvad juba kohapeal, klõpsake nuppu Jäta vahele.
Lõpuks liigume edasi projekti sätete juurde.
Lisame kokkupaneku etapid, selleks klõpsake: Järje etapid -> Lisa ehitusetapp.
Siin valime:
- Jooksja tüüp -> käsurida
- Käivita -> Kohandatud skript
Kuna analüüsi teostame projekti koostamise ajal, peaks kokkupanek ja analüüs olema üks samm, seega täitke väli Kohandatud skript:
Üksikuid samme vaatame hiljem. Oluline on, et analüsaatori laadimine, projekti kokkupanemine, analüüsimine, aruande väljastamine ja vormindamine võtavad vaid üksteist koodirida.
Viimase asjana peame määrama keskkonnamuutujad, mille loetavuse parandamiseks olen välja toonud mõned viisid. Selleks liigume edasi: Parameetrid -> Lisa uus parameeter ja lisage kolm muutujat:
Kõik, mida pead tegema, on vajutada nuppu jooks paremas ülanurgas. Projekti kokkupanemise ja analüüsimise ajal räägin teile stsenaariumist.
Otse skript
Esiteks peame alla laadima uusima PVS-Studio distributsiooni. Selleks kasutame Chocolatey paketihaldurit. Neile, kes soovivad selle kohta rohkem teada saada, on vastav
choco install pvs-studio -y
Järgmisena käivitame projekti CLMonitor ehituse jälgimise utiliidi.
%CLmon% monitor –-attach
Seejärel koostame projekti keskkonnamuutujana MSB on tee MSBuildi versioonini, mida ma pean ehitama
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Sisestame PVS-Studio sisselogimis- ja litsentsivõtme:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Kui ehitamine on lõppenud, käivitage eeltöödeldud failide ja staatilise analüüsi loomiseks uuesti CLMonitor:
%CLmon% analyze -l "c:ptest.plog"
Seejärel kasutame teist distributsiooni utiliiti. PlogConverter teisendab aruande standardvormingust TeamCity-spetsiifiliseks vorminguks. Tänu sellele saame seda vaadata otse ehitusaknas.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Viimane samm on vormindatud aruande kuvamine stdout, kust TeamCity parser selle üles võtab.
type "C:tempptest.plog_TeamCity.txt"
Täielik skripti kood:
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"
Vahepeal on projekti kokkupanek ja analüüs edukalt lõpetatud, saame vahekaardile minna Projektid ja veendu selles.
Nüüd klõpsame edasi Ülevaatused Kokkuanalüsaatori aruande vaatamiseks:
Hoiatused on rühmitatud diagnostikareeglite numbrite järgi. Koodis navigeerimiseks peate klõpsama hoiatusega rea numbril. Klõpsates paremas ülanurgas oleval küsimärgil, avaneb uus dokumentatsiooniga vahekaart. Samuti saate koodis navigeerida, klõpsates analüsaatori hoiatusega rea numbril. Kasutamisel on võimalik navigeerida kaugarvutist SourceTreeRoot marker. Kõik, kes on huvitatud sellest analüsaatori töörežiimist, saavad tutvuda vastava jaotisega
Analüsaatori tulemuste vaatamine
Nüüd, kui oleme järgu juurutamise ja konfigureerimise lõpetanud, vaatame mõningaid huvitavaid hoiatusi, mis leiti vaadeldavast projektist.
Hoiatus 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;
}
Analüsaator märkas viga, mis tekkis pärast mälu dünaamilist eraldamist Loo objekt, kui erand ilmneb, mälu ei tühjendata ja tekib mäluleke.
Hoiatus 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),
....
};
Vähesed inimesed peale staatilise analüsaatori suudavad selle tähelepanelikkuse testi läbida. See copy-paste näide on just sel põhjusel hea.
Hoiatused N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Muidugi ei ole sama nimega muutuja kasutamine põhiklassis ja järeltulijas alati viga. Pärimistehnoloogia ise aga eeldab, et alamklassis on olemas kõik vanemklassi väljad. Deklareerides pärijas samanimelised väljad, tekitame segaduse.
Hoiatus N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Vaatame lähemalt. Väljendus imageDirection/8 on vale, kui imageDirection on vahemikus -7 kuni 7. Teine osa: (imageDirection / 8) != 3 kontrollid imageDirection väljaspool vahemikku jäämise eest: vastavalt -31 kuni -24 ja 24 kuni 31. Mulle tundub üsna kummaline kontrollida sel viisil numbreid teatud vahemikku ja isegi kui selles koodiosas pole viga, soovitaksin need tingimused selgemaks muuta. See teeks nende inimeste elu palju lihtsamaks, kes seda koodi loevad ja hooldavad.
Hoiatus 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;
....
}
....
}
See koodifragment saadi suure tõenäosusega dekompileerimise teel. Seejärel eemaldati jäetud kommentaari järgi otsustades osa mittetöötavast koodist. Paar operatsiooni on aga veel jäänud kursori ID, millel pole samuti erilist mõtet.
Hoiatus 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); // <=
}
....
}
Seda koodi on üsna lihtne parandada; peate seda lihtsalt kolmandat korda kontrollima mängija nullkursorile või lisage see tingimuslause kehasse. Mina pakuks teist varianti:
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);
}
}
....
}
Hoiatus 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));
....
}
....
}
Saate ühe hoobiga lahti saada raskesti loetavast koodireast ja lahendada probleemi nullptr. Soovitan koodi muuta järgmiselt:
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);
....
}
....
}
Hoiatus N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Kood näeb üsna kummaline välja. Mulle tundub, et kas tingimuses või muutuja ümber määramisel oli kirjaviga ColumnHeaderPressedCurrentState tähendused vale.
Väljund
Nagu näeme, on PVS-Studio staatilise analüsaatori integreerimine oma TeamCity projekti üsna lihtne. Selleks piisab vaid ühe väikese konfiguratsioonifaili kirjutamisest. Koodi kontrollimine võimaldab tuvastada probleemid kohe pärast kokkupanekut, mis aitab need kõrvaldada, kui muudatuste keerukus ja maksumus on endiselt madal.
Kui soovite seda artiklit inglise keelt kõneleva publikuga jagada, kasutage tõlkelinki: Vladislav Stolyarov.
Allikas: www.habr.com