PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Jedan od najaktuelnijih scenarija za korišćenje PVS-Studio analizatora je njegova integracija sa CI sistemima. I iako se analiza PVS-Studio projekta iz gotovo svakog sistema kontinuirane integracije može ugraditi u samo nekoliko naredbi, mi nastavljamo da ovaj proces činimo još praktičnijim. PVS-Studio sada ima podršku za pretvaranje izlaza analizatora u format za TeamCity - TeamCity Inspections Type. Hajde da vidimo kako to radi.

Informacije o korištenom softveru

PVS Studio — statički analizator C, C++, C# i Java koda, dizajniran da olakša zadatak pronalaženja i ispravljanja različitih vrsta grešaka. Analizator se može koristiti na Windows, Linux i macOS. U ovom članku ćemo aktivno koristiti ne samo sam analizator, već i neke uslužne programe iz njegove distribucije.

CLMonitor — je server za nadgledanje koji prati pokretanje kompajlera. Mora se pokrenuti neposredno prije početka izgradnje vašeg projekta. U režimu njuškanja, server će presresti izvođenje svih podržanih kompajlera. Vrijedi napomenuti da se ovaj uslužni program može koristiti samo za analizu C/C++ projekata.

PlogConverter – uslužni program za pretvaranje izvještaja analizatora u različite formate.

Informacije o projektu koji se proučava

Isprobajmo ovu funkcionalnost na praktičnom primjeru - analizirajmo OpenRCT2 projekat.

OpenRCT2 - otvorena implementacija igre RollerCoaster Tycoon 2 (RCT2), proširujući je novim funkcijama i ispravljajući greške. Igra se vrti oko izgradnje i održavanja zabavnog parka koji sadrži vožnje, trgovine i objekte. Igrač mora pokušati ostvariti profit i održati dobru reputaciju parka, a da gosti budu zadovoljni. OpenRCT2 vam omogućava da igrate iu scenariju iu sandboxu. Scenariji zahtijevaju od igrača da završi određeni zadatak u zadanom vremenu, dok Sandbox omogućava igraču da izgradi fleksibilniji park bez ikakvih ograničenja ili finansija.

podešavanje

Da bih uštedio vrijeme, vjerovatno ću preskočiti proces instalacije i početi od trenutka kada na svom računaru bude pokrenut TeamCity server. Moramo otići na: localhost:{port specificiran tokom procesa instalacije} (u mom slučaju, localhost:9090) i unijeti autorizacijske podatke. Nakon ulaska dočekaće nas:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Kliknite na dugme Kreiraj projekat. Zatim odaberite Ručno i popunite polja.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Nakon pritiska na dugme stvoriti, dočeka nas prozor sa postavkama.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Hajde da kliknemo Kreirajte konfiguraciju izgradnje.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Popunite polja i kliknite stvoriti. Vidimo prozor u kojem se traži da odaberete sistem kontrole verzija. Pošto se izvori već nalaze lokalno, kliknite preskočiti.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Na kraju prelazimo na postavke projekta.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Dodajmo korake sastavljanja, da biste to uradili kliknite: Koraci izgradnje -> Dodaj korak izgradnje.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Ovdje biramo:

  • Tip pokretača -> Komandna linija
  • Pokreni -> Prilagođena skripta

Budući da ćemo analizu vršiti tokom kompilacije projekta, montaža i analiza treba da budu jedan korak, pa popunite polje Prilagođeni scenario:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Kasnije ćemo pogledati pojedinačne korake. Važno je da učitavanje analizatora, sklapanje projekta, njegova analiza, izlaz izvještaja i formatiranje traje samo jedanaest linija koda.

Poslednja stvar koju treba da uradimo je da postavimo varijable okruženja, koje sam naveo neke načine da poboljšamo njihovu čitljivost. Da bismo to uradili, idemo dalje: Parametri -> Dodaj novi parametar i dodajte tri varijable:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Sve što treba da uradite je da pritisnete dugme trčanje u gornjem desnom uglu. Dok se projekat sklapa i analizira, pričat ću vam o scenariju.

Direktno skripta

Prvo, moramo preuzeti najnoviju PVS-Studio distribuciju. Za to koristimo Chocolatey paket menadžer. Za one koji žele da saznaju više o ovome, postoji dopisnik članak:

choco install pvs-studio -y

Zatim, pokrenimo uslužni program za praćenje izgradnje projekta CLMonitor.

%CLmon% monitor –-attach

Zatim ćemo izgraditi projekat kao varijablu okruženja MSB je put do verzije MSBuild-a koju trebam napraviti

%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable

Unesite login i licencni ključ za PVS-Studio:

%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%

Nakon što je gradnja završena, ponovo pokrenite CLMonitor da generišete prethodno obrađene datoteke i statičku analizu:

%CLmon% analyze -l "c:ptest.plog"

Zatim ćemo koristiti drugi uslužni program iz naše distribucije. PlogConverter konvertuje izveštaj iz standardnog formata u format specifičan za TeamCity. Zahvaljujući tome, moći ćemo je vidjeti direktno u prozoru za izgradnju.

%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"

Posljednji korak je da prikažete formatirani izvještaj stdout, gdje će ga pokupiti TeamCity parser.

type "C:tempptest.plog_TeamCity.txt"

Cijeli kod skripte:

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"

U međuvremenu je montaža i analiza projekta uspješno završena, možemo prijeći na karticu projekata i ubeditʹsâ v étom.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Sada kliknimo Pregledi Totalda pređete na pregled izvještaja analizatora:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Upozorenja su grupirana prema brojevima dijagnostičkih pravila. Da biste se kretali kroz kod, morate kliknuti na broj reda s upozorenjem. Klikom na upitnik u gornjem desnom kutu otvara se nova kartica sa dokumentacijom. Možete se kretati i kroz kod klikom na broj reda s upozorenjem analizatora. Navigacija sa udaljenog računara je moguća kada se koristi SourceTreeRoot marker. Svi koji su zainteresovani za ovaj način rada analizatora mogu se upoznati sa odgovarajućim odeljkom dokumentaciju.

Pregled rezultata analizatora

Sada kada smo završili sa implementacijom i konfiguracijom build-a, pogledajmo neka zanimljiva upozorenja koja se nalaze u projektu koji gledamo.

Upozorenje N1

V773 [CWE-401] Izuzetak je izbačen bez puštanja pokazivača 'rezultata'. Moguće je curenje memorije. libopenrct2 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;
}

Analizator je uočio grešku koja nakon dinamičke dodjele memorije u CreateObject, kada se dogodi izuzetak, memorija se ne briše i dolazi do curenja memorije.

Upozorenje N2

V501 Postoje identični podizrazi '(1ULL << WIDX_MONTH_BOX)' lijevo i desno od '|' operater. 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),
  ....
};

Nekoliko ljudi osim statičkog analizatora moglo bi proći ovaj test pažnje. Ovaj primjer kopiranja i lijepljenja dobar je upravo iz tog razloga.

Upozorenja N3

V703 Čudno je da polje 'flags' u izvedenoj klasi 'RCT12BannerElement' prepisuje polje u osnovnoj klasi 'RCT12TileElementBase'. Kontrolne linije: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

struct RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};
struct rct1_peep : RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};

Naravno, korištenje varijable s istim imenom u osnovnoj klasi i u potomku nije uvijek greška. Međutim, sama tehnologija nasljeđivanja pretpostavlja da su sva polja roditeljske klase prisutna u podređenoj klasi. Deklarisanjem polja sa istim imenom u nasledniku stvaramo zabunu.

Upozorenje N4

V793 Čudno je da je rezultat naredbe 'imageDirection / 8' dio uvjeta. Možda je ovu izjavu trebalo uporediti sa nečim drugim. libopenrct2 ObservationTower.cpp 38

void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
  if ((imageDirection / 8) && (imageDirection / 8) != 3)
  {
    ....
  }
  ....
}

Pogledajmo izbliza. Izraz imageDirection/8 biće lažno ako imageDirection je u rasponu od -7 do 7. Drugi dio: (imageDirection / 8) != 3 čekovi imageDirection za izvan opsega: od -31 do -24 i od 24 do 31, respektivno. Čini mi se prilično čudnim da na ovaj način provjeravam brojeve da li su uključeni u određeni raspon i, čak i ako nema greške u ovom dijelu koda, preporučio bih prepisivanje ovih uvjeta kako bi bili eksplicitniji. Ovo bi znatno olakšalo život ljudima koji bi čitali i održavali ovaj kod.

Upozorenje N5

V587 Neparan niz zadataka ove vrste: A = B; B = A;. Kontrolne linije: 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;
      ....
  }
  ....
}

Ovaj fragment koda je najvjerovatnije dobijen dekompilacijom. Tada je, sudeći po ostavljenom komentaru, uklonjen dio nefunkcionalnog koda. Međutim, ostalo je još nekoliko operacija cursorId, što takođe nema mnogo smisla.

Upozorenje N6

V1004 [CWE-476] Pokazivač 'player' korišten je nesigurno nakon što je verifikovan u odnosu na nullptr. Provjerite linije: 2085, 2094. libopenrct2 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);                    // <=
  }
  ....
}

Ovaj kod je prilično lako ispraviti; samo ga trebate provjeriti treći put igrač na nulti pokazivač ili ga dodajte u tijelo uslovne izjave. Predlažem drugu opciju:

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);
    }
  }
  ....
}

Upozorenje N7

V547 [CWE-570] Izraz 'name == nullptr' je uvijek lažan. libopenrct2 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));
    ....
  }
  ....
}

Možete se riješiti teško čitljive linije koda jednim potezom i riješiti problem provjerom nullptr. Predlažem da promijenite kod na sljedeći način:

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);
    ....
  }
  ....
}

Upozorenje N8

V1048 [CWE-1164] Varijabli 'ColumnHeaderPressedCurrentState' dodijeljena je ista vrijednost. libopenrct2ui CustomListView.cpp 510

void CustomListView::MouseUp(....)
{
  ....
  if (!ColumnHeaderPressedCurrentState)
  {
    ColumnHeaderPressed = std::nullopt;
    ColumnHeaderPressedCurrentState = false;
    Invalidate();
  }
}

Kod izgleda prilično čudno. Čini mi se da je došlo do greške u kucanju ili u uslovu ili pri ponovnom dodeljivanju varijable ColumnHeaderPressedCurrentState vrijednosti lažan.

zaključak

Kao što vidimo, integracija statičkog analizatora PVS-Studio u vaš TeamCity projekat je prilično jednostavna. Da biste to učinili, dovoljno je napisati samo jednu malu konfiguracijsku datoteku. Provjera koda će vam omogućiti da identificirate probleme odmah nakon sastavljanja, što će vam pomoći da ih eliminišete kada su složenost i cijena promjena još uvijek niska.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Ako želite da podelite ovaj članak sa publikom koja govori engleski, koristite link za prevod: Vladislav Stolyarov. PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2.

izvor: www.habr.com

Dodajte komentar