PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
NjĂ« nga skenarĂ«t mĂ« aktualĂ« pĂ«r pĂ«rdorimin e analizatorit PVS-Studio Ă«shtĂ« integrimi i tij me sistemet CI. Edhe pse analiza e projektit PVS-Studio mund tĂ« integrohet nga çdo sistem tĂ« vazhdueshĂ«m tĂ« integrimit pĂ«rmes vetĂ«m disa komandave, ne vazhdojmĂ« ta bĂ«jmĂ« kĂ«tĂ« proces edhe mĂ« tĂ« lehtĂ«. PVS-Studio tani mbĂ«shtet transformimin e daljes sĂ« analizatorit nĂ« njĂ« format pĂ«r TeamCity — TeamCity Inspections Type. Le tĂ« shohim se si funksionon.

Informata mbi softuerin e përdorur

PVS-Studio — njĂ« analizator statik pĂ«r kode C, C++, C# dhe Java, i destinuar pĂ«r tĂ« lehtĂ«suar detyrĂ«n e gjetjes dhe korrigjimit tĂ« ndryshimeve tĂ« ndryshme. Analizatori mund tĂ« pĂ«rdoret nĂ« Windows, Linux dhe macOS. NĂ« kĂ«tĂ« artikull, ne do tĂ« pĂ«rdorim aktivisht jo vetĂ«m analizatorin, por edhe disa nga utilitetet nga distribuimi i tij.

CLMonitor — pĂ«rfaqĂ«son njĂ« server monitorimi qĂ« monitoron ekzekutimet e kompajlerĂ«ve. Duhet tĂ« aktivizohet menjĂ«herĂ« para fillimit tĂ« ndĂ«rtimit tĂ« projektit tuaj. NĂ« mĂ«nyrĂ«n e monitorimit, serveri do tĂ« kapĂ« ekzekutimet e tĂ« gjithĂ« kompajlerĂ«ve tĂ« mbĂ«shtetur. Duhet theksuar se ky utilitet mund tĂ« pĂ«rdoret vetĂ«m pĂ«r analizimin e projekteve C/C++.

PlogConverter – njĂ« mjet pĂ«r konvertimin e raporteve tĂ« analizuesit nĂ« formate tĂ« ndryshme.

Informacion mbi projektin e studiuar

Le ta provojmĂ« kĂ«tĂ« funksionalitet me njĂ« shembull praktik – tĂ« analizojmĂ« projektin OpenRCT2.

OpenRCT2 – Ă«shtĂ« njĂ« implementim i hapur i lojĂ«s RollerCoaster Tycoon 2 (RCT2), qĂ« e zgjeron atĂ« me funksione tĂ« reja dhe rregullon gabimet. Loja pĂ«rqendrohet rreth ndĂ«rtimit dhe menaxhimit tĂ« njĂ« parku argĂ«timi, ku gjenden atraksione, dyqane dhe objekte. Lojtari duhet tĂ« pĂ«rpiqet tĂ« marrĂ« fitim dhe tĂ« mbajĂ« njĂ« reputacion tĂ« mirĂ« pĂ«r parkun, duke u siguruar njĂ«kohĂ«sisht qĂ« vizitorĂ«t tĂ« jenĂ« tĂ« lumtur. OpenRCT2 lejon qĂ« tĂ« luhet si nĂ« skenarĂ«, ashtu edhe nĂ« mĂ«nyrĂ«n sandbox. SkenarĂ«t kĂ«rkojnĂ« qĂ« lojtari tĂ« pĂ«rmbushĂ« njĂ« detyrĂ« tĂ« caktuar brenda njĂ« kohe tĂ« caktuar, ndĂ«rsa mĂ«nyra sandbox i lejon lojtarit tĂ« ndĂ«rtosh njĂ« park mĂ« fleksibĂ«l pa kufizime apo financa.

Konfigurimi

Për të kursyer kohë, ndoshta do të anashkaloj procesin e instalimit dhe do të filloj nga momenti kur kam serverin TeamCity të hapur në kompjuterin tim. Na duhet të shkojmë te: localhost:{porta e caktuar gjatë procesit të instalimit}(në rastin tim, localhost:9090) dhe të futim të dhënat për autentifikim. Pasi të hyjmë, do të na presë:

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Klikoni butonin Create Project. Më pas, zgjidhim Manually dhe plotësojmë fushat.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Pas klikimit në butonin Create, na pritet një dritare me cilësimet.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Klikoni Create build configuration.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Plotësojmë fushat, klikojmë Create. Ne shohim një dritare që ofron zgjedhjen e sistemit të kontrollit të versioneve. Duke qenë se burimet tashmë ndodhen lokalitet, klikojmë Skip.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Më në fund, ne kalojmë në cilësimet e projektit.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Shtojmë hapat e ndërtimit, për këtë klikojmë: Build steps -> Add build step.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Këtu zgjidhim:

  • Runner type -> Command Line
  • Run -> Custom Script

Duke qenë se do të kryejmë analizën gjatë kompaktimit të projektit, ndërtimi dhe analiza duhet të jenë një hap, prandaj plotësojmë fushën Custom Script:

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
NĂ« hapa tĂ« veçantĂ« do tĂ« ndalemi mĂ« vonĂ«. ËshtĂ« e rĂ«ndĂ«sishme qĂ« ngarkimi i analizuesit, ndĂ«rtimi i projektit, analiza e tij, prodhimi i raportit dhe formatimi i tij tĂ« zĂ«nĂ« vetĂ«m njĂ«mbĂ«dhjetĂ« rreshta kodi.

E fundit që na nevojitet është të vendosim variablat e ambientit, për të cilat kam shënuar disa rrugë për të përmirësuar lexueshmërinë e tyre. Për këtë do të kalojmë në: Parameters -> Add new parameter dhe do të shtojmë tre variabla:

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Na mbetet të klikojmë mbi butonin Ekzekuto në këndin e sipërm të djathtë. Ndërsa ndërtimi dhe analiza e projektit po zhvillohen, do t'ju tregoj rreth skriptit.

Skripti në vetvete

Së pari, na nevojitet të shkarkojmë distribucionin e ri të PVS-Studio. Për këtë, ne do të përdorim menaxherin e paketave Chocolatey. Për ata që duan të dinë më shumë rreth kësaj, ka informacion të përkatshëm. artikull:

choco install pvs-studio -y

Më pas, do të nisnim utilitarin për monitorimin e ndërtimit të projektit CLMonitor.

%CLmon% monitor –-attach

Pastaj do të ndërtojmë projektin, duke përdorur si variabël mjedisi MSB i cili përfaqëson rrugën për versionin e duhur të MSBuild për ndërtim

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

Do të hyjmë me emrin e përdoruesit dhe çelësin e licencës për PVS-Studio:

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

Pas përfundimit të ndërtimit, do të nisnim sërish CLMonitor për të gjeneruar skedarët e parapërpunuar dhe analizën statike:

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

Më pas do të përdorim edhe një utilitar nga distribucioni ynë. PlogConverter konverton raportin nga formati standard në atë specifik për TeamCity. Falë kësaj, do të mund ta shohim atë drejtpërdrejt në dritaren e ndërtimit.

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

Si veprimin përfundimtar, do të nxjerrim raportin e formatizuar në stdout, ku do ta kapë parser-i i TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Kodi i plotë i skriptit:

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"

Ndërkohë, ndërtimi dhe analiza e projektit përfunduan me sukses, tani mund të kalojmë te tabu Projekti dhe të sigurohemi për këtë.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Tani të klikojmë në Totali i Inspeksioneve, për të kaluar në shikimin e raportit të analizuesit:

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Këmbënguljet janë grupuar sipas numrave të rregullave diagnostikuese. Për të naviguar në kod, duhet të klikoni në numrin e rreshtit me paralajmërimin. Kliki në shenjën e pyetjes në këndin e sipërm të djathtë do t'ju hapë një tab të re me dokumentacionin. gjithashtu mund të navigoni në kod duke klikuar në numrin e rreshtit me paralajmërimin e analizuesit. Navigimi nga një kompjuter i largët është i mundur duke përdorur SourceTreeRoot marker. Ata që janë të interesuar për këtë mod të funksionimit të analizuesit, mund të konsultohen me seksionin përkatës dokumentacion.

Shikimi i rezultateve të punës së analizuesit

Pasi përfunduam me implementimin dhe konfigurimin e ndërtimit, sugjeroj të shohim disa paralajmërime interesante që u zbuluan në projektin e shqyrtuar.

Paralajmërimi N1

V773 [CWE-401] Përjashtimi u hodh pa lirimin e treguesit 'rezultat'. Një rrjedhje memorie është e mundur. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* rezultat = nullptr;
  ....
  rezultat = CreateObject(entry);
  ....
  nëse (readContext.WasError())
  {
    throw std::runtime_error("Objekti ka gabime");
  }
  ....
}

Object* CreateObject(const rct_object_entry& entry)
{
  Object* rezultat;
  switch (entry.GetType())
  {
    rast tipi_OBJECT_RIDE:
      rezultat = new RideObject(entry);
      break;
    rast tipi_OBJECT_SMALL_SCENERY:
      rezultat = new SmallSceneryObject(entry);
      break;
    rast tipi_OBJECT_LARGE_SCENERY:
      rezultat = new LargeSceneryObject(entry);
      break;
    ....
    default:
      throw std::runtime_error("Lloji i objektit është i pavlefshëm");
  }
  kthe rezultat;
}

Analizuesi vuri re një gabim, duke theksuar se pas alokimit dinamik të memories në CreateObject, në rast të një përjashtimi, memoria nuk pastrohet, duke rezultuar kështu në një rrjedhje memorje.

Paralajmërimi N2

V501 Ka nën-shprehje identike '(1ULL << WIDX_MONTH_BOX)' të majtë dhe të djathtë të operatorit '|'. 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),
  ....
};

Pothuajse askush, përveç analizuesit statik, nuk do të ishte në gjendje të kalonte këtë test vëmendësie. Ky shembull i kopjimit është i mirë pikërisht për këtë.

Paralajmërimet N3

V703 ËshtĂ« e çuditshme qĂ« fusha 'flags' nĂ« klasĂ«n e derivateve 'RCT12BannerElement' mbulon fushĂ«n nĂ« klasĂ«n bazĂ« 'RCT12TileElementBase'. Kontrolloni rreshtat: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Sigurisht, përdorimi i një variable me emër të njëjtë në klasën bazë dhe në trashëgimtar nuk është gjithmonë një gabim. Megjithatë, teknologjia e trashëgimisë vetë supozon praninë e të gjitha kampeve të klasës prindore në atë fëmijë. Duke shpallur në trashëgimtar kampe me emra të tillë, ne krijojmë konfuzion.

Kujdesi N4

V793 ËshtĂ« e çuditshme qĂ« rezultati i deklaratĂ«s 'imageDirection / 8' Ă«shtĂ« pjesĂ« e kushteve. Ndoshta, kjo deklaratĂ« duhet tĂ« ishte krahasuar me diçka tjetĂ«r. libopenrct2 ObservationTower.cpp 38

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

Le të hedhim një vështrim më të afërt. Shprehja imageDirection / 8 do të jetë false në rastin kur imageDirection është në intervalin nga -7 deri në 7. Pjesa e dytë: (imageDirection / 8) != 3 kontrollon imageDirection për qëndrim jashtë intervalit: nga -31 deri në -24 dhe nga 24 deri në 31 përkatësisht. Më duket e çuditshme të kontrollosh numrat për përfshirje në një interval të caktuar në këtë mënyrë dhe, edhe nëse në këtë segment kodi nuk ka gabim, do të rekomandoja të ri-shkruhen këto kushte në mënyra më të qarta. Kjo do të thjeshtonte ndjeshëm jetën e atyre që do të lexonin dhe mbështetnin këtë kod.

Kujdesi N5

V587 Një sekuencë e çuditshme e caktimeve të kësaj lloj: A = B; B = A;. Kontrolloni rreshtat: 1115, 1118. libopenrct2ui MouseInput.cpp 1118

void process_mouse_over(....)
{
  ....
  switch (window->widgets[widgetId].type)
  {
    case WWT_VIEWPORT:
      ebx = 0;
      edi = cursorId;                                 // <=
      // Ngjarja e dritares WE_UNKNOWN_0E u thirr këtu,
      // por asnjë dritare në të vërtetë nuk implementoi një menaxher dhe
      // nuk dihet për çfarë ishte
      cursorId = edi;                                 // <=
      if ((ebx & 0xFF) != 0)
      {
        set_cursor(cursorId);
        return;
      }
      break;
      ....
  }
  ....
}

Ky fragment kodi, për siguri, është marrë përmes dekompilimit. Pastaj, sipas komentit të lënë, është fshirë një pjesë e kodit të papunuar. Megjithatë, ka mbetur një çift operacionesh mbi cursorId, të cilat gjithashtu nuk kanë ndonjë kuptim të veçantë.

Kujdesi N6

V1004 [CWE-476] Pika 'player' u përdor në mënyrë të pasigurt pas verifikimit kundrejt nullptr. Kontrolloni rreshtat: 2085, 2094. libopenrct2 Network.cpp 2094

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Shtoni lojtarin e ri.
    player = AddPlayer("", "");
    if (player)                                          // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Ky kod është shumë i lehtë të rregullohet, është e nevojshme ose të kontrolloni për herë të tretë player në një tregues të null, ose ta përfshini atë në trupin e operatorit kushtor. Do të sugjeroja opsionin e dytë:

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Shto lojtar të ri.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}

Paralajmërimi N7

V547 [CWE-570] Shprehja 'name == nullptr' gjithmonë është e pavërtetë. libopenrct2 ServerList.cpp 102

std::optional ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    ....
  }
  else
  {
    ....
    entry.name = (name == nullptr ? "" : json_string_value(name));
    ....
  }
  ....
}

Mund të heqim njëherazi një varg të vështirë për t'u lexuar dhe të zgjidhim problemin me kontrollimin e nullptr. Propozoj të ndryshojmë kodin në këtë mënyrë:

std::optional ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    name = "";
    ....
  }
  else
  {
    ....
    entry.name = json_string_value(name);
    ....
  }
  ....
}

Paralajmërimi N8

V1048 [CWE-1164] Variabla 'ColumnHeaderPressedCurrentState' u caktoi të njëjtin vlerë. libopenrct2ui CustomListView.cpp 510

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

Kodi duket mjaft i çuditshëm. Më duket se ka pasur një gabim qoftë në kusht, ose gjatë ripërdorimit të variablës. KolonaHeaderPressedCurrentState vlerat false.

Përfundim

Siç e shohim, integrimi i analizuesit statik PVS-Studio në projektin tuaj në TeamCity është mjaft i thjeshtë. Mjafton të shkruani vetëm një skedar të vogël konfigurimi. Kontrolli i kodit do të ndihmojë në identifikimin e problemeve menjëherë pas ndërtimit, duke lejuar që të zgjidhen atëherë kur kompleksiteti dhe kostoja e korrigjimeve janë akoma të vogla.

PVS-Studio dhe Integrimi i Vazhdushëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Nëse dëshironi të ndani këtë artikull me një audiencë anglishtfolëse, ju lutem përdorni lidhjen për përkthimin: Vladislav Stolyarov. PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2.

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster