A PVS-Studio analizátor használatának egyik legfrissebb forgatókönyve a CI rendszerekkel való integrációja. És bár egy PVS-Studio projekt elemzése szinte bármilyen folyamatos integrációs rendszerből néhány parancsba beépíthető, továbbra is ezt a folyamatot még kényelmesebbé tesszük. A PVS-Studio mostantól támogatja az analizátor kimenetének TeamCity - TeamCity Inspections Type formátumba való átalakítását. Lássuk, hogyan működik.
Információ a használt szoftverről
Információk a vizsgált projektről
Próbáljuk ki ezt a funkciót egy gyakorlati példán – elemezzük az OpenRCT2 projektet.
beállítás
Az időmegtakarítás érdekében valószínűleg kihagyom a telepítési folyamatot, és attól a pillanattól kezdem, amikor a TeamCity szerver fut a számítógépemen. El kell mennünk a következő helyre: localhost:{telepítési folyamat során megadott port} (esetemben localhost:9090), és meg kell adnunk az engedélyezési adatokat. Belépés után fogad minket:
Kattintson a Projekt létrehozása gombra. Ezután válassza a Manuálisan lehetőséget, és töltse ki a mezőket.
A gomb megnyomása után Teremt, beállításokat tartalmazó ablak fogad minket.
Kattintsunk Építési konfiguráció létrehozása.
Töltse ki a mezőket, és kattintson a gombra Teremt. Megjelenik egy ablak, amely arra kéri, hogy válasszon verziókezelő rendszert. Mivel a források már helyileg találhatók, kattintson a gombra Tovább.
Végül áttérünk a projektbeállításokra.
Adjuk hozzá az összeállítási lépéseket, ehhez kattintson: Építési lépések -> Építési lépés hozzáadása.
Itt választunk:
- Futó típusa -> Parancssor
- Futtatás -> Egyéni szkript
Mivel a projekt összeállítása során elemzést végzünk, ezért az összeállítás és az elemzés egy lépésből áll, ezért töltse ki a mezőt Egyéni parancsfájl:
A későbbiekben megvizsgáljuk az egyes lépéseket. Fontos, hogy az analizátor betöltése, a projekt összeállítása, elemzése, a jelentés kiadása és formázása mindössze tizenegy sornyi kódot vesz igénybe.
Az utolsó dolog, amit meg kell tennünk, az a környezeti változók beállítása, amelyekben felvázoltam néhány módszert az olvashatóság javítására. Ehhez lépjünk tovább: Paraméterek -> Új paraméter hozzáadása és adjunk hozzá három változót:
Nincs más dolgod, mint megnyomni a gombot futás a jobb felső sarokban. Amíg a projekt összeállítása és elemzése folyik, mesélek a forgatókönyvről.
Közvetlenül forgatókönyv
Először is le kell töltenünk a legújabb PVS-Studio disztribúciót. Ehhez a Chocolatey csomagkezelőt használjuk. Azok számára, akik többet szeretnének tudni erről, van egy megfelelő
choco install pvs-studio -y
Ezután indítsuk el a CLMonitor projekt build-követő segédprogramját.
%CLmon% monitor –-attach
Ezután a projektet környezeti változóként építjük fel MSB az az elérési út az MSBuild verziójához, amelyet meg kell építeni
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Adjuk meg a PVS-Studio bejelentkezési és licenckulcsát:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
A felépítés befejezése után futtassa újra a CLMonitort az előfeldolgozott fájlok és a statikus elemzés létrehozásához:
%CLmon% analyze -l "c:ptest.plog"
Ezután egy másik segédprogramot fogunk használni a disztribúciónkból. A PlogConverter a jelentést szabványos formátumból TeamCity-specifikus formátumba konvertálja. Ennek köszönhetően közvetlenül a build ablakban tudjuk majd megtekinteni.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Az utolsó lépés a formázott jelentés megjelenítése stdout, ahol azt a TeamCity elemző fogja felvenni.
type "C:tempptest.plog_TeamCity.txt"
Teljes szkript kód:
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"
Időközben a projekt összeállítása, elemzése sikeresen befejeződött, mehetünk a fülre projektek és győződjön meg róla.
Most pedig kattintsunk Ellenőrzések Összesenaz analizátor jelentésének megtekintéséhez:
A figyelmeztetések diagnosztikai szabályszámok szerint vannak csoportosítva. A kódban való navigáláshoz a figyelmeztetést tartalmazó sorszámra kell kattintani. A jobb felső sarokban lévő kérdőjelre kattintva egy új lap nyílik meg a dokumentációval. A kódban úgy is navigálhat, hogy rákattint az elemző figyelmeztető sorszámára. Használat közben lehetséges a navigáció távoli számítógépről SourceTreeRoot jelző. Bárki, akit érdekel az analizátor ezen üzemmódja, megismerkedhet a megfelelő fejezettel
Az analizátor eredményeinek megtekintése
Most, hogy befejeztük a build telepítését és konfigurálását, vessünk egy pillantást néhány érdekes figyelmeztetésre, amelyek az éppen vizsgált projektben találhatók.
Figyelmeztetés 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;
}
Az analizátor hibát észlelt a memória dinamikus lefoglalása után CreateObject, amikor kivétel történik, a memória nem törlődik, és memóriaszivárgás lép fel.
Figyelmeztetés 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),
....
};
A statikus analizátoron kívül kevés ember tudott átmenni ezen a figyelmességi teszten. Ez a másolás-beillesztés példa éppen ezért jó.
Figyelmeztetések N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Természetesen nem mindig hiba, ha az alaposztályban és a leszármazottban azonos nevű változót használunk. Maga az öröklődési technológia azonban azt feltételezi, hogy a szülőosztály összes mezője jelen van a gyermekosztályban. Azzal, hogy az örökösben azonos nevű mezőket deklarálunk, zavart okozunk.
Figyelmeztetés N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Nézzük meg közelebbről. Kifejezés imageDirection/8 hamis lesz, ha imageDirection -7 és 7 közötti tartományban van. Második rész: (imageDirection / 8) != 3 csekkeket imageDirection a tartományon kívülre: -31 és -24, illetve 24 és 31 között. Számomra elég furcsának tűnik, hogy ilyen módon ellenőrzöm a számokat egy bizonyos tartományban, és még ha nincs is hiba ebben a kódrészletben, azt javaslom, hogy írd át ezeket a feltételeket, hogy egyértelműbbé váljanak. Ez sokkal könnyebbé tenné azoknak az embereknek az életét, akik olvassák és karbantartják ezt a kódot.
Figyelmeztetés 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;
....
}
....
}
Ezt a kódrészletet nagy valószínűséggel dekompilációval kaptuk. Aztán a hagyott megjegyzésből ítélve a nem működő kód egy részét eltávolították. Azonban még hátra van néhány műtét cursorId, aminek szintén nincs sok értelme.
Figyelmeztetés 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); // <=
}
....
}
Ez a kód meglehetősen könnyen javítható, csak harmadszor kell ellenőriznie játékos egy null mutatóhoz, vagy adja hozzá a feltételes utasítás törzséhez. Én a második lehetőséget javaslom:
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);
}
}
....
}
Figyelmeztetés 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));
....
}
....
}
Egy csapásra megszabadulhat egy nehezen olvasható kódsortól, és megoldhatja a problémát a nullptr. Javaslom a kód megváltoztatását az alábbiak szerint:
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);
....
}
....
}
Figyelmeztetés N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
A kód elég furcsán néz ki. Számomra úgy tűnik, hogy vagy a feltételben, vagy a változó újbóli hozzárendelésekor volt elírás ColumnHeaderPressedCurrentState jelentését hamis.
Teljesítmény
Amint látjuk, a PVS-Studio statikus analizátor integrálása a TeamCity projektbe meglehetősen egyszerű. Ehhez elég csak egy kis konfigurációs fájlt írni. A kód ellenőrzése lehetővé teszi a problémák azonnali azonosítását az összeszerelés után, ami segít kiküszöbölni azokat, amikor a változtatások bonyolultsága és költsége még mindig alacsony.
Ha meg szeretné osztani ezt a cikket egy angolul beszélő közönséggel, használja a fordítási linket: Vladislav Stolyarov.
Forrás: will.com