
Üks PVS-Studio analüsaatorite kõige asjakohasemaid kasutusstsenaariume on selle integreerimine CI süsteemidega. Ja kuigi PVS-Studio projektianalüüsi saab praktiliselt iga continuous integration süsteemi alla integreerida vaid mõne käsuga, töötame me selle protsessi veelgi mugavdamise nimel. PVS-Studiosse on lisatud tugi analüsaatori väljundi ümberkujundamiseks TeamCity formaati — TeamCity Inspections Type. Vaatame, kuidas see töötab.
Kasutatava tarkvara teave
— staatiline analüsaator C, C++, C# ja Java koodi jaoks, mis on mõeldud erinevate vigade leidmise ja parandamise ülesande lihtsustamiseks. Analüsaatorit saab kasutada Windowsis, Linuxis ja macOS-is. Käesolevas artiklis kasutame me aktiivselt mitte ainult analüsaatorit, vaid ka mõningaid utiliite selle distributsioonist.
— on jälgimisteenuse server, mis jälgib kompilaatorite käivitusi. Seda tuleb käivitada otse enne oma projekti kokkupanekut. Jälgimismooduliga server jääb kinni kõikide toetatud kompilaatorite käivitustele. Tasub märkida, et seda utiliiti saab kasutada ainult C/C++ projektide analüüsimiseks.
– tööriist analüüsiharu aruande konverteerimiseks erinevatesse formaatidesse.
Teave uuritava projekti kohta
Katsetame seda funktsionaalsust praktilise näitega – analüüsime projekti OpenRCT2.
— avatud rakendus mängust RollerCoaster Tycoon 2 (RCT2), mis laiendab seda uute funktsioonidega ja parandab vigu. Mängu sisu keskendub lõbustuspargi ehitamisele ja haldamisele, kus asuvad atraktsioonid, kauplused ja rajatised. Mängija peab püüdma teenida tulu ja säilitama pargi head mainet, hoides samal ajal külalised õnnelikena. OpenRCT2 võimaldab mängida nii stsenaariumide kui ka liivakasti režiimis. Stsenaariumid nõuavad, et mängija täidaks teatud ülesande kindlaksmääratud ajavahemikus, samas kui liivakast võimaldab mängijal ehitada paindlikuma pargi ilma piirangute või rahaliste takistusteta.
Seadistamine
Aja kokkuhoiu huvides jäta vahele paigaldusprotsess ja alustan hetkest, mil mul on TeamCity server arvutis käivitunud. Peame minema: localhost:{paigaldusprotsessis määratud port}(minu puhul, localhost:9090) ja sisestama autentimisandmed. Pärast sisselogimist näeme:

Klõpsame nuppu Create Project. Seejärel valime Manually ja täidame väljad.

Pärast nupu vajutamist Loo, leiame end seadetega aknast.

Klõpsame Create build configuration.

Täidame väljad ja vajutame Loo. Näeme aken, kus pakutakse versioonikontrollisüsteemi valikut. Kuna lähtekoodid on juba kohalikult saadaval, vajutame Skip.

Lõpuks liigume projekti seadete juurde.

Lisame ehitusetapid, selleks vajutame: Build steps -> Add build step.

Siit valime:
- Runner type -> Command Line
- Run -> Custom Script
Kuna me viime analüüsi läbi projekti kompileerimise ajal, peavad ehitamine ja analüüs olema üks samm, seega täidame välja Custom Script:

Peatume eraldi sammudel hiljem. Oluline on, et analüsaatori laadimine, projekti ehitamine, selle analüüsimine, aruande genereerimine ja vormindamine võtaks kokku vaid üheteist rida koodi.
Viimane asi, mida peame tegema, on keskkonnamuutujate seadistamine, mille abil olen mõnel teel lugemise hõlbustamiseks eraldi määranud. Selleks liigume: Parameters -> Add new parameter ja lisame kolm muutujat:

Jääb vaid vajutada nuppu Run paremas ülanurgas. Kui projekt on ehitamisel ja analüüsitakse, räägin teile skriptist.
Tõenäoliselt skript
Alustamiseks peame alla laadima värske PVS-Studio distributsiooni. Selleks kasutame pakihaldurit Chocoatey. Rohkem teavet soovivatele on olemas vastav :
choco install pvs-studio -ySeejärel käivitame projekti ehituse jälgimise utiliidi CLMonitor.
%CLmon% monitor --attachSeejärel teeme projekti ehituse, kus keskkonnaparameeter MSB esindab teed vajalikku MSBuild versioonile minu ehitamiseks
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortableSisestame PVS-Studio kasutajanime ja litsentsivõtme:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%Ehituse lõppedes käivitame uuesti CLMonitor, et genereerida eelkoostatavad failid ja staatiline analüüs:
%CLmon% analyze -l "c:ptest.plog"Peale seda kasutame veel ühte meie distributsioonis olevat utiliiti. PlogConverter teisendab aruande standardvormist TeamCity spetsiifiliseks vorminguks. Nii saame seda vaadata otse ehitusaknas.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"Viimase sammuna väljastame vormindatud aruande stdout, kuhu TeamCity parser selle haarab.
type "C:tempptest.plog_TeamCity.txt"Täielik skriptikood:
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 koostamine ja analüüsimine edukalt lõpetatud, saame liikuda vahekaardile Projektid ja veenduda selles.

Nüüd klikkige Kokku inspektsioonid, et minna analüüsaraporti vaatamisele:

Hoiatused on grupeeritud diagnoosimisreeglite numbrite järgi. Koodis navigeerimiseks tuleb klikkida hoiatusse seotud real oleva numbri peale. Küsimärgi puudutamine paremas ülanurgas avab uue vahekaart dokumentatsiooniga. Samuti on võimalik navigeerida koodis, klikkides analüsaatori hoiatusse seotud real oleva numbri peale. Kaugjuhtimise režiimi kasutamine on võimalik SourceTreeRoot markerit. Need, keda huvitab antud analüsaatori töörežiim, võivad tutvuda vastava jaotisega .
Analüsaatori töö tulemuste vaatamine
Kui oleme lõpetanud kogumise juurutamise ja seadistamise, vaatame mõningaid huvitavaid hoiatusi, mis uuritavas projektis avastati.
Hoiatus N1
[CWE-401] Erand visati, ilma et 'result' näidik vabastataks. Mälu lekke võimalus on olemas. libopenrct2 ObjectFactory.cpp 443
Object* CreateObjectFromJson(....)
{
Object* result = nullptr;
....
result = CreateObject(entry);
....
if (readContext.WasError())
{
throw std::runtime_error("Objektil on vead");
}
....
}
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("Kehtetu objekti tüüp");
}
return result;
}Analüüsimootor märkas viga, et pärast dünaamilist mälu eraldamist CreateObject, erandi korral mälu ei puhastata, mis põhjustab mälu lekke.
Hoiatus N2
On identsed alamavaldised '(1ULL << WIDX_MONTH_BOX)' vasakul ja paremal '|' operaatorist. libopenrct2ui Cheats.cpp 487
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äga vähesed, välja arvatud staatiline analüsaator, suudaksid selle tähelepanelikkuse testi läbida. See copy-paste näide on hea just sellega.
Hoiatused N3
On kummaline, et tuletatud klassi 'RCT12BannerElement' 'flags' väljad ületavad baasklassi 'RCT12TileElementBase' väljad. Kontrollige ridu: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};Tõepoolest, muutuja kasutamine, millel on sama nimi põhiklassis ja pärandis, ei ole alati viga. Kuid päranditehnoloogia eeldab, et kõik vanema klassi väljad on olemas lapsklassides. Kui meil on pärandis väljad sama nimega, tekitame me segadust.
Hoiatus N4
On kummaline, et näitaja 'imageDirection / 8' tulemus on osa tingimusest. Võib-olla oleks see väljend pidanud olema võrreldud millegagi muuga. libopenrct2 ObservationTower.cpp 38
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}Vaatame seda lähemalt. Väljend imageDirection / 8 on vale juhul, kui imageDirection on vahemikus -7 kuni 7. Teine osa: (imageDirection / 8) != 3 kontrollib imageDirection kas see on vahemikust väljas: vahemikus -31 kuni -24 ja vahemikus 24 kuni 31. Ma leian kummalisena kontrollida numbreid niimoodi kindlasse vahemikku kuuluvateks ja isegi kui sellel koodil pole viga, soovitaksin ma neid tingimusi kirjutada selgemaks. See lihtsustaks oluliselt nende inimeste elu, kes seda koodi loevad ja toetavad.
Hoiatus N5
Seda tüüpi ülesannete kummaline järjestus: A = B; B = A;. Kontrollige ridu: 1115, 1118. libopenrct2ui MouseInput.cpp 1118
void process_mouse_over(....)
{
....
switch (window->widgets[widgetId].type)
{
case WWT_VIEWPORT:
ebx = 0;
edi = cursorId; // <=
// Akna sündmus WE_UNKNOWN_0E kutsuti siin,
// kuid ükski akendest ei rakendanud tegelikult töötlejat ja
// ei ole teada, milleks see mõeldud oli
cursorId = edi; // <=
if ((ebx & 0xFF) != 0)
{
set_cursor(cursorId);
return;
}
break;
....
}
....
}See koodilõik on tõenäoliselt saadud dekompileerimise teel. Siis, jääks koodist maha jättes tehtud kommentaari, on eemaldatud osa mittetöötavast koodist. Siiski on jäänud paar operatsiooni cursorId, mis samuti ei kanna erilist tähendust.
Hoiatamine N6
[CWE-476] 'player' pointerit kasutati enne seda ohtlikult, kui see oli kontrollitud nullptr vastu. Kontrollige ridu: 2085, 2094. libopenrct2 Network.cpp 2094
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Lisa uus mängija.
player = AddPlayer("", "");
if (player) // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
}
newPlayers.push_back(player->Id); // <=
}
....
}Seda koodi on suhteliselt lihtne parandada, tuleb lihtsalt teha see kolmas kord kontroll player null viitena, või lisada see tingimuslause keha. Soovitan teist varianti:
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
\/\/ Lisa uus mängija.
player = AddPlayer("", "");
if (player)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
newPlayers.push_back(player->Id);
}
}
....
}Hoiatus N7
[CWE-570] Väljend 'name == nullptr' on alati vale. libopenrct2 ServerList.cpp 102
std::optional ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
....
}
else
{
....
entry.name = (name == nullptr ? "" : json_string_value(name));
....
}
....
}Ühega on võimalik vabaneda raskesti loetavast koodirivist ja lahendada probleem kontrolliga nullptr. Soovitan muuta koodi järgmiselt:
std::optional ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
name = ""
....
}
else
{
....
entry.name = json_string_value(name);
....
}
....
}Hoiatus N8
[CWE-1164] Muutujale 'ColumnHeaderPressedCurrentState' anti sama väärtus. libopenrct2ui CustomListView.cpp 510
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}Kood näeb üsna kummaline välja. Tundub, et kas lahenduses või muutuja uuesti määramisel oli viga. ColumnHeaderPressedCurrentState väärtusi false.
Kokkuvõte
Nagu näeme, on PVS-Studio staatilise analüsaatori integreerimine oma projekti TeamCitys üsna lihtne. Selleks piisab vaid ühe väikese konfiguratsioonifaili loomise. Koodi kontrollimine võimaldab probleemid avastada kohe pärast ehitamist, mis aitab neid parandada, kui nende keerukus ja maksumus on veel madalad.
Kui soovite seda artiklit ingliskeelse publikuga jagada, kasutage palun tõlke linki: Vladislav Stolyarov. .
Allikas: habr.com
