Et av de mest aktuelle scenariene for bruk av PVS-Studio-analysatoren er dens integrasjon med CI-systemer. Og selv om analysen av et PVS-Studio-prosjekt fra nesten alle kontinuerlige integreringssystem kan bygges inn i bare noen få kommandoer, fortsetter vi å gjøre denne prosessen enda mer praktisk. PVS-Studio har nå støtte for å konvertere analysatorutdata til et format for TeamCity - TeamCity Inspections Type. La oss se hvordan det fungerer.
Informasjon om programvaren som brukes
Informasjon om prosjektet som studeres
La oss prøve denne funksjonaliteten med et praktisk eksempel - la oss analysere OpenRCT2-prosjektet.
justering
For å spare tid vil jeg sannsynligvis hoppe over installasjonsprosessen og starte fra det øyeblikket jeg har TeamCity-serveren kjørende på datamaskinen min. Vi må gå til: localhost:{port spesifisert under installasjonsprosessen} (i mitt tilfelle, localhost:9090) og angi autorisasjonsdata. Etter innreise vil vi bli møtt av:
Klikk på knappen Opprett prosjekt. Deretter velger du Manuelt og fyller ut feltene.
Etter å ha trykket på knappen Opprett, blir vi møtt av et vindu med innstillinger.
La oss klikke Lag byggekonfigurasjon.
Fyll ut feltene og klikk Opprett. Vi ser et vindu som ber deg velge et versjonskontrollsystem. Siden kildene allerede er lokalisert, klikk Hopp.
Til slutt går vi videre til prosjektinnstillingene.
La oss legge til monteringstrinn, for å gjøre dette klikk: Byggtrinn -> Legg til byggetrinn.
Her velger vi:
- Løpertype -> Kommandolinje
- Kjør -> Egendefinert skript
Siden vi skal utføre analyser under prosjektsammenstilling, bør montering og analyse være ett trinn, så fyll ut feltet Tilpasset skript:
Vi vil se på de enkelte trinnene senere. Det er viktig at det bare tar elleve linjer med kode å laste analysatoren, sette sammen prosjektet, analysere den, skrive ut rapporten og formatere den.
Det siste vi må gjøre er å angi miljøvariablene, som jeg har skissert noen måter å forbedre lesbarheten på. For å gjøre dette, la oss gå videre: Parametere -> Legg til ny parameter og legg til tre variabler:
Alt du trenger å gjøre er å trykke på knappen Kjør i øvre høyre hjørne. Mens prosjektet blir satt sammen og analysert, vil jeg fortelle deg om manuset.
Manus direkte
Først må vi laste ned den nyeste PVS-Studio-distribusjonen. Til dette bruker vi Chocolatey package manager. For de som ønsker å vite mer om dette, finnes det tilsvarende
choco install pvs-studio -y
La oss deretter lansere CLMonitor-prosjektbyggsporingsverktøyet.
%CLmon% monitor –-attach
Deretter skal vi bygge prosjektet som en miljøvariabel MSB er banen til versjonen av MSBuild jeg må bygge
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
La oss skrive inn påloggings- og lisensnøkkelen for PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Etter at byggingen er fullført, kjør CLMonitor igjen for å generere forhåndsbehandlede filer og statisk analyse:
%CLmon% analyze -l "c:ptest.plog"
Da vil vi bruke et annet verktøy fra vår distribusjon. PlogConverter konverterer en rapport fra et standardformat til et TeamCity-spesifikt format. Takket være dette vil vi kunne se det direkte i byggevinduet.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Det siste trinnet er å vise den formaterte rapporten i stdout, hvor den vil bli plukket opp av TeamCity-parseren.
type "C:tempptest.plog_TeamCity.txt"
Full skriptkode:
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 mellomtiden er monteringen og analysen av prosjektet fullført, vi kan gå til fanen Prosjekter og sørg for det.
La oss nå klikke videre Inspeksjoner Totaltfor å gå til å vise analysatorrapporten:
Advarsler er gruppert etter diagnostiske regelnumre. For å navigere gjennom koden, må du klikke på linjenummeret med advarselen. Ved å klikke på spørsmålstegnet i øvre høyre hjørne åpner du en ny fane med dokumentasjon. Du kan også navigere gjennom koden ved å klikke på linjenummeret med analysatoradvarselen. Navigering fra en ekstern datamaskin er mulig når du bruker SourceTreeRoot markør. Alle som er interessert i denne driftsmodusen til analysatoren kan gjøre seg kjent med den tilsvarende delen
Viser analysatorens resultater
Nå som vi er ferdige med å distribuere og konfigurere bygget, la oss ta en titt på noen interessante advarsler i prosjektet vi ser 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 la merke til en feil som etter dynamisk allokering av minne CreateObject, når et unntak oppstår, slettes ikke minnet, og det oppstår en minnelekkasje.
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),
....
};
Få andre enn en statisk analysator kunne bestå denne oppmerksomhetstesten. Dette copy-paste-eksemplet er bra av nettopp denne grunnen.
Advarsler N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Å bruke en variabel med samme navn i grunnklassen og i etterkommeren er selvfølgelig ikke alltid en feil. Imidlertid forutsetter arveteknologien selv at alle feltene til foreldreklassen er til stede i barneklassen. Ved å deklarere felt med samme navn i arvingen skaper vi forvirring.
Advarsel N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
La oss ta en nærmere titt. Uttrykk imageDirection/8 vil være falsk hvis imageDirection er i området fra -7 til 7. Andre del: (imageDirection / 8) != 3 sjekker imageDirection for å være utenfor området: henholdsvis fra -31 til -24 og fra 24 til 31. Det virker ganske rart for meg å sjekke tall for inkludering i et bestemt område på denne måten, og selv om det ikke er noen feil i denne kodebiten, vil jeg anbefale å omskrive disse betingelsene for å være mer eksplisitte. Dette ville gjøre livet mye enklere for folk som ville lese og vedlikeholde denne koden.
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 kodefragmentet ble mest sannsynlig oppnådd ved dekompilering. Deretter ble en del av den ikke-fungerende koden fjernet etter kommentaren som ble lagt igjen. Det gjenstår imidlertid fortsatt et par operasjoner cursorId, som heller ikke gir mye 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 koden er ganske enkel å rette; du trenger bare å sjekke den en tredje gang spiller til en null-peker, eller legg den til i hoveddelen av den betingede setningen. Jeg vil foreslå det andre alternativet:
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 kvitte deg med en vanskelig å lese kodelinje med ett slag og løse problemet med å sjekke for nullptr. Jeg foreslår at du endrer 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 ganske merkelig ut. Det ser ut til at det var en skrivefeil enten i tilstanden eller ved omtildeling av variabelen ColumnHeaderPressedCurrentState som betyr falsk.
Utgang
Som vi kan se, er det ganske enkelt å integrere den statiske PVS-Studio-analysatoren i TeamCity-prosjektet ditt. For å gjøre dette er det nok å skrive bare en liten konfigurasjonsfil. Ved å sjekke koden kan du identifisere problemer umiddelbart etter montering, noe som vil bidra til å eliminere dem når kompleksiteten og kostnadene ved endringer fortsatt er lave.
Hvis du vil dele denne artikkelen med et engelsktalende publikum, vennligst bruk oversettelseslenken: Vladislav Stolyarov.
Kilde: www.habr.com