PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Eden najbolj aktualnih scenarijev za uporabo analizatorja PVS-Studio je njegova integracija s sistemi CI. In čeprav je analizo projekta PVS-Studio iz skoraj katerega koli neprekinjenega integracijskega sistema mogoče vgraditi v le nekaj ukazov, še naprej delamo ta proces še bolj priročen. PVS-Studio ima zdaj podporo za pretvorbo izhoda analizatorja v format za TeamCity - TeamCity Inspections Type. Poglejmo, kako deluje.

Informacije o uporabljeni programski opremi

PVS-Studio — statični analizator kode C, C++, C# in Java, zasnovan za olajšanje iskanja in popravljanja različnih vrst napak. Analizator se lahko uporablja v sistemih Windows, Linux in macOS. V tem članku bomo aktivno uporabljali ne le sam analizator, temveč tudi nekatere pripomočke iz njegove distribucije.

CLMonitor — je nadzorni strežnik, ki spremlja zagone prevajalnika. Zagnati ga je treba tik pred začetkom gradnje vašega projekta. V načinu vohljanja bo strežnik prestregel zagone vseh podprtih prevajalnikov. Omeniti velja, da se ta pripomoček lahko uporablja samo za analizo projektov C/C++.

PlogConverter – pripomoček za pretvorbo poročil analizatorja v različne formate.

Informacije o proučevanem projektu

Preizkusimo to funkcionalnost na praktičnem primeru – analizirajmo projekt OpenRCT2.

OpenRCT2 - odprta izvedba igre RollerCoaster Tycoon 2 (RCT2), ki jo razširja z novimi funkcijami in odpravlja napake. Igranje se vrti okoli gradnje in vzdrževanja zabaviščnega parka, ki vsebuje vožnje, trgovine in objekte. Igralec mora poskušati ustvariti dobiček in ohraniti dober ugled parka, hkrati pa zadovoljiti goste. OpenRCT2 vam omogoča igranje v scenariju in peskovniku. Scenariji zahtevajo, da igralec opravi določeno nalogo v določenem času, medtem ko peskovnik omogoča, da zgradi bolj prilagodljiv park brez kakršnih koli omejitev ali financ.

prilagoditev

Da bi prihranil čas, bom verjetno preskočil postopek namestitve in začel od trenutka, ko bo strežnik TeamCity deloval na mojem računalniku. Moramo iti na: localhost:{vrata, določena med postopkom namestitve} (v mojem primeru localhost:9090) in vnesti avtorizacijske podatke. Po vstopu nas bodo pozdravili:

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Kliknite na gumb Ustvari projekt. Nato izberite Ročno in izpolnite polja.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Po pritisku na gumb ustvarjanje, nas pozdravi okno z nastavitvami.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Kliknimo Ustvari konfiguracijo gradnje.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Izpolnite polja in kliknite ustvarjanje. Vidimo okno, ki vas prosi, da izberete sistem za nadzor različic. Ker se viri že nahajajo lokalno, kliknite Preskoči.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Na koncu preidemo na nastavitve projekta.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Dodajmo korake sestavljanja, za to kliknite: Koraki gradnje -> Dodaj korak gradnje.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Tukaj izberemo:

  • Vrsta tekača -> Ukazna vrstica
  • Zaženi -> Skript po meri

Ker bomo analizo izvajali med sestavljanjem projekta, mora biti sestavljanje in analiza en korak, zato izpolnite polje Skript po meri:

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Posamezne korake si bomo ogledali kasneje. Pomembno je, da nalaganje analizatorja, sestavljanje projekta, njegova analiza, izpis poročila in oblikovanje zahtevajo samo enajst vrstic kode.

Zadnja stvar, ki jo moramo narediti, je, da nastavimo spremenljivke okolja, za katere sem orisal nekaj načinov za izboljšanje njihove berljivosti. Če želite to narediti, pojdimo naprej: Parametri -> Dodaj nov parameter in dodajte tri spremenljivke:

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Vse kar morate storiti je, da pritisnete gumb Run v zgornjem desnem kotu. Medtem ko se projekt sestavlja in analizira, vam bom povedal o scenariju.

Neposredno scenarij

Najprej moramo prenesti najnovejšo distribucijo PVS-Studio. Za to uporabljamo upravitelja paketov Chocolatey. Za tiste, ki želijo izvedeti več o tem, obstaja ustrezen članek:

choco install pvs-studio -y

Nato zaženimo pripomoček za sledenje gradnji projekta CLMonitor.

%CLmon% monitor –-attach

Nato bomo zgradili projekt kot spremenljivko okolja MSB je pot do različice MSBuild, ki jo moram zgraditi

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

Vnesemo prijavo in licenčni ključ za PVS-Studio:

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

Ko je gradnja končana, znova zaženite CLMonitor, da ustvarite vnaprej obdelane datoteke in statično analizo:

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

Nato bomo uporabili drug pripomoček iz naše distribucije. PlogConverter pretvori poročilo iz standardne oblike v obliko, specifično za TeamCity. Zahvaljujoč temu si ga bomo lahko ogledali neposredno v oknu gradnje.

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

Zadnji korak je prikaz oblikovanega poročila v stdout, kjer ga bo pobral razčlenjevalnik TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Celotna koda skripta:

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"

V tem času je bila montaža in analiza projekta uspešno zaključena, gremo lahko na zavihek projekti in prepričati se v tem.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Zdaj pa kliknimo na Pregledi Skupajza ogled poročila analizatorja:

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Opozorila so razvrščena po številkah diagnostičnih pravil. Za krmarjenje po kodi morate klikniti številko vrstice z opozorilom. S klikom na vprašaj v zgornjem desnem kotu se vam odpre nov zavihek z dokumentacijo. Po kodi se lahko pomikate tudi s klikom na številko vrstice z opozorilom analizatorja. Pri uporabi je možna navigacija z oddaljenega računalnika SourceTreeRoot marker. Vsi, ki jih zanima ta način delovanja analizatorja, se lahko seznanijo z ustreznim razdelkom dokumentacijo.

Ogled rezultatov analizatorja

Zdaj, ko smo končali z uvajanjem in konfiguriranjem gradnje, si poglejmo nekaj zanimivih opozoril, ki jih najdemo v projektu, ki ga gledamo.

Opozorilo N1

V773 [CWE-401] Izjema je bila vržena brez sprostitve kazalca 'result'. Možno je puščanje spomina. 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 opazil napako, ki je po dinamični dodelitvi pomnilnika v CreateObject, ko pride do izjeme, se pomnilnik ne izbriše in pride do puščanja pomnilnika.

Opozorilo N2

V501 Obstajajo enaki podizrazi '(1ULL << WIDX_MONTH_BOX)' levo in 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),
  ....
};

Le malo ljudi razen statičnega analizatorja bi lahko opravilo ta test pozornosti. Ta primer copy-paste je dober ravno zaradi tega razloga.

Opozorila N3

V703 Nenavadno je, da polje »zastavice« v izpeljanem razredu »RCT12BannerElement« prepiše polje v osnovnem razredu »RCT12TileElementBase«. Preverite vrstice: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Seveda uporaba spremenljivke z istim imenom v osnovnem razredu in potomcu ni vedno napaka. Vendar sama tehnologija dedovanja predpostavlja, da so vsa polja nadrejenega razreda prisotna v podrejenem razredu. Z razglasitvijo polj z istim imenom v dediču ustvarimo zmedo.

Opozorilo N4

V793 Nenavadno je, da je rezultat stavka 'imageDirection / 8' del pogoja. Morda bi to izjavo morali primerjati s čim drugim. libopenrct2 ObservationTower.cpp 38

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

Pa poglejmo pobliže. Izraz imageDirection/8 bo napačno, če imageDirection je v območju od -7 do 7. Drugi del: (imageDirection / 8) != 3 čeki imageDirection za izven območja: od -31 do -24 oziroma od 24 do 31. Precej nenavadno se mi zdi preverjanje števil za vključitev v določen obseg na ta način in, tudi če v tem delu kode ni napake, priporočam, da te pogoje ponovno napišete, da bodo bolj jasni. To bi olajšalo življenje ljudem, ki bi brali in vzdrževali to kodo.

Opozorilo N5

V587 Nenavadno zaporedje nalog te vrste: A = B; B = A;. Preverite vrstice: 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;
      ....
  }
  ....
}

Ta fragment kode je bil najverjetneje pridobljen z dekompilacijo. Nato je bil, sodeč po komentarju, odstranjen del nedelujoče kode. Ostalo pa je še par operacij cursorId, kar tudi nima velikega smisla.

Opozorilo N6

V1004 [CWE-476] Kazalec 'player' je bil uporabljen nevarno, potem ko je bil preverjen glede na nullptr. Preverite vrstice: 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);                    // <=
  }
  ....
}

To kodo je zelo enostavno popraviti, samo še tretjič jo morate preveriti predvajalnik na ničelni kazalec ali ga dodajte v telo pogojnega stavka. Jaz bi predlagal drugo možnost:

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

Opozorilo N7

V547 [CWE-570] Izraz 'name == nullptr' je vedno napačen. 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));
    ....
  }
  ....
}

Z enim zamahom se lahko znebite težko berljive vrstice kode in rešite težavo s preverjanjem nullptr. Predlagam, da kodo spremenite na naslednji 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);
    ....
  }
  ....
}

Opozorilo N8

V1048 [CWE-1164] Spremenljivki 'ColumnHeaderPressedCurrentState' je bila dodeljena ista vrednost. libopenrct2ui CustomListView.cpp 510

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

Koda izgleda precej čudno. Zdi se mi, da je prišlo do tipkarske napake ali pri pogoju ali pri ponovnem dodeljevanju spremenljivke ColumnHeaderPressedCurrentState pomene false.

Izhod

Kot lahko vidimo, je integracija statičnega analizatorja PVS-Studio v vaš projekt TeamCity precej preprosta. Če želite to narediti, je dovolj, da napišete samo eno majhno konfiguracijsko datoteko. Preverjanje kode vam bo omogočilo, da prepoznate težave takoj po sestavljanju, kar jih bo pomagalo odpraviti, ko bodo kompleksnost in stroški sprememb še nizki.

PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2
Če želite ta članek deliti z angleško govorečim občinstvom, uporabite povezavo za prevod: Vladislav Stolyarov. PVS-Studio in stalna integracija: TeamCity. Analiza projekta Open RollerCoaster Tycoon 2.

Vir: www.habr.com

Dodaj komentar