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 najnovijih scenarija za korištenje PVS-Studio analizatora je njegova integracija sa CI sustavima. I premda se analiza PVS-Studio projekta iz gotovo bilo kojeg kontinuiranog integracijskog sustava može ugraditi u samo nekoliko naredbi, nastavljamo ovaj proces činiti još praktičnijim. PVS-Studio sada ima podršku za pretvaranje izlaza analizatora u format za TeamCity - TeamCity Inspections Type. Pogledajmo kako radi.

Informacije o korištenom softveru

PVS-Studio — statički analizator C, C++, C# i Java koda, osmišljen kako bi olakšao zadatak pronalaženja i ispravljanja različitih vrsta pogrešaka. Analizator se može koristiti u sustavima Windows, Linux i macOS. U ovom ćemo članku aktivno koristiti ne samo sam analizator, već i neke uslužne programe iz njegove distribucije.

CLMonitor — je poslužitelj za nadgledanje koji prati pokretanje kompilatora. Mora se pokrenuti neposredno prije početka izgradnje vašeg projekta. U načinu njuškanja, poslužitelj će presresti izvođenja svih podržanih prevoditelja. Važno je napomenuti da se ovaj uslužni program može koristiti samo za analizu C/C++ projekata.

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

Informacije o projektu koji se proučava

Isprobajmo ovu funkcionalnost na praktičnom primjeru – analizirajmo projekt OpenRCT2.

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 zaraditi i održati dobru reputaciju parka, a da gosti budu zadovoljni. OpenRCT2 vam omogućuje igranje u scenariju i sandboxu. Scenariji zahtijevaju od igrača da izvrši određeni zadatak unutar postavljenog vremena, dok Sandbox omogućuje igraču da izgradi fleksibilniji park bez ikakvih ograničenja ili financija.

podešavanje

Kako bih uštedio vrijeme, vjerojatno ću preskočiti proces instalacije i započeti od trenutka kada imam pokrenut TeamCity server na mom računalu. Moramo otići na: localhost:{port naveden tijekom postupka instalacije} (u mom slučaju, localhost:9090) i unijeti podatke za autorizaciju. Nakon ulaska pozdravit će nas:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Pritisnite gumb Kreiraj projekt. Zatim odaberite Ručno i ispunite polja.

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

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Kliknimo Stvorite konfiguraciju gradnje.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Ispunite polja i kliknite stvoriti. Vidimo prozor koji traži da odaberete sustav kontrole verzija. Budući da se izvori već nalaze lokalno, kliknite Preskočiti.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Konačno, prelazimo na postavke projekta.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Dodajmo korake sastavljanja, za ovo kliknite: Koraci izrade -> Dodaj korak izrade.

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

  • Vrsta trkača -> Naredbeni redak
  • Pokreni -> Prilagođena skripta

Budući da ćemo tijekom kompilacije projekta izvršiti analizu, montaža i analiza trebaju biti jedan korak, stoga ispunite polje Prilagođena skripta:

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, sastavljanje projekta, njegova analiza, ispisivanje izvješća i formatiranje zahtijeva samo jedanaest redaka koda.

Zadnje što trebamo učiniti je postaviti varijable okruženja, za koje sam naveo neke načine za poboljšanje njihove čitljivosti. Da bismo to učinili, idemo dalje: Parametri -> Dodaj novi parametar i dodajte tri varijable:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Sve što trebate učiniti je pritisnuti gumb trčanje u gornjem desnom kutu. Dok se projekt sastavlja i analizira, pričat ću vam o scenariju.

Izravno scenarij

Prvo moramo preuzeti najnoviju PVS-Studio distribuciju. Za to koristimo upravitelj paketa Chocolatey. Za one koji žele znati više o ovome, postoji odgovarajući članak:

choco install pvs-studio -y

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

%CLmon% monitor –-attach

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

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

Unesite prijavu i licencni ključ za PVS-Studio:

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

Nakon završetka izgradnje, ponovno pokrenite CLMonitor za generiranje prethodno obrađenih datoteka i statičku analizu:

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

Zatim ćemo koristiti drugi pomoćni program iz naše distribucije. PlogConverter pretvara izvješće iz standardnog formata u format specifičan za TeamCity. Zahvaljujući tome, moći ćemo ga vidjeti izravno u prozoru za izradu.

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

Zadnji korak je prikazati formatirano izvješće stdout, gdje će ga pokupiti parser TeamCity.

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, montaža i analiza projekta je uspješno završena, možemo prijeći na karticu Projekti i uvjeriti se u ovome.

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Sada kliknimo na Inspekcije Ukupnoza odlazak na pregled izvješća analizatora:

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Upozorenja su grupirana prema brojevima dijagnostičkih pravila. Za navigaciju kodom morate kliknuti na broj retka s upozorenjem. Klikom na upitnik u gornjem desnom kutu otvorit će vam se novi tab s dokumentacijom. Također se možete kretati kroz kod klikom na broj retka s upozorenjem analizatora. Prilikom korištenja moguća je navigacija s udaljenog računala SourceTreeRoot marker. Svatko tko je zainteresiran za ovaj način rada analizatora može se upoznati s odgovarajućim odjeljkom dokumentacija.

Pregled rezultata analizatora

Sada kada smo završili s implementacijom i konfiguracijom izgradnje, pogledajmo neka zanimljiva upozorenja koja se nalaze u projektu koji gledamo.

Upozorenje N1

V773 [CWE-401] Iznimka je bačena bez otpuš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 pogrešku koja je nakon dinamičke dodjele memorije u CreateObject, kada se dogodi iznimka, 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),
  ....
};

Malo je ljudi osim statičkog analizatora moglo proći ovaj test pažnje. Ovaj copy-paste primjer je dobar upravo iz tog razloga.

Upozorenja N3

V703 Čudno je da polje 'flags' u izvedenoj klasi 'RCT12BannerElement' prepisuje polje u osnovnoj klasi 'RCT12TileElementBase'. Provjerite 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 iu potomku nije uvijek pogreška. Međutim, sama tehnologija nasljeđivanja pretpostavlja da su sva polja roditeljske klase prisutna u podređenoj klasi. Proglašavanjem istoimenih polja u nasljedniku unosimo zabunu.

Upozorenje N4

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

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

Pogledajmo pobliže. Izraz imageDirection/8 bit će lažno ako imageDirection je u rasponu od -7 do 7. Drugi dio: (imageDirection / 8) != 3 provjere imageDirection jer su izvan raspona: od -31 do -24 odnosno od 24 do 31. Čini mi se prilično čudnim provjeravati brojeve za uključivanje u određeni raspon na ovaj način i, čak i ako nema pogreške u ovom dijelu koda, preporučio bih da se ti uvjeti ponovno napišu kako bi bili eksplicitniji. To 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;. Provjerite 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 najvjerojatnije je dobiven dekompilacijom. Tada je, sudeći po ostavljenom komentaru, uklonjen dio neradnog koda. No, ostalo je još par operacija cursorId, što također nema previše smisla.

Upozorenje N6

V1004 [CWE-476] Pokazivač 'player' korišten je nesigurno nakon što je provjeren 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 uvjetne naredbe. Ja bih predložio 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' uvijek je 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 čitljivog retka koda jednim potezom i riješiti problem s provjerom nullptr. Predlažem promjenu koda 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 tipfelera ili u uvjetu ili kod ponovnog dodjeljivanja varijable ColumnHeaderPressedCurrentState smisao lažan.

Izlaz

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

PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Ako želite podijeliti ovaj članak s publikom koja govori engleski, upotrijebite vezu za prijevod: Vladislav Stolyarov. PVS-Studio i kontinuirana integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2.

Izvor: www.habr.com

Dodajte komentar