Jedným z najaktuálnejších scenárov používania analyzátora PVS-Studio je jeho integrácia so systémami CI. A hoci analýza projektu PVS-Studio z takmer akéhokoľvek systému kontinuálnej integrácie môže byť zabudovaná len do niekoľkých príkazov, naďalej robíme tento proces ešte pohodlnejším. PVS-Studio teraz podporuje konverziu výstupu analyzátora do formátu pre TeamCity - TeamCity Inspections Type. Pozrime sa, ako to funguje.
Informácie o použitom softvéri
Informácie o skúmanom projekte
Skúsme si túto funkcionalitu vyskúšať na praktickom príklade – analyzujme projekt OpenRCT2.
nastavenie
Aby som ušetril čas, pravdepodobne preskočím inštalačný proces a začnem od chvíle, keď mám na počítači spustený server TeamCity. Musíme prejsť na: localhost:{port zadaný počas procesu inštalácie} (v mojom prípade localhost:9090) a zadať autorizačné údaje. Po vstupe nás privíta:
Kliknite na tlačidlo Vytvoriť projekt. Ďalej vyberte možnosť Manuálne a vyplňte polia.
Po stlačení tlačidla vytvoriť, privíta nás okno s nastaveniami.
Poďme kliknúť Vytvorte konfiguráciu zostavy.
Vyplňte polia a kliknite vytvoriť. Vidíme okno s výzvou na výber systému správy verzií. Keďže zdroje sú už umiestnené lokálne, kliknite Preskočiť.
Nakoniec prejdeme k nastaveniam projektu.
Pridajme montážne kroky, ak to chcete urobiť, kliknite na: Kroky zostavenia -> Pridať krok zostavenia.
Tu vyberáme:
- Typ bežca -> Príkazový riadok
- Spustiť -> Vlastný skript
Keďže analýzu vykonáme pri zostavovaní projektu, montáž a analýza by mali byť jedným krokom, preto pole vyplňte Vlastný skript:
Na jednotlivé kroky sa pozrieme neskôr. Je dôležité, aby načítanie analyzátora, zostavenie projektu, jeho analýza, výstup správy a jej formátovanie zabralo iba jedenásť riadkov kódu.
Posledná vec, ktorú musíme urobiť, je nastaviť premenné prostredia, ktorým som načrtol niekoľko spôsobov, ako zlepšiť ich čitateľnosť. Ak to chcete urobiť, poďme ďalej: Parametre -> Pridať nový parameter a pridajte tri premenné:
Jediné, čo musíte urobiť, je stlačiť tlačidlo beh v pravom hornom rohu. Zatiaľ čo sa projekt zostavuje a analyzuje, poviem vám o scenári.
Priamo skript
Najprv si musíme stiahnuť najnovšiu distribúciu PVS-Studio. Na tento účel používame správcu balíkov Chocolatey. Pre tých, ktorí sa chcú o tom dozvedieť viac, je tu zodpovedajúci
choco install pvs-studio -y
Ďalej spustíme nástroj na sledovanie zostavenia projektu CLMonitor.
%CLmon% monitor –-attach
Potom vytvoríme projekt ako premennú prostredia MSB je cesta k verzii MSBuild, ktorú potrebujem zostaviť
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Zadáme prihlasovacie meno a licenčný kľúč pre PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Po dokončení zostavovania znova spustite CLMonitor, aby sa vygenerovali predspracované súbory a statická analýza:
%CLmon% analyze -l "c:ptest.plog"
Potom použijeme inú utilitu z našej distribúcie. PlogConverter konvertuje správu zo štandardného formátu do formátu špecifického pre TeamCity. Vďaka tomu si ho budeme môcť pozrieť priamo v okne zostavenia.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Posledným krokom je zobrazenie naformátovanej zostavy stdout, kde ho prevezme analyzátor TeamCity.
type "C:tempptest.plog_TeamCity.txt"
Úplný kód skriptu:
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"
Medzitým je montáž a analýza projektu úspešne dokončená, môžeme prejsť na kartu projekty a presvedčte sa o tom.
Teraz kliknime ďalej Kontroly Celkomprejdite na zobrazenie správy analyzátora:
Varovania sú zoskupené podľa čísel diagnostických pravidiel. Ak chcete prechádzať kódom, musíte kliknúť na číslo riadku s upozornením. Kliknutím na otáznik v pravom hornom rohu sa vám otvorí nová karta s dokumentáciou. V kóde sa môžete pohybovať aj kliknutím na číslo riadku s upozornením analyzátora. Pri používaní je možná navigácia zo vzdialeného počítača SourceTreeRoot marker. Každý, kto má záujem o tento režim činnosti analyzátora, sa môže zoznámiť s príslušnou časťou
Zobrazenie výsledkov analyzátora
Teraz, keď sme dokončili nasadenie a konfiguráciu zostavy, pozrime sa na niekoľko zaujímavých upozornení, ktoré sa nachádzajú v projekte, na ktorý sa pozeráme.
Upozornenie 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;
}
Analyzátor si všimol chybu, že po dynamickom pridelení pamäte v CreateObject, keď sa vyskytne výnimka, pamäť sa nevymaže a dôjde k úniku pamäte.
Upozornenie 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),
....
};
Len málo ľudí okrem statického analyzátora by mohlo prejsť týmto testom pozornosti. Tento príklad kopírovania a vkladania je dobrý práve z tohto dôvodu.
Varovania N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Samozrejme, použitie premennej s rovnakým názvom v základnej triede a v potomkovi nie je vždy chyba. Samotná technológia dedenia však predpokladá, že všetky polia nadradenej triedy sú prítomné v podradenej triede. Deklarovaním polí s rovnakým názvom v dedičovi vytvárame zmätok.
Upozornenie N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Poďme sa na to pozrieť bližšie. Výraz imageDirection/8 bude falošné, ak imageDirection je v rozsahu od -7 do 7. Druhá časť: (imageDirection / 8) != 3 šeky imageDirection pre mimo rozsahu: od -31 do -24 a od 24 do 31, v tomto poradí. Zdá sa mi dosť zvláštne kontrolovať čísla na zahrnutie do určitého rozsahu týmto spôsobom a aj keď v tomto kúsku kódu nie je žiadna chyba, odporučil by som prepísať tieto podmienky, aby boli jasnejšie. To by značne uľahčilo život ľuďom, ktorí by tento kód čítali a udržiavali.
Upozornenie 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;
....
}
....
}
Tento fragment kódu bol s najväčšou pravdepodobnosťou získaný dekompiláciou. Potom, súdiac podľa zanechaného komentára, bola časť nefunkčného kódu odstránená. Zostáva však ešte niekoľko operácií kurzorId, čo tiež nedáva veľký zmysel.
Upozornenie 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); // <=
}
....
}
Tento kód sa dá pomerne ľahko opraviť, stačí ho skontrolovať tretíkrát hráč na nulový ukazovateľ alebo ho pridajte do tela podmieneného príkazu. Navrhoval by som druhú možnosť:
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);
}
}
....
}
Upozornenie 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));
....
}
....
}
Jedným ťahom sa môžete zbaviť ťažko čitateľného riadku kódu a vyriešiť problém s kontrolou nullptr. Navrhujem zmeniť kód takto:
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);
....
}
....
}
Upozornenie N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Kód vyzerá dosť zvláštne. Zdá sa mi, že došlo k preklepu buď v podmienke alebo pri opätovnom priraďovaní premennej ColumnHeaderPressedCurrentState význam nepravdivý.
Výkon
Ako vidíme, integrácia statického analyzátora PVS-Studio do vášho projektu TeamCity je pomerne jednoduchá. Na to stačí napísať len jeden malý konfiguračný súbor. Kontrola kódu vám umožní identifikovať problémy ihneď po zostavení, čo ich pomôže odstrániť, keď sú zložitosť a náklady na zmeny stále nízke.
Ak chcete zdieľať tento článok s anglicky hovoriacim publikom, použite odkaz na preklad: Vladislav Stolyarov.
Zdroj: hab.com