Et af de mest aktuelle scenarier for brug af PVS-Studio-analysatoren er dens integration med CI-systemer. Og selvom analysen af et PVS-Studio-projekt fra næsten ethvert kontinuerligt integrationssystem kan indbygges i nogle få kommandoer, fortsætter vi med at gøre denne proces endnu mere bekvem. PVS-Studio har nu understøttelse af konvertering af analysatoroutput til et format for TeamCity - TeamCity Inspections Type. Lad os se, hvordan det virker.
Oplysninger om den anvendte software
Information om det undersøgte projekt
Lad os prøve denne funktionalitet på et praktisk eksempel - lad os analysere OpenRCT2-projektet.
justering
For at spare tid vil jeg sandsynligvis springe installationsprocessen over og starte fra det øjeblik, hvor jeg har TeamCity-serveren kørende på min computer. Vi skal gå til: localhost:{port specificeret under installationsprocessen} (i mit tilfælde localhost:9090) og indtaste autorisationsdata. Efter indtastning vil vi blive mødt af:
Klik på knappen Opret projekt. Vælg derefter Manuelt og udfyld felterne.
Efter at have trykket på knappen Opret, bliver vi mødt af et vindue med indstillinger.
Lad os klikke Opret build-konfiguration.
Udfyld felterne og klik Opret. Vi ser et vindue, der beder dig vælge et versionskontrolsystem. Da kilderne allerede er lokaliseret, skal du klikke Spring.
Til sidst går vi videre til projektindstillingerne.
Lad os tilføje monteringstrin, for at gøre dette klik: Byg trin -> Tilføj byggetrin.
Her vælger vi:
- Løbertype -> Kommandolinje
- Kør -> Brugerdefineret script
Da vi vil udføre analyser under projektopstilling, bør montering og analyse være ét trin, så udfyld feltet Brugerdefineret script:
Vi vil se på de enkelte trin senere. Det er vigtigt at indlæse analysatoren, samle projektet, analysere det, udskrive rapporten og formatere det kun tager elleve linjer kode.
Den sidste ting, vi skal gøre, er at indstille miljøvariablerne, som jeg har skitseret nogle måder at forbedre deres læsbarhed på. For at gøre dette, lad os gå videre: Parametre -> Tilføj ny parameter og tilføj tre variable:
Alt du skal gøre er at trykke på knappen Kør i øverste højre hjørne. Mens projektet bliver samlet og analyseret, vil jeg fortælle dig om manuskriptet.
Direkte script
Først skal vi downloade den seneste PVS-Studio-distribution. Til dette bruger vi Chocolatey Package Manager. For dem, der vil vide mere om dette, er der en tilsvarende
choco install pvs-studio -y
Lad os derefter lancere CLMonitor-projektbyggesporingsværktøjet.
%CLmon% monitor –-attach
Derefter vil vi bygge projektet som en miljøvariabel MSB er stien til den version af MSBuild jeg skal bygge
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Lad os indtaste login og licensnøglen til PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Når opbygningen er færdig, skal du køre CLMonitor igen for at generere forbehandlede filer og statisk analyse:
%CLmon% analyze -l "c:ptest.plog"
Så vil vi bruge et andet hjælpeprogram fra vores distribution. PlogConverter konverterer en rapport fra et standardformat til et TeamCity-specifikt format. Takket være dette vil vi være i stand til at se det direkte i byggevinduet.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Det sidste trin er at vise den formaterede rapport i stdout, hvor det vil blive samlet op af TeamCity-parseren.
type "C:tempptest.plog_TeamCity.txt"
Fuld script kode:
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"
I mellemtiden er montagen og analysen af projektet gennemført med succes, vi kan gå til fanen Projekter og sørg for det.
Lad os nu klikke videre Eftersyn i altfor at gå til visning af analysatorrapporten:
Advarsler er grupperet efter diagnostiske regelnumre. For at navigere gennem koden skal du klikke på linjenummeret med advarslen. Ved at klikke på spørgsmålstegnet i øverste højre hjørne åbner du en ny fane med dokumentation. Du kan også navigere gennem koden ved at klikke på linjenummeret med analysatoradvarslen. Navigation fra en fjerncomputer er mulig ved brug SourceTreeRoot markør. Enhver, der er interesseret i denne funktionsmåde for analysatoren, kan gøre sig bekendt med det tilsvarende afsnit
Visning af analysatorens resultater
Nu hvor vi er færdige med at implementere og konfigurere bygningen, lad os tage et kig på nogle interessante advarsler, der findes i det projekt, vi kigger på.
Advarsel 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;
}
Analysatoren bemærkede en fejl, der efter dynamisk allokering af hukommelse i Opret objekt, når der opstår en undtagelse, ryddes hukommelsen ikke, og der opstår et hukommelseslæk.
Advarsel 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),
....
};
Kun få andre end en statisk analysator kunne bestå denne opmærksomhedstest. Dette copy-paste eksempel er godt af netop denne grund.
Advarsler N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Det er selvfølgelig ikke altid en fejl at bruge en variabel med samme navn i basisklassen og i efterkommeren. Dog forudsætter arveteknologien selv, at alle forældreklassens felter er til stede i børneklassen. Ved at angive felter med samme navn i arvingen introducerer vi forvirring.
Advarsel N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Lad os se nærmere. Udtryk imageDirection/8 vil være falsk hvis imageDirection er i området fra -7 til 7. Anden del: (imageDirection / 8) != 3 checks imageDirection for at være uden for området: henholdsvis fra -31 til -24 og fra 24 til 31. Det forekommer mig ret mærkeligt at kontrollere tal for inklusion i et bestemt område på denne måde, og selvom der ikke er nogen fejl i dette stykke kode, vil jeg anbefale at omskrive disse betingelser for at være mere eksplicitte. Dette ville gøre livet meget lettere for de mennesker, der ville læse og vedligeholde denne kode.
Advarsel 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;
....
}
....
}
Dette kodefragment blev højst sandsynligt opnået ved dekompilering. Derefter, at dømme efter kommentaren, blev en del af den ikke-fungerende kode fjernet. Der er dog stadig et par operationer tilbage cursorId, hvilket heller ikke giver meget mening.
Advarsel 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); // <=
}
....
}
Denne kode er ret nem at rette; du skal bare tjekke den en tredje gang spiller til en nul-markør, eller føj den til brødteksten i den betingede sætning. Jeg vil foreslå den anden mulighed:
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);
}
}
....
}
Advarsel 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));
....
}
....
}
Du kan slippe af med en svær-læselig kodelinje i ét hug og løse problemet ved at tjekke efter nullptr. Jeg foreslår at ændre koden som følger:
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);
....
}
....
}
Advarsel N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Koden ser ret mærkelig ud. Det forekommer mig, at der var en tastefejl enten i betingelsen eller ved gentildeling af variablen ColumnHeaderPressedCurrentState betydning falsk.
Output
Som vi kan se, er det ret simpelt at integrere den statiske PVS-Studio-analysator i dit TeamCity-projekt. For at gøre dette er det nok kun at skrive en lille konfigurationsfil. Kontrol af koden giver dig mulighed for at identificere problemer umiddelbart efter montering, hvilket vil hjælpe med at eliminere dem, når kompleksiteten og omkostningerne ved ændringer stadig er lave.
Hvis du vil dele denne artikel med et engelsktalende publikum, så brug venligst oversættelseslinket: Vladislav Stolyarov.
Kilde: www.habr.com