Yksi PVS-Studio-analysaattorin uusimmista käyttöskenaarioista on sen integrointi CI-järjestelmiin. Ja vaikka PVS-Studio-projektin analyysi lähes mistä tahansa jatkuvasta integraatiojärjestelmästä voidaan rakentaa vain muutamaan komentoon, teemme tästä prosessista entistäkin mukavampaa. PVS-Studio tukee nyt analysaattorin tulosteen muuntamista TeamCity - TeamCity Inspections Type -muotoon. Katsotaan kuinka se toimii.
Tietoja käytetystä ohjelmistosta
Tietoja tutkittavasta hankkeesta
Kokeillaan tätä toimintoa käytännön esimerkissä - analysoidaan OpenRCT2-projektia.
säätö
Ajan säästämiseksi jätän todennäköisesti asennuksen väliin ja aloitan siitä hetkestä, kun TeamCity-palvelin on käynnissä tietokoneellani. Meidän on mentävä osoitteeseen localhost:{portti määritetty asennuksen aikana} (minun tapauksessani localhost:9090) ja syötettävä valtuutustiedot. Sisääntulon jälkeen meitä tervehtivät:
Napsauta Luo projekti -painiketta. Valitse seuraavaksi Manuaalisesti ja täytä kentät.
Painikkeen painamisen jälkeen luoda, meitä tervehtii ikkuna, jossa on asetukset.
Napsautetaan Luo koontikokoonpano.
Täytä kentät ja napsauta luoda. Näemme ikkunan, jossa sinua pyydetään valitsemaan versionhallintajärjestelmä. Koska lähteet sijaitsevat jo paikallisesti, napsauta hyppiä.
Lopuksi siirrymme projektin asetuksiin.
Lisätään kokoonpanovaiheet tehdäksesi tämän napsauttamalla: Rakennusvaiheet -> Lisää rakennusvaihe.
Tästä valitsemme:
- Runner type -> Command Line
- Suorita -> Mukautettu komentosarja
Koska suoritamme analyysin projektin kokoamisen aikana, kokoamisen ja analysoinnin tulisi olla yksi vaihe, joten täytä kenttä Muokattu komentosarja:
Tarkastelemme yksittäisiä vaiheita myöhemmin. On tärkeää, että analysaattorin lataaminen, projektin kokoaminen, analysointi, raportin tulostaminen ja sen muotoilu vie vain yksitoista koodiriviä.
Viimeinen asia, joka meidän on tehtävä, on asettaa ympäristömuuttujat, joita olen hahmotellut joitakin tapoja parantaa niiden luettavuutta. Tätä varten siirrytään eteenpäin: Parametrit -> Lisää uusi parametri ja lisää kolme muuttujaa:
Sinun tarvitsee vain painaa painiketta ajaa oikeassa yläkulmassa. Kun projektia kootaan ja analysoidaan, kerron sinulle käsikirjoituksesta.
Käsikirjoitus suoraan
Ensin meidän on ladattava uusin PVS-Studio-jakelu. Tätä varten käytämme Chocolatey-pakettienhallintaa. Niille, jotka haluavat tietää tästä lisää, on vastaava
choco install pvs-studio -y
Seuraavaksi käynnistetään CLMonitor-projektin rakentamisen seurantaapuohjelma.
%CLmon% monitor –-attach
Sitten rakennamme projektin ympäristömuuttujaksi MSB on polku MSBuild-versioon, joka minun on rakennettava
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Syötetään PVS-Studion kirjautumis- ja lisenssiavain:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Kun koonti on valmis, suorita CLMonitor uudelleen esikäsiteltyjen tiedostojen ja staattisen analyysin luomiseksi:
%CLmon% analyze -l "c:ptest.plog"
Sitten käytämme toista apuohjelmaa jakelustamme. PlogConverter muuntaa raportin vakiomuodosta TeamCity-kohtaiseen muotoon. Tämän ansiosta voimme tarkastella sitä suoraan rakennusikkunassa.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Viimeinen vaihe on muotoillun raportin näyttäminen stdout, josta TeamCityn jäsentäjä poimii sen.
type "C:tempptest.plog_TeamCity.txt"
Koko skriptikoodi:
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"
Sillä välin projektin kokoonpano ja analysointi on saatu päätökseen onnistuneesti, voimme siirtyä välilehdelle Projektit ja varmista se.
Nyt klikataan Tarkastukset yhteensävoit siirtyä tarkastelemaan analysaattoriraporttia:
Varoitukset on ryhmitelty diagnostisten sääntöjen numeroiden mukaan. Voit selata koodia napsauttamalla varoituksen sisältävää rivinumeroa. Napsauttamalla oikeassa yläkulmassa olevaa kysymysmerkkiä saat uuden välilehden dokumentaatiolla. Voit myös selata koodia napsauttamalla rivinumeroa, jossa on analysaattorin varoitus. Navigointi etätietokoneelta on mahdollista käytettäessä LähdeTreeRoot merkki. Jokainen, joka on kiinnostunut tästä analysaattorin toimintatavasta, voi tutustua vastaavaan osaan
Analysaattorin tulosten tarkasteleminen
Nyt kun olemme ottaneet käyttöön ja määrittäneet koontiversion, katsotaanpa joitain mielenkiintoisia varoituksia, jotka löytyvät tarkastelemastamme projektista.
Varoitus 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;
}
Analysaattori havaitsi virheen, joka tapahtui muistin dynaamisen varauksen jälkeen LuoObject, kun poikkeus tapahtuu, muistia ei tyhjennetä ja tapahtuu muistivuoto.
Varoitus 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),
....
};
Harvat muut kuin staattinen analysaattori pystyivät läpäisemään tämän tarkkaavaisuustestin. Tämä copy-paste-esimerkki on hyvä juuri tästä syystä.
Varoitukset N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Samannimisen muuttujan käyttäminen perusluokassa ja jälkeläisessä ei tietenkään aina ole virhe. Periytystekniikka itsessään olettaa kuitenkin, että kaikki emoluokan kentät ovat läsnä lapsiluokassa. Ilmoittamalla kentät, joilla on sama nimi perillisessä, luomme sekaannusta.
Varoitus N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Katsotaanpa tarkemmin. Ilmaisu imageDirection/8 on väärä, jos imageDirection on välillä -7 - 7. Toinen osa: (imageDirection / 8) != 3 tarkastukset imageDirection alueen ulkopuolella: -31 - -24 ja 24 - 31, vastaavasti. Minusta tuntuu melko oudolta tarkistaa tällä tavalla numeroiden sisällyttäminen tietylle alueelle, ja vaikka tässä koodinpätkässä ei olisi virhettä, suosittelen näiden ehtojen uudelleenkirjoittamista selvemmiksi. Tämä helpottaisi huomattavasti tätä koodia lukevien ja ylläpitävien ihmisten elämää.
Varoitus 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;
....
}
....
}
Tämä koodifragmentti on todennäköisesti saatu purkamalla. Sitten jätetystä kommentista päätellen osa toimimattomasta koodista poistettiin. Pari operaatiota on kuitenkin vielä jäljellä cursorId, joissa ei myöskään ole paljon järkeä.
Varoitus 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); // <=
}
....
}
Tämä koodi on melko helppo korjata; sinun tarvitsee vain tarkistaa se kolmannen kerran soitin nollaosoittimeen tai lisää se ehdollisen lauseen runkoon. Suosittelen toista vaihtoehtoa:
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);
}
}
....
}
Varoitus 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));
....
}
....
}
Voit päästä eroon vaikeasti luettavasta koodirivistä yhdellä iskulla ja ratkaista ongelman tarkistamalla nullptr. Suosittelen vaihtamaan koodin seuraavasti:
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);
....
}
....
}
Varoitus N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Koodi näyttää aika oudolta. Minusta vaikuttaa siltä, että joko ehdossa tai muuttujaa uudelleen määritettäessä oli kirjoitusvirhe ColumnHeaderPressedCurrentState merkitys väärä.
johtopäätös
Kuten näemme, PVS-Studio-staattisen analysaattorin integrointi TeamCity-projektiisi on melko yksinkertaista. Tätä varten riittää, että kirjoitat vain yhden pienen määritystiedoston. Tarkistamalla koodin voit tunnistaa ongelmat heti kokoonpanon jälkeen, mikä auttaa poistamaan ne, kun muutosten monimutkaisuus ja kustannukset ovat edelleen alhaiset.
Jos haluat jakaa tämän artikkelin englanninkielisen yleisön kanssa, käytä käännöslinkkiä: Vladislav Stolyarov.
Lähde: will.com