Eden najbolj aktualnih scenarijev za uporabo analizatorja PVS-Studio je njegova integracija s sistemi CI. In čeprav je analizo projekta PVS-Studio iz skoraj katerega koli neprekinjenega integracijskega sistema mogoče vgraditi v le nekaj ukazov, še naprej delamo ta proces še bolj priročen. PVS-Studio ima zdaj podporo za pretvorbo izhoda analizatorja v format za TeamCity - TeamCity Inspections Type. Poglejmo, kako deluje.
Informacije o uporabljeni programski opremi
Informacije o proučevanem projektu
Preizkusimo to funkcionalnost na praktičnem primeru – analizirajmo projekt OpenRCT2.
prilagoditev
Da bi prihranil čas, bom verjetno preskočil postopek namestitve in začel od trenutka, ko bo strežnik TeamCity deloval na mojem računalniku. Moramo iti na: localhost:{vrata, določena med postopkom namestitve} (v mojem primeru localhost:9090) in vnesti avtorizacijske podatke. Po vstopu nas bodo pozdravili:
Kliknite na gumb Ustvari projekt. Nato izberite Ročno in izpolnite polja.
Po pritisku na gumb ustvarjanje, nas pozdravi okno z nastavitvami.
Kliknimo Ustvari konfiguracijo gradnje.
Izpolnite polja in kliknite ustvarjanje. Vidimo okno, ki vas prosi, da izberete sistem za nadzor različic. Ker se viri že nahajajo lokalno, kliknite Preskoči.
Na koncu preidemo na nastavitve projekta.
Dodajmo korake sestavljanja, za to kliknite: Koraki gradnje -> Dodaj korak gradnje.
Tukaj izberemo:
- Vrsta tekača -> Ukazna vrstica
- Zaženi -> Skript po meri
Ker bomo analizo izvajali med sestavljanjem projekta, mora biti sestavljanje in analiza en korak, zato izpolnite polje Skript po meri:
Posamezne korake si bomo ogledali kasneje. Pomembno je, da nalaganje analizatorja, sestavljanje projekta, njegova analiza, izpis poročila in oblikovanje zahtevajo samo enajst vrstic kode.
Zadnja stvar, ki jo moramo narediti, je, da nastavimo spremenljivke okolja, za katere sem orisal nekaj načinov za izboljšanje njihove berljivosti. Če želite to narediti, pojdimo naprej: Parametri -> Dodaj nov parameter in dodajte tri spremenljivke:
Vse kar morate storiti je, da pritisnete gumb Run v zgornjem desnem kotu. Medtem ko se projekt sestavlja in analizira, vam bom povedal o scenariju.
Neposredno scenarij
Najprej moramo prenesti najnovejšo distribucijo PVS-Studio. Za to uporabljamo upravitelja paketov Chocolatey. Za tiste, ki želijo izvedeti več o tem, obstaja ustrezen
choco install pvs-studio -y
Nato zaženimo pripomoček za sledenje gradnji projekta CLMonitor.
%CLmon% monitor –-attach
Nato bomo zgradili projekt kot spremenljivko okolja MSB je pot do različice MSBuild, ki jo moram zgraditi
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Vnesemo prijavo in licenčni ključ za PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Ko je gradnja končana, znova zaženite CLMonitor, da ustvarite vnaprej obdelane datoteke in statično analizo:
%CLmon% analyze -l "c:ptest.plog"
Nato bomo uporabili drug pripomoček iz naše distribucije. PlogConverter pretvori poročilo iz standardne oblike v obliko, specifično za TeamCity. Zahvaljujoč temu si ga bomo lahko ogledali neposredno v oknu gradnje.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Zadnji korak je prikaz oblikovanega poročila v stdout, kjer ga bo pobral razčlenjevalnik TeamCity.
type "C:tempptest.plog_TeamCity.txt"
Celotna koda skripta:
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"
V tem času je bila montaža in analiza projekta uspešno zaključena, gremo lahko na zavihek projekti in prepričati se v tem.
Zdaj pa kliknimo na Pregledi Skupajza ogled poročila analizatorja:
Opozorila so razvrščena po številkah diagnostičnih pravil. Za krmarjenje po kodi morate klikniti številko vrstice z opozorilom. S klikom na vprašaj v zgornjem desnem kotu se vam odpre nov zavihek z dokumentacijo. Po kodi se lahko pomikate tudi s klikom na številko vrstice z opozorilom analizatorja. Pri uporabi je možna navigacija z oddaljenega računalnika SourceTreeRoot marker. Vsi, ki jih zanima ta način delovanja analizatorja, se lahko seznanijo z ustreznim razdelkom
Ogled rezultatov analizatorja
Zdaj, ko smo končali z uvajanjem in konfiguriranjem gradnje, si poglejmo nekaj zanimivih opozoril, ki jih najdemo v projektu, ki ga gledamo.
Opozorilo 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 opazil napako, ki je po dinamični dodelitvi pomnilnika v CreateObject, ko pride do izjeme, se pomnilnik ne izbriše in pride do puščanja pomnilnika.
Opozorilo 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),
....
};
Le malo ljudi razen statičnega analizatorja bi lahko opravilo ta test pozornosti. Ta primer copy-paste je dober ravno zaradi tega razloga.
Opozorila N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Seveda uporaba spremenljivke z istim imenom v osnovnem razredu in potomcu ni vedno napaka. Vendar sama tehnologija dedovanja predpostavlja, da so vsa polja nadrejenega razreda prisotna v podrejenem razredu. Z razglasitvijo polj z istim imenom v dediču ustvarimo zmedo.
Opozorilo N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Pa poglejmo pobliže. Izraz imageDirection/8 bo napačno, če imageDirection je v območju od -7 do 7. Drugi del: (imageDirection / 8) != 3 čeki imageDirection za izven območja: od -31 do -24 oziroma od 24 do 31. Precej nenavadno se mi zdi preverjanje števil za vključitev v določen obseg na ta način in, tudi če v tem delu kode ni napake, priporočam, da te pogoje ponovno napišete, da bodo bolj jasni. To bi olajšalo življenje ljudem, ki bi brali in vzdrževali to kodo.
Opozorilo 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;
....
}
....
}
Ta fragment kode je bil najverjetneje pridobljen z dekompilacijo. Nato je bil, sodeč po komentarju, odstranjen del nedelujoče kode. Ostalo pa je še par operacij cursorId, kar tudi nima velikega smisla.
Opozorilo 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); // <=
}
....
}
To kodo je zelo enostavno popraviti, samo še tretjič jo morate preveriti predvajalnik na ničelni kazalec ali ga dodajte v telo pogojnega stavka. Jaz bi predlagal drugo možnost:
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);
}
}
....
}
Opozorilo 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));
....
}
....
}
Z enim zamahom se lahko znebite težko berljive vrstice kode in rešite težavo s preverjanjem nullptr. Predlagam, da kodo spremenite na naslednji 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);
....
}
....
}
Opozorilo N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Koda izgleda precej čudno. Zdi se mi, da je prišlo do tipkarske napake ali pri pogoju ali pri ponovnem dodeljevanju spremenljivke ColumnHeaderPressedCurrentState pomene false.
Izhod
Kot lahko vidimo, je integracija statičnega analizatorja PVS-Studio v vaš projekt TeamCity precej preprosta. Če želite to narediti, je dovolj, da napišete samo eno majhno konfiguracijsko datoteko. Preverjanje kode vam bo omogočilo, da prepoznate težave takoj po sestavljanju, kar jih bo pomagalo odpraviti, ko bodo kompleksnost in stroški sprememb še nizki.
Če želite ta članek deliti z angleško govorečim občinstvom, uporabite povezavo za prevod: Vladislav Stolyarov.
Vir: www.habr.com