
Een van de meest relevante scenario's voor het gebruik van de PVS-Studio-analyzer is de integratie ervan met CI-systemen. Hoewel de analyse van een PVS-Studio-project praktisch vanuit elk continuous integration-systeem in slechts een paar commando's kan worden geïntegreerd, blijven we proberen dit proces nog gemakkelijker te maken. PVS-Studio heeft nu ondersteuning toegevoegd voor het omzetten van de output van de analyzer naar een formaat voor TeamCity - TeamCity Inspections Type. Laten we kijken hoe dit werkt.
Informatie over de gebruikte software
— een statische analyzer voor C, C++, C# en Java-code, bedoeld om de taak van het vinden en corrigeren van verschillende soorten fouten te vergemakkelijken. De analyzer kan worden gebruikt op Windows, Linux en macOS. In dit artikel zullen we niet alleen de analyzer zelf, maar ook enkele hulpprogramma's uit zijn distributie actief gebruiken.
— is een monitoringserver die de uitvoeringen van compilers volgt. Het moet worden gestart voordat je begint met het bouwen van je project. In de monitoringmodus zal de server alle ondersteunde compilers opstarten. Houd er rekening mee dat deze tool alleen voor de analyse van C/C++-projecten kan worden gebruikt.
– een hulpprogramma voor het converteren van analyzerrapporten naar verschillende formaten.
Informatie over het te onderzoeken project
Laten we deze functionaliteit op een praktisch voorbeeld proberen - laten we het project OpenRCT2 analyseren.
— een open bronimplementatie van het spel RollerCoaster Tycoon 2 (RCT2), dat nieuwe functies toevoegt en fouten corrigeert. Het spel draait om het bouwen en onderhouden van een pretpark, waar attracties, winkels en objecten zich bevinden. De speler moet proberen winst te maken en een goede reputatie voor het park te behouden, terwijl hij zijn gasten gelukkig houdt. OpenRCT2 biedt de mogelijkheid om zowel in scenario's als in een sandbox-modus te spelen. Scenario's vereisen dat de speler een bepaalde taak binnen een bepaalde tijd volbrengt, terwijl de sandbox-modus de speler de vrijheid geeft om een flexibeler park te bouwen zonder enige beperkingen of financiën.
Instellingen
Om tijd te besparen, sla ik het installatieproces over en begin ik op het moment dat de TeamCity-server op mijn computer draait. We moeten gaan naar: localhost:{gegeven poort tijdens installatie}(in mijn geval, localhost:9090) en de autorisatiegegevens invoeren. Na inloggen treffen we ons:

Laten we de knop Project Aanmaken aanklikken. Kies vervolgens Handmatig en vul de velden in.

Na het klikken op de knop Create, krijgen we een venster met instellingen.

Klik op Maak bouwconfiguratie aan.

Vul de velden in en klik op Create. We zien een venster met de optie om een versiebeheersysteem te selecteren. Aangezien de bronbestanden al lokaal zijn, klikken we op Overslaan.

Tenslotte gaan we naar de projectinstellingen.

Voeg bouwstappen toe, klik op: Bouwstappen -> Voeg bouwstap toe.

Kies hier:
- Runner type -> Opdrachtregel
- Uitvoeren -> Aangepast script
Aangezien we de analyse tijdens de compilatie van het project zullen uitvoeren, moeten de bouw en analyse één stap zijn, dus vullen we het veld in met Aangepast script:

We zullen later stoppen bij de afzonderlijke stappen. Het is belangrijk dat het laden van de analyzer, het bouwen van het project, de analyse, het genereren van het rapport en de opmaak in totaal slechts elf regels code in beslag nemen.
Het laatste wat we nodig hebben, is het instellen van omgevingsvariabelen, waarvoor ik enkele paden heb gemarkeerd om hun leesbaarheid te verbeteren. Ga hiervoor naar: Parameters -> Voeg nieuwe parameter toe en voeg drie variabelen toe:

Het blijft over om op de knop te klikken Run in de rechterbovenhoek. Terwijl de bouw en analyse van het project plaatsvinden, zal ik je vertellen over het script.
Het script zelf
Om te beginnen moeten we de nieuwste distributie van PVS-Studio downloaden. Hiervoor gebruiken we de pakketbeheerder Chocolatey. Voor degenen die hier meer over willen weten, is er een bijbehorende :
choco install pvs-studio -yLaten we vervolgens de builtracking-tool CLMonitor starten.
%CLmon% monitor --attachVervolgens bouwen we het project, waarbij het pad naar de versie van MSBuild die ik nodig heb als omgevingsvariabele fungeert. %MSB% %ProjPath% /t:clean %MSB% %ProjPath% /t:rebuild /p:configuration=release %MSB% %ProjPath% /t:g2 %MSB% %ProjPath% /t:PublishPortable Laten we de gebruikersnaam en het licentiesleutel van PVS-Studio invoeren:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%Na de voltooiing van de bouw starten we CLMonitor opnieuw voor het genereren van voorbewerkte bestanden en statische analyse:
%CLmon% analyze -l "c:ptest.plog"Nadat de assemblage is voltooid, starten we CLMonitor opnieuw om de voorbewerkte bestanden en statische analyse te genereren:
%CLmon% analyze -l "c:ptest.plog"Daarna gebruiken we nog een hulpprogramma uit onze distributie. PlogConverter converteert het rapport van het standaardformaat naar een specifiek TeamCity-formaat. Dit stelt ons in staat om het rechtstreeks in het bouwvenster te bekijken.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"Als laatste stap zullen we het opgemaakte rapport weergeven in stdout, waar het door de TeamCity-parser wordt opgepikt.
type "C:tempptest.plog_TeamCity.txt"De volledige scriptcode:
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"Ondertussen is de bouw en analyse van het project succesvol voltooid, we kunnen naar het tabblad Projecten gaan en dit verifiëren.

Laten we nu klikken op Inspections Totaal, om het rapport van de analyzer te bekijken:

Waarschuwingen zijn gegroepeerd op nummer van de diagnostische regels. Om door de code te navigeren, moet u klikken op het regelnummer met de waarschuwing. Klikken op het vraagteken in de rechterbovenhoek opent een nieuw tabblad met documentatie. U kunt ook door de code navigeren door op het regelnummer met de analyzer-waarschuwing te klikken. Navigatie vanaf een externe computer is mogelijk met behulp van de SourceTreeRoot marker. Degenen die geïnteresseerd zijn in deze analyzemodus kunnen de relevante sectie bekijken .
Bekijk de resultaten van de analyzer
Nadat we klaar zijn met het implementeren en configureren van de build, stel ik voor om naar enkele interessante waarschuwingen te kijken die in het onderzochte project zijn gevonden.
Waarschuwing N1
[CWE-401] De uitzondering werd gegooid zonder de 'result' pointer vrij te geven. Een geheugenlek is mogelijk. libopenrct2 ObjectFactory.cpp 443
Object* CreateObjectFromJson(....)
{
Object* result = nullptr;
....
result = CreateObject(entry);
....
if (readContext.WasError())
{
throw std::runtime_error("Object heeft fouten");
}
....
}
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("Ongeldig objecttype");
}
return result;
}De analyzer merkte een fout op, namelijk dat na dynamische geheugenallocatie in CreateObject, bij het optreden van een uitzondering het geheugen niet wordt vrijgegeven, wat resulteert in een geheugenlek.
Waarschuwing N2
Er zijn identieke subexpressies '(1ULL << WIDX_MONTH_BOX)' aan de linker- en rechterkant van de '|' operator. libopenrct2ui Cheats.cpp 487
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),
....
};Bijna niemand, behalve een statische analyser, zou deze test op aandacht kunnen doorstaan. Dit voorbeeld van copy-paste is hier juist goed in.
Waarschuwingen N3
struct RCT12SpriteBase { .... uint8_t flags; .... }; struct rct1_peep : RCT12SpriteBase { .... uint8_t flags; .... };
Natuurlijk is het gebruik van een variabele met dezelfde naam in de basisklasse en in de afgeleide klasse niet altijd een fout. De technologie van overerving zelf gaat echter uit van de aanwezigheid van alle velden van de ouderklasse in de kindklasse. Door in de afgeleide klasse velden met dezelfde naam te declareren, veroorzaak je verwarring.Waarschuwing N4
V793
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....) { if ((imageDirection / 8) && (imageDirection / 8) != 3) { .... } .... }
Laten we het iets gedetailleerder bekijken. De expressieimageDirection / 8 is false als imageDirection tussen -7 en 7 ligt. Het tweede deel: (imageDirection / 8) != 3 controleert of het zich buiten de reeksen bevindt: van -31 tot -24 en van 24 tot 31 respectievelijk. Het lijkt me behoorlijk vreemd om te controleren of getallen binnen een bepaald bereik vallen op deze manier, en zelfs als er in dit fragment van de code geen fout is, zou ik aanraden om deze voorwaarden te herschrijven naar meer expliciete. Dit zou het leven aanzienlijk vereenvoudigen voor de mensen die deze code gaan lezen en onderhouden. tussen -7 en 7 ligt. Het tweede deel: Waarschuwing N5
V587
An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 1115, 1118. libopenrct2ui MouseInput.cpp 1118
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;
....
}
....
}Dit fragment code is waarschijnlijk verkregen door decompilatie. Uit de achtergelaten opmerking blijkt dat een deel van de niet-werkende code is verwijderd. Er zijn echter een paar bewerkingen overgebleven cursorId, die ook niet bijzonder veel betekenis hebben.
Waarschuwing N6
[CWE-476] De 'player' pointer werd onveilig gebruikt nadat deze was geverifieerd tegen nullptr. Controleer regels: 2085, 2094. libopenrct2 Network.cpp 2094
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Voeg nieuwe speler toe.
player = AddPlayer("", "");
if (player) // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
}
newPlayers.push_back(player->Id); // <=
}
....
}Deze code is redelijk eenvoudig te corrigeren, je moet of opnieuw controleren op null pointer, of het in het lichaam van de voorwaarde plaatsen. Ik zou de tweede optie voorstellen: player op null pointer, of het in het lichaam van de voorwaarde plaatsen. Ik zou de tweede optie voorstellen:
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Voeg nieuwe speler toe.
player = AddPlayer("", "");
if (player)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
newPlayers.push_back(player->Id);
}
}
....
}Waarschuwing N7
[CWE-570] Expressie 'name == nullptr' is altijd onwaar. libopenrct2 ServerList.cpp 102
std::optional ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
....
}
else
{
....
entry.name = (name == nullptr ? "" : json_string_value(name));
....
}
....
}Je kunt in één keer de moeilijk leesbare code kwijt en het probleem met de controle op nullptr. Ik stel voor de code als volgt te wijzigen:
std::optional ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
name = ""
....
}
else
{
....
entry.name = json_string_value(name);
....
}
....
}Waarschuwing N8
[CWE-1164] De 'ColumnHeaderPressedCurrentState' variabele is dezelfde waarde toegewezen. libopenrct2ui CustomListView.cpp 510
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}De code ziet er nogal vreemd uit. Het lijkt erop dat er een typefout was, hetzij in de voorwaarde, hetzij bij het opnieuw toewijzen van de variabele ColumnHeaderPressedCurrentState waarden false.
Uitslag
Zoals we zien, is het vrij eenvoudig om de statische analyser PVS-Studio in uw project op TeamCity te integreren. Hiervoor is het voldoende om slechts één klein configuratiebestand te schrijven. De codecontrole maakt het mogelijk om problemen onmiddellijk na de build te identificeren, wat helpt om deze op te lossen wanneer de complexiteit en de kosten van correcties nog laag zijn.
Als je dit artikel met een Engelstalig publiek wilt delen, gebruik dan alstublieft de link naar de vertaling: Vladislav Stolyarov. .
Bron: habr.com
