PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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

PVS-studio — C-, C++-, C#- ja Java-koodin staattinen analysaattori, joka on suunniteltu helpottamaan erityyppisten virheiden etsimistä ja korjaamista. Analysaattoria voidaan käyttää Windowsissa, Linuxissa ja macOS:ssä. Tässä artikkelissa käytämme aktiivisesti paitsi itse analysaattoria, myös joitain apuohjelmia sen jakelusta.

CLMonitor — on valvontapalvelin, joka valvoo kääntäjien käynnistystä. Se on suoritettava välittömästi ennen projektin rakentamisen aloittamista. Snooping-tilassa palvelin sieppaa kaikkien tuettujen kääntäjien ajot. On syytä huomata, että tätä apuohjelmaa voidaan käyttää vain C/C++-projektien analysointiin.

PlogConverter – apuohjelma analysaattoriraporttien muuntamiseen eri muotoihin.

Tietoja tutkittavasta hankkeesta

Kokeillaan tätä toimintoa käytännön esimerkissä - analysoidaan OpenRCT2-projektia.

OpenRCT2 - pelin RollerCoaster Tycoon 2 (RCT2) avoin toteutus, joka laajentaa sitä uusilla toiminnoilla ja korjaa vikoja. Peli pyörii huvipuiston rakentamisessa ja ylläpidossa, jossa on laitteita, kauppoja ja tiloja. Pelaajan on yritettävä tehdä voittoa ja ylläpitää puiston hyvää mainetta pitäen samalla vieraat tyytyväisinä. OpenRCT2 mahdollistaa pelaamisen sekä skenaariossa että hiekkalaatikossa. Skenaariot edellyttävät, että pelaaja suorittaa tietyn tehtävän tietyssä ajassa, kun taas Sandboxin avulla pelaaja voi rakentaa joustavamman puiston ilman rajoituksia tai rahoitusta.

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:

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Napsauta Luo projekti -painiketta. Valitse seuraavaksi Manuaalisesti ja täytä kentät.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Painikkeen painamisen jälkeen luoda, meitä tervehtii ikkuna, jossa on asetukset.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Napsautetaan Luo koontikokoonpano.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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ä.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Lopuksi siirrymme projektin asetuksiin.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Lisätään kokoonpanovaiheet tehdäksesi tämän napsauttamalla: Rakennusvaiheet -> Lisää rakennusvaihe.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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:

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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:

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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 artikkeli:

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.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Nyt klikataan Tarkastukset yhteensävoit siirtyä tarkastelemaan analysaattoriraporttia:

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
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 dokumentointi.

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

V773 [CWE-401] Poikkeus tehtiin vapauttamatta 'tulos'-osoitinta. Muistivuoto on mahdollinen. libopenct2 ObjectFactory.cpp 443

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

V501 Kohteen '|' vasemmalla ja oikealla puolella on identtiset alilausekkeet '(1ULL << WIDX_MONTH_BOX)'. operaattori. libopenct2ui 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),
  ....
};

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

V703 On outoa, että johdetun luokan 'RCT12BannerElement' liput-kenttä korvaa perusluokan 'RCT12TileElementBase' kentän. Tarkistusrivit: RCT12.h:570, RCT12.h:259. libopenct2 RCT12.h 570

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

V793 On outoa, että 'imageDirection / 8' -lauseen tulos on osa ehtoa. Ehkä tätä väitettä olisi pitänyt verrata johonkin muuhun. libopenct2 ObservationTower.cpp 38

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

V587 Pariton tämän tyyppinen tehtäväsarja: A = B; B = A;. Tarkistusrivit: 1115, 1118. libopenct2ui 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;
      ....
  }
  ....
}

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

V1004 [CWE-476] Soittimen osoitinta käytettiin epäturvallisesti sen jälkeen, kun se varmistettiin nullptr:tä vastaan. Tarkistusrivit: 2085, 2094. libopenct2 Network.cpp 2094

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

V547 [CWE-570] Lauseke 'name == nullptr' on aina epätosi. libopenct2 ServerList.cpp 102

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

V1048 [CWE-1164] ColumnHeaderPressedCurrentState-muuttujalle on määritetty sama arvo. libopenct2ui CustomListView.cpp 510

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.

PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi
Jos haluat jakaa tämän artikkelin englanninkielisen yleisön kanssa, käytä käännöslinkkiä: Vladislav Stolyarov. PVS-Studio ja jatkuva integrointi: TeamCity. Open RollerCoaster Tycoon 2 -projektin analyysi.

Lähde: will.com

Lisää kommentti