Isa sa mga pinakabagong sitwasyon para sa paggamit ng PVS-Studio analyzer ay ang pagsasama nito sa mga CI system. At kahit na ang pagsusuri ng isang proyekto ng PVS-Studio mula sa halos anumang tuluy-tuloy na sistema ng pagsasama ay maaaring itayo sa ilang mga utos, patuloy naming ginagawang mas maginhawa ang prosesong ito. Ang PVS-Studio ay mayroon na ngayong suporta para sa pag-convert ng output ng analyzer sa isang format para sa TeamCity - Uri ng TeamCity Inspections. Tingnan natin kung paano ito gumagana.
Impormasyon tungkol sa software na ginamit
Impormasyon tungkol sa proyektong pinag-aaralan
Subukan natin ang functionality na ito gamit ang isang praktikal na halimbawa - suriin natin ang proyekto ng OpenRCT2.
pag-aayos
Upang makatipid ng oras, malamang na laktawan ko ang proseso ng pag-install at magsimula mula sa sandaling mayroon akong server ng TeamCity na tumatakbo sa aking computer. Kailangan nating pumunta sa: localhost:{port na tinukoy sa panahon ng proseso ng pag-install} (sa aking kaso, localhost:9090) at ipasok ang data ng pahintulot. Pagkapasok namin ay sasalubungin kami ni:
Mag-click sa pindutan ng Lumikha ng Proyekto. Susunod, piliin ang Manu-manong at punan ang mga patlang.
Pagkatapos pindutin ang pindutan Lumikha, binati kami ng isang window na may mga setting.
Pindutin natin Lumikha ng configuration ng build.
Punan ang mga patlang at i-click Lumikha. Nakikita namin ang isang window na humihiling sa iyo na pumili ng isang version control system. Dahil ang mga mapagkukunan ay matatagpuan na sa lokal, i-click Laktawan.
Sa wakas, lumipat kami sa mga setting ng proyekto.
Magdagdag tayo ng mga hakbang sa pagpupulong, para magawa ang pag-click na ito: Mga hakbang sa pagbuo -> Magdagdag ng hakbang sa pagbuo.
Dito kami pumili:
- Uri ng runner -> Command Line
- Run -> Custom na Script
Dahil magsasagawa kami ng pagsusuri sa panahon ng pagsasama-sama ng proyekto, ang pagpupulong at pagsusuri ay dapat na isang hakbang, kaya punan ang field Custom Script:
Titingnan natin ang mga indibidwal na hakbang mamaya. Mahalaga na ang pag-load ng analyzer, pag-assemble ng proyekto, pagsusuri nito, pag-output ng ulat at pag-format nito ay tumatagal lamang ng labing-isang linya ng code.
Ang huling bagay na kailangan nating gawin ay itakda ang mga variable ng kapaligiran, na binalangkas ko ng ilang paraan upang mapabuti ang kanilang pagiging madaling mabasa. Upang gawin ito, magpatuloy tayo: Mga Parameter -> Magdagdag ng bagong parameter at magdagdag ng tatlong variable:
Ang kailangan mo lang gawin ay pindutin ang pindutan Tumakbo sa kanang sulok sa itaas. Habang binubuo at sinusuri ang proyekto, sasabihin ko sa iyo ang tungkol sa script.
Direktang script
Una, kailangan nating i-download ang pinakabagong pamamahagi ng PVS-Studio. Para dito ginagamit namin ang Chocolatey package manager. Para sa mga nais malaman ang higit pa tungkol dito, mayroong isang katumbas
choco install pvs-studio -y
Susunod, ilunsad natin ang CLmonitor project build tracking utility.
%CLmon% monitor β-attach
Pagkatapos ay bubuo kami ng proyekto bilang isang variable ng kapaligiran MSB ay ang landas sa bersyon ng MSBuild na kailangan kong buuin
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Ilagay natin ang login at license key para sa PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Pagkatapos makumpleto ang build, patakbuhin muli ang CLmonitor upang makabuo ng mga na-preprocess na file at static na pagsusuri:
%CLmon% analyze -l "c:ptest.plog"
Pagkatapos ay gagamit kami ng isa pang utility mula sa aming pamamahagi. Kino-convert ng PlogConverter ang isang ulat mula sa isang karaniwang format sa isang format na partikular sa TeamCity. Salamat dito, makikita namin ito nang direkta sa window ng build.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Ang huling hakbang ay upang ipakita ang na-format na ulat sa stdout, kung saan ito ay kukunin ng TeamCity parser.
type "C:tempptest.plog_TeamCity.txt"
Buong script code:
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"
Samantala, matagumpay na nakumpleto ang pagpupulong at pagsusuri ng proyekto, maaari tayong pumunta sa tab proyekto at siguraduhin mo ito.
Ngayon mag-click tayo sa Kabuuan ng mga Inspeksyonupang pumunta sa pagtingin sa ulat ng analyzer:
Nakapangkat ang mga babala ayon sa mga numero ng diagnostic na panuntunan. Upang mag-navigate sa code, kailangan mong mag-click sa numero ng linya na may babala. Ang pag-click sa tandang pananong sa kanang sulok sa itaas ay magbubukas sa iyo ng bagong tab na may dokumentasyon. Maaari ka ring mag-navigate sa code sa pamamagitan ng pag-click sa numero ng linya na may babala ng analyzer. Ang pag-navigate mula sa isang malayuang computer ay posible kapag gumagamit SourceTreeRoot pananda. Ang sinumang interesado sa mode na ito ng pagpapatakbo ng analyzer ay maaaring maging pamilyar sa kaukulang seksyon
Pagtingin sa mga resulta ng analyzer
Ngayong tapos na tayo sa pag-deploy at pag-configure ng build, tingnan natin ang ilang kawili-wiling babala na makikita sa proyektong tinitingnan natin.
Babala 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;
}
Napansin ng analyzer ang isang error na pagkatapos ng dynamic na paglalaan ng memorya CreateObject, kapag may naganap na pagbubukod, ang memorya ay hindi na-clear, at ang isang memory leak ay nangyayari.
Babala 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),
....
};
Ilang tao maliban sa isang static na analyzer ang maaaring makapasa sa attentiveness test na ito. Ang halimbawang copy-paste na ito ay mabuti para sa eksaktong kadahilanang ito.
Mga Babala N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Siyempre, ang paggamit ng variable na may parehong pangalan sa base class at sa descendant ay hindi palaging isang error. Gayunpaman, ipinapalagay mismo ng inheritance technology na ang lahat ng field ng parent class ay nasa child class. Sa pamamagitan ng pagdedeklara ng mga field na may parehong pangalan sa tagapagmana, lumilikha kami ng kalituhan.
Babala N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Tingnan natin nang maigi. Pagpapahayag Direksyon ng larawan/8 magiging huwad kung Direksyon ng imahe ay nasa hanay mula -7 hanggang 7. Pangalawang bahagi: (Direksiyon ng imahe / 8) != 3 mga tseke Direksyon ng imahe para sa pagiging nasa labas ng saklaw: mula -31 hanggang -24 at mula 24 hanggang 31, ayon sa pagkakabanggit. Mukhang kakaiba sa akin na suriin ang mga numero para sa pagsasama sa isang tiyak na hanay sa ganitong paraan at, kahit na walang error sa piraso ng code na ito, inirerekumenda kong muling isulat ang mga kundisyong ito upang maging mas malinaw. Gagawin nitong mas madali ang buhay para sa mga taong magbabasa at magpanatili ng code na ito.
Babala 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;
....
}
....
}
Ang fragment ng code na ito ay malamang na nakuha sa pamamagitan ng decompilation. Pagkatapos, sa paghusga sa komentong iniwan, inalis ang bahagi ng hindi gumaganang code. Gayunpaman, mayroon pa ring ilang mga operasyon na natitira cursorId, na hindi rin gaanong makatuwiran.
Babala 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); // <=
}
....
}
Ang code na ito ay medyo madaling itama; kailangan mo lamang itong suriin sa pangatlong beses manlalaro sa isang null pointer, o idagdag ito sa katawan ng conditional statement. Iminumungkahi ko ang pangalawang pagpipilian:
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);
}
}
....
}
Babala 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));
....
}
....
}
Maaalis mo ang isang mahirap basahin na linya ng code sa isang iglap at lutasin ang problema sa pagsuri para sa nullptr. Iminumungkahi kong baguhin ang code tulad ng sumusunod:
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);
....
}
....
}
Babala N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Ang code ay mukhang medyo kakaiba. Tila sa akin ay nagkaroon ng typo alinman sa kundisyon o kapag muling pagtatalaga ng variable ColumnHeaderPressedCurrentState kahulugan hindi totoo.
Pagbubuhos
Tulad ng nakikita natin, ang pagsasama ng PVS-Studio static analyzer sa iyong proyekto ng TeamCity ay medyo simple. Upang gawin ito, sapat na upang magsulat lamang ng isang maliit na file ng pagsasaayos. Ang pagsuri sa code ay magbibigay-daan sa iyo na matukoy ang mga problema kaagad pagkatapos ng pagpupulong, na makakatulong na maalis ang mga ito kapag ang pagiging kumplikado at gastos ng mga pagbabago ay mababa pa rin.
Kung nais mong ibahagi ang artikulong ito sa isang madla na nagsasalita ng Ingles, mangyaring gamitin ang link ng pagsasalin: Vladislav Stolyarov.
Pinagmulan: www.habr.com