Një nga skenarët më aktualë për përdorimin e analizuesit PVS-Studio është integrimi i tij me sistemet CI. Dhe megjithëse analiza e një projekti PVS-Studio nga pothuajse çdo sistem integrimi i vazhdueshëm mund të ndërtohet në vetëm disa komanda, ne vazhdojmë ta bëjmë këtë proces edhe më të përshtatshëm. PVS-Studio tani ka mbështetje për konvertimin e daljes së analizuesit në një format për TeamCity - TeamCity Inspections Type. Le të shohim se si funksionon.
Informacion rreth softuerit të përdorur
Informacion rreth projektit në studim
Le ta provojmë këtë funksionalitet në një shembull praktik - le të analizojmë projektin OpenRCT2.
rregullim
Për të kursyer kohë, ndoshta do ta kapërcej procesin e instalimit dhe do të filloj nga momenti kur serveri TeamCity funksionon në kompjuterin tim. Duhet të shkojmë te: localhost:{porti i specifikuar gjatë procesit të instalimit} (në rastin tim, localhost:9090) dhe të fusim të dhënat e autorizimit. Pas hyrjes do të na përshëndesin:
Klikoni në butonin Krijo projekt. Më pas, zgjidhni Manual dhe plotësoni fushat.
Pas shtypjes së butonit Krijo, na pret një dritare me cilësime.
Le të klikojmë Krijo konfigurimin e ndërtimit.
Plotësoni fushat dhe klikoni Krijo. Ne shohim një dritare që ju kërkon të zgjidhni një sistem të kontrollit të versionit. Meqenëse burimet janë vendosur tashmë në nivel lokal, klikoni Kalo.
Më në fund kalojmë te cilësimet e projektit.
Le të shtojmë hapat e montimit, për ta bërë këtë klikoni: Ndërtimi i hapave -> Shto hapin e ndërtimit.
Këtu zgjedhim:
- Lloji Runner -> Linja e Komandës
- Ekzekuto -> Skript i personalizuar
Meqenëse do të kryejmë analiza gjatë përpilimit të projektit, montimi dhe analiza duhet të jenë një hap, prandaj plotësoni fushën Custom Script:
Ne do të shqyrtojmë hapat individualë më vonë. Është e rëndësishme që ngarkimi i analizuesit, montimi i projektit, analizimi i tij, nxjerrja e raportit dhe formatimi i tij duhen vetëm njëmbëdhjetë rreshta kodi.
Gjëja e fundit që duhet të bëjmë është të vendosim variablat e mjedisit, të cilat unë kam përshkruar disa mënyra për të përmirësuar lexueshmërinë e tyre. Për ta bërë këtë, le të vazhdojmë: Parametrat -> Shto parametër të ri dhe shtoni tre variabla:
Gjithçka që duhet të bëni është të shtypni butonin run në këndin e sipërm të djathtë. Ndërsa projekti është duke u mbledhur dhe analizuar, unë do t'ju tregoj për skenarin.
Direkt skenar
Së pari, duhet të shkarkojmë shpërndarjen më të fundit të PVS-Studio. Për këtë ne përdorim menaxherin e paketave Chocolatey. Për ata që duan të dinë më shumë për këtë, ekziston një përkatës
choco install pvs-studio -y
Më pas, le të nisim mjetin e përcjelljes së ndërtimit të projektit CLMonitor.
%CLmon% monitor –-attach
Më pas do të ndërtojmë projektin si variabël mjedisor MSB është rruga drejt versionit të MSBuild që duhet të ndërtoj
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Le të fusim çelësin e hyrjes dhe licencës për PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Pasi të përfundojë ndërtimi, ekzekutoni përsëri CLMonitor për të gjeneruar skedarë të parapërpunuar dhe analiza statike:
%CLmon% analyze -l "c:ptest.plog"
Pastaj do të përdorim një mjet tjetër nga shpërndarja jonë. PlogConverter konverton një raport nga një format standard në një format specifik për TeamCity. Falë kësaj, ne do të jemi në gjendje ta shikojmë atë drejtpërdrejt në dritaren e ndërtimit.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Hapi i fundit është shfaqja e raportit të formatuar në stdout, ku do të merret nga analizuesi TeamCity.
type "C:tempptest.plog_TeamCity.txt"
Kodi i plotë i skenarit:
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"
Ndërkohë, montimi dhe analiza e projektit ka përfunduar me sukses, ne mund të shkojmë në skedën projektet dhe sigurohuni për të.
Tani le të klikojmë Inspektimet Totalipër të shkuar te shikimi i raportit të analizuesit:
Paralajmërimet grupohen sipas numrave të rregullave diagnostikuese. Për të lundruar nëpër kod, duhet të klikoni në numrin e linjës me paralajmërimin. Klikimi në pikëpyetjen në këndin e sipërm djathtas do t'ju hapë një skedë të re me dokumentacion. Ju gjithashtu mund të lundroni nëpër kod duke klikuar në numrin e linjës me paralajmërimin e analizuesit. Lundrimi nga një kompjuter në distancë është i mundur kur përdorni SourceTreeRoot shënues. Kushdo që është i interesuar për këtë mënyrë funksionimi të analizuesit mund të njihet me seksionin përkatës
Shikimi i rezultateve të analizuesit
Tani që kemi mbaruar vendosjen dhe konfigurimin e ndërtimit, le të hedhim një vështrim në disa paralajmërime interesante që gjenden në projektin që po shikojmë.
Paralajmërim 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;
}
Analizuesi vuri re një gabim që pas shpërndarjes dinamike të kujtesës në KrijoObjekt, kur ndodh një përjashtim, memoria nuk pastrohet dhe ndodh një rrjedhje e kujtesës.
Paralajmërim 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),
....
};
Pak njerëz përveç një analizuesi statik mund ta kalojnë këtë test të vëmendjes. Ky shembull copy-paste është i mirë pikërisht për këtë arsye.
Paralajmërimet N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Natyrisht, përdorimi i një ndryshoreje me të njëjtin emër në klasën bazë dhe në pasardhësin nuk është gjithmonë një gabim. Megjithatë, vetë teknologjia e trashëgimisë supozon se të gjitha fushat e klasës prind janë të pranishme në klasën e fëmijës. Duke deklaruar fushat me të njëjtin emër në trashëgimtar, krijojmë konfuzion.
Paralajmërim N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Le të hedhim një vështrim më të afërt. Shprehje imageDirection/8 do të jetë false nëse Drejtimi i imazhit është në rangun nga -7 në 7. Pjesa e dytë: (imageDrection / 8) != 3 çeqe Drejtimi i imazhit për të qenë jashtë intervalit: nga -31 në -24 dhe nga 24 në 31, përkatësisht. Më duket mjaft e çuditshme të kontrolloj numrat për përfshirje në një gamë të caktuar në këtë mënyrë dhe, edhe nëse nuk ka gabim në këtë pjesë të kodit, unë do të rekomandoja rishkrimin e këtyre kushteve për të qenë më të qartë. Kjo do ta bënte jetën shumë më të lehtë për njerëzit që do të lexonin dhe ruanin këtë kod.
Paralajmërim 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;
....
}
....
}
Ky fragment kodi ka shumë të ngjarë të jetë marrë nga dekompilimi. Më pas, duke gjykuar nga komenti i lënë, një pjesë e kodit që nuk funksiononte u hoq. Megjithatë, kanë mbetur edhe disa operacione kursorId, të cilat gjithashtu nuk kanë shumë kuptim.
Paralajmërim 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); // <=
}
....
}
Ky kod është mjaft i lehtë për t'u korrigjuar; thjesht duhet ta kontrolloni për herë të tretë lojtar në një tregues null, ose shtoni atë në trupin e deklaratës së kushtëzuar. Unë do të sugjeroja opsionin e dytë:
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);
}
}
....
}
Paralajmërim 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));
....
}
....
}
Ju mund të hiqni qafe një linjë kodi të vështirë për t'u lexuar me një goditje dhe të zgjidhni problemin me kontrollimin e nullptr. Unë sugjeroj ndryshimin e kodit si më poshtë:
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);
....
}
....
}
Paralajmërim N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Kodi duket mjaft i çuditshëm. Më duket se ka pasur një gabim shtypi ose në kusht ose gjatë ricaktimit të ndryshores ColumnHeaderPressedCurrentState kuptimi i rremë.
Prodhim
Siç mund ta shohim, integrimi i analizuesit statik PVS-Studio në projektin tuaj TeamCity është mjaft i thjeshtë. Për ta bërë këtë, mjafton të shkruani vetëm një skedar të vogël konfigurimi. Kontrollimi i kodit do t'ju lejojë të identifikoni problemet menjëherë pas montimit, gjë që do të ndihmojë në eliminimin e tyre kur kompleksiteti dhe kostoja e ndryshimeve janë ende të ulëta.
Nëse dëshironi ta ndani këtë artikull me një audiencë anglishtfolëse, ju lutemi përdorni lidhjen e përkthimit: Vladislav Stolyarov.
Burimi: www.habr.com