PVS-Studio անալիզատորի օգտագործման ամենաարդի սցենարներից մեկը CI համակարգերի հետ դրա ինտեգրումն է: Եվ չնայած PVS-Studio նախագծի վերլուծությունը գրեթե ցանկացած շարունակական ինտեգրացիոն համակարգից կարող է ներկառուցվել ընդամենը մի քանի հրամանների մեջ, մենք շարունակում ենք այս գործընթացը դարձնել էլ ավելի հարմար: PVS-Studio-ն այժմ ունի աջակցություն անալիզատորի ելքը TeamCity - TeamCity Inspections Type-ի ձևաչափի փոխակերպելու համար: Տեսնենք, թե ինչպես է այն աշխատում:
Տեղեկություններ օգտագործվող ծրագրաշարի մասին
Տեղեկություններ ուսումնասիրվող ծրագրի մասին
Փորձենք այս ֆունկցիոնալությունը գործնական օրինակով. եկեք վերլուծենք OpenRCT2 նախագիծը:
հարմարեցում
Ժամանակ խնայելու համար ես հավանաբար բաց կթողնեմ տեղադրման գործընթացը և կսկսեմ այն պահից, երբ իմ համակարգչում աշխատում է TeamCity սերվերը: Մենք պետք է գնանք. Մտնելուց հետո մեզ կդիմավորեն.
Սեղմեք Ստեղծել նախագիծ կոճակը: Հաջորդը, ընտրեք Ձեռքով և լրացրեք դաշտերը:
Կոճակը սեղմելուց հետո Ստեղծել, մեզ դիմավորում է կարգավորումներով պատուհան։
Եկեք սեղմենք Ստեղծեք կառուցման կոնֆիգուրացիա.
Լրացրեք դաշտերը և սեղմեք Ստեղծել. Մենք տեսնում ենք պատուհան, որում խնդրում ենք ընտրել տարբերակի կառավարման համակարգ: Քանի որ աղբյուրներն արդեն տեղակայված են տեղում, սեղմեք Բացթողել.
Ի վերջո, մենք անցնում ենք նախագծի կարգավորումներին:
Եկեք ավելացնենք հավաքման քայլերը, սա անելու համար սեղմեք. Կառուցման քայլեր -> Ավելացնել կառուցման քայլ.
Այստեղ մենք ընտրում ենք.
- Runner տեսակ -> Հրամանի տող
- Run -> Custom Script
Քանի որ մենք վերլուծություն կկատարենք նախագծի կազմման ժամանակ, հավաքումը և վերլուծությունը պետք է լինեն մեկ քայլ, ուստի լրացրեք դաշտը Մաքսային սց:
Հետագայում մենք կանդրադառնանք առանձին քայլերին: Կարևոր է, որ անալիզատորի բեռնումը, նախագիծը հավաքելը, այն վերլուծելը, զեկույցը թողարկելը և դրա ձևաչափումը պահանջում են ընդամենը տասնմեկ տող կոդ:
Վերջին բանը, որ մենք պետք է անենք, շրջակա միջավայրի փոփոխականները սահմանելն է, որոնք ես նախանշել եմ դրանց ընթեռնելիությունը բարելավելու որոշ ուղիներ: Դա անելու համար եկեք շարժվենք. Պարամետրեր -> Ավելացնել նոր պարամետր և ավելացնել երեք փոփոխական.
Ընդամենը պետք է սեղմել կոճակը Վազում վերին աջ անկյունում։ Մինչ նախագիծը հավաքվում և վերլուծվում է, ես ձեզ կպատմեմ սցենարի մասին:
Ուղիղ սցենար
Նախ, մենք պետք է ներբեռնենք PVS-Studio-ի վերջին բաշխումը: Դրա համար մենք օգտագործում ենք Chocolatey փաթեթի կառավարիչը: Նրանց համար, ովքեր ցանկանում են ավելին իմանալ այս մասին, կա համապատասխան
choco install pvs-studio -y
Հաջորդը, եկեք գործարկենք CLMonitor նախագծի կառուցման հետագծման օգտակար ծրագիրը:
%CLmon% monitor –-attach
Այնուհետև մենք նախագիծը կկառուցենք որպես շրջակա միջավայրի փոփոխական MSB MSBuild-ի այն տարբերակն է, որը ես պետք է կառուցեմ
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Եկեք մուտքագրենք PVS-Studio-ի մուտքի և լիցենզիայի բանալին.
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Կառուցումն ավարտվելուց հետո նորից գործարկեք CLMonitor-ը՝ նախապես մշակված ֆայլեր և ստատիկ վերլուծություն ստեղծելու համար.
%CLmon% analyze -l "c:ptest.plog"
Այնուհետև մենք կօգտագործենք մեկ այլ օգտակար ծառայություն մեր բաշխումից: PlogConverter-ը հաշվետվությունը ստանդարտ ձևաչափից վերածում է TeamCity-ի հատուկ ձևաչափի: Դրա շնորհիվ մենք կկարողանանք դիտել այն անմիջապես կառուցման պատուհանում:
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Վերջին քայլը ձևաչափված զեկույցի ցուցադրումն է stdout, որտեղ այն կվերցվի TeamCity վերլուծիչի կողմից:
type "C:tempptest.plog_TeamCity.txt"
Ամբողջական սցենարի կոդը.
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"
Միևնույն ժամանակ, նախագծի հավաքումն ու վերլուծությունը հաջողությամբ ավարտվել են, մենք կարող ենք անցնել ներդիր ծրագրեր և համոզվեք դրանում:
Հիմա եկեք սեղմենք Ստուգումներ Ընդամենըգնալ անալիզատորի հաշվետվությունը դիտելու համար.
Զգուշացումները խմբավորված են ըստ ախտորոշիչ կանոնների համարների: Կոդի միջով նավարկելու համար հարկավոր է սեղմել նախազգուշացումով տողի համարը: Սեղմելով վերևի աջ անկյունում գտնվող հարցական նշանի վրա, կբացվի փաստաթղթերով նոր ներդիր: Կարող եք նաև նավարկել կոդի միջով՝ սեղմելով անալիզատորի նախազգուշացումով տողի համարը: Օգտագործելիս հնարավոր է նավարկություն հեռավոր համակարգչից SourceTreeRoot մարկեր. Յուրաքանչյուր ոք, ով հետաքրքրված է անալիզատորի աշխատանքի այս եղանակով, կարող է ծանոթանալ համապատասխան բաժնին
Դիտելով անալիզատորի արդյունքները
Այժմ, երբ մենք ավարտեցինք կառուցվածքի տեղակայումն ու կազմաձևումը, եկեք տեսնենք մի քանի հետաքրքիր նախազգուշացումներ, որոնք հայտնաբերվել են մեր դիտարկած նախագծում:
Զգուշացում 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;
}
Անալիզատորը նկատել է սխալ, որը դինամիկ կերպով հիշողությունը տեղաբաշխելուց հետո CreateObject, երբ բացառություն է տեղի ունենում, հիշողությունը չի մաքրվում, և հիշողության արտահոսք է տեղի ունենում:
Զգուշացում 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),
....
};
Քիչ մարդիկ, բացի ստատիկ անալիզատորից, կարող էին անցնել ուշադրության այս թեստը: Այս copy-paste օրինակը լավ է հենց այս պատճառով:
Զգուշացումներ N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Իհարկե, նույն անունով փոփոխական օգտագործելը բազային դասում և հետնորդում միշտ չէ, որ սխալ է: Այնուամենայնիվ, ժառանգական տեխնոլոգիան ինքնին ենթադրում է, որ ծնող դասի բոլոր դաշտերը առկա են երեխայի դասարանում: Ժառանգում հայտարարելով նույն անունով դաշտերը՝ մենք շփոթություն ենք ստեղծում։
Զգուշացում N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Եկեք ավելի սերտ նայենք: Արտահայտություն imageDirection/8 կեղծ կլինի, եթե պատկերի ուղղություն գտնվում է -7-ից 7-ի սահմաններում: Երկրորդ մաս. (imageDirection / 8) != 3 ստուգումներ պատկերի ուղղություն միջակայքից դուրս գտնվելու համար՝ համապատասխանաբար -31-ից -24 և 24-ից 31: Ինձ համար բավականին տարօրինակ է թվում այս կերպ ստուգել թվերը որոշակի տիրույթում ներառելու համար, և, նույնիսկ եթե այս կոդի մեջ սխալ չկա, ես խորհուրդ կտայի վերաշարադրել այս պայմանները, որպեսզի ավելի հստակ լինի: Սա շատ ավելի կհեշտացնի կյանքը այն մարդկանց համար, ովքեր կկարդան և կպահպանեն այս կոդը:
Զգուշացում 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;
....
}
....
}
Կոդի այս հատվածը, ամենայն հավանականությամբ, ստացվել է ապակոմպիլյացիայի միջոցով: Հետո, դատելով թողած մեկնաբանությունից, չաշխատող կոդի մի մասը հանվել է։ Այնուամենայնիվ, դեռ մի քանի վիրահատություն է մնացել կուրսորի ID, որոնք նույնպես այնքան էլ իմաստ չունեն:
Զգուշացում 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); // <=
}
....
}
Այս կոդը բավականին հեշտ է ուղղել, պարզապես անհրաժեշտ է այն երրորդ անգամ ստուգել խաղացող զրոյական ցուցիչի վրա կամ ավելացրե՛ք այն պայմանական հայտարարության մարմնին: Ես կառաջարկեի երկրորդ տարբերակը.
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);
}
}
....
}
Զգուշացում 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));
....
}
....
}
Դուք կարող եք մեկ հարվածով ազատվել կոդի դժվար ընթեռնելի տողից և լուծել խնդիրը՝ ստուգելով nullptr. Առաջարկում եմ ծածկագիրը փոխել հետևյալ կերպ.
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);
....
}
....
}
Զգուշացում N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Կոդը բավականին տարօրինակ տեսք ունի։ Ինձ թվում է՝ տառասխալ է եղել կա՛մ պայմանում, կա՛մ փոփոխականը վերահանձնելիս ColumnHeaderPressedCurrentState արժեքներ սուտ.
Արտադրողականություն
Ինչպես տեսնում ենք, PVS-Studio ստատիկ անալիզատորի ինտեգրումը ձեր TeamCity նախագծին բավականին պարզ է: Դա անելու համար բավական է գրել ընդամենը մեկ փոքր կոնֆիգուրացիայի ֆայլ: Կոդի ստուգումը թույլ կտա հայտնաբերել խնդիրները հավաքումից անմիջապես հետո, ինչը կօգնի վերացնել դրանք, երբ փոփոխությունների բարդությունն ու արժեքը դեռևս ցածր են:
Եթե ցանկանում եք կիսվել այս հոդվածով անգլիախոս լսարանի հետ, խնդրում ենք օգտագործել թարգմանության հղումը՝ Վլադիսլավ Ստոլյարով:
Source: www.habr.com