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

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
NjĂ« nga skenarĂ«t mĂ« tĂ« rĂ«ndĂ«sishĂ«m tĂ« pĂ«rdorimit tĂ« analizatorit PVS-Studio Ă«shtĂ« integrimi i tij me sistemet CI. Edhe pse analiza e projektit me PVS-Studio mund tĂ« integrohet nĂ« çdo sistem continuous integration me vetĂ«m disa komanda, ne vazhdojmĂ« ta bĂ«jmĂ« kĂ«tĂ« proces edhe mĂ« tĂ« lehtĂ«. PVS-Studio tani ka mbĂ«shtetje pĂ«r shndĂ«rrimin e daljes sĂ« analizatorit nĂ« formatin pĂ«r TeamCity — Lloji i Inspektimeve TeamCity. Le tĂ« shohim se si funksionon kjo.

Informacion mbi softuerin e përdorur

PVS-Studio — njĂ« analizator statik pĂ«r kodin nĂ« C, C++, C# dhe Java, i cili Ă«shtĂ« i destinuar pĂ«r tĂ« lehtĂ«suar detyrĂ«n e gjetjes dhe korrigjimit tĂ« gabimeve 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 mjete nga shpĂ«rndarja e tij.

CLMonitor — Ă«shtĂ« njĂ« server monitorimi qĂ« monitoron nisjet e kompajlerĂ«ve. Ai duhet tĂ« nisĂ« pĂ«rpara se tĂ« filloni ndĂ«rtimin e projektit tuaj. NĂ« modin e monitorimit, serveri do tĂ« kapĂ« tĂ« gjitha nisjet e kompajlerĂ«ve tĂ« mbĂ«shtetur. Duhet theksuar se kjo mjet mund tĂ« pĂ«rdoret vetĂ«m pĂ«r analizĂ«n e projekteve C/C++.

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

Informacion mbi projektin e hulumtuar

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

OpenRCT2 — njĂ« implementim i hapur i lojĂ«s RollerCoaster Tycoon 2 (RCT2), qĂ« e zgjeron atĂ« me karakteristika tĂ« reja dhe korrigjon gabimet. Procesi i lojĂ«s pĂ«rqendrohet nĂ« ndĂ«rtimin dhe menaxhimin e njĂ« parku tematik, ku ndodhen atraksione, dyqane dhe objekte. Lojtari duhet tĂ« pĂ«rpiqet tĂ« arrijĂ« fitim dhe tĂ« ruajĂ« njĂ« reputacion tĂ« mirĂ« pĂ«r parkun, duke mbajtur gjithashtu vizitorĂ«t tĂ« lumtur. OpenRCT2 lejon tĂ« luhet si nĂ« skenarĂ«, ashtu edhe nĂ« modin sandbox. SkenarĂ«t kĂ«rkojnĂ« qĂ« lojtari tĂ« pĂ«rfundojĂ« njĂ« detyrĂ« specifike brenda njĂ« kohe tĂ« caktuar, ndĂ«rsa sandbox-i lejon lojtarin tĂ« ndĂ«rtojĂ« njĂ« park mĂ« fleksibĂ«l pa asnjĂ« kufizim apo financa.

Configuration

Për të kursyer kohë, ndoshta do ta anashkaloja procesin e instalimit dhe do të filloja nga momenti kur unë kam serverin TeamCity të hapur në kompjuterin tim. Ne duhet të kalojmë në: localhost:{porta e caktuar gjatë procesit të instalimit}(në rastin tim, localhost:9090) dhe të futim të dhënat për autorizar. Pas hyrjes do të na mirëpritet:

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Klikoni butonin Krijo Projekt. Më pas, zgjidhni Manualisht dhe plotësoni fushat.

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

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Të klikojmë Krijo konfigurimin e ndërtimit.

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Plotësoni fushat, klikoni Krijo. Shihni një dritare që ofron zgjedhjen e sistemit të kontrollit të versioneve. Nëse burimet tanë janë mbajtur lokal, klikoni Shkalo.

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

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Do të shtojmë hapat e ndërtimit, për këtë klikoni: Hapat e ndërtimit -> Shto hap ndërtimi.

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

  • Lloji i runner-it -> LinjĂ« komandash
  • Ekzekuto -> Skript i personalizuar

Duke qenë se do të kryejmë analizën gjatë kompilimit të projektit, ndërtimi dhe analiza duhet të jenë një hap i vetëm, kështu që plotësoni fushën Skript i personalizuar:

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Do tĂ« ndalemi nĂ« hapa tĂ« veçantĂ« mĂ« vonĂ«. ËshtĂ« e rĂ«ndĂ«sishme qĂ« ngarkimi i analizatorit, ndĂ«rtimi i projektit, analiza e tij, gjenerimi i raportit dhe formatimi tĂ« zgjasin vetĂ«m njĂ«mbĂ«dhjetĂ« rreshta kod.

E fundit që na nevojitet të bëjmë është të vendosim variablat e ambientit, të cilat kam shënuar disa rrugë për ta bërë më të lehtë lexueshmërinë e tyre. Për këtë kalojmë në: Parametrat -> Shto parametrin e ri dhe shtojmë tri variabla:

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Tani mbetet të klikoni butonin Ekzekuto në këndin e sipërm të djathtë. Ndërsa ndodhet në proces ndërtimi dhe analize, do t'ju flas për skriptin.

Skripti në fjalë

Së pari, na nevojitet të shkarkojmë versionin më të ri të PVS-Studio. Përdorim menaxherin e paketave Chocoaltey. Për ata që dëshirojnë të dinë më shumë, ka një informacion të nevojshëm. artikulli:

choco install pvs-studio -y

Më pas, do të nisim mjetin e monitorimit të ndërtimit të projektit CLMonitor.

%CLmon% monitor --bashkëngjit

Më pas do të kryejmë ndërtimin e projektit, duke përdorur si variabël ambienti MSB që paraqet rrugën për versionin e nevojshëm për ndërtimin e MSBuild

%MSB% %ProjPath% /t:pastro
%MSB% %ProjPath% /t:rinovim /p:konfigurimi=zgjedhje
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublikimPortativ

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

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

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

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

Më pas, do të përdorim një tjetër mjet nga distibutivi ynë. PlogConverter konverton raportin nga formati standard në atë specifik për TeamCity. Kështu, do të mund ta shohim drejtpërdrejt në dritaren e ndërtimit.

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

Veprimi i fundit do të jetë të nxjerrim raportin e formatizuar në stdout, ku do të kapet nga 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, mund të kalojmë në skedën Projektet dhe të sigurohemi për këtë.

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Tani të klikojmë në Inspections Total, për të vazhduar në shikimin e raportit të analizatorit:

PVS-Studio dhe Integrimi i Vazhdueshëm: TeamCity. Analiza e projektit Open RollerCoaster Tycoon 2
Paralajmërimet grupohen sipas numrave të rregullave diagnostike. Për të naviguar në kod, duhet të klikoni në numrin e rreshtit me paralajmërim. Klikimi në shenjën e pyetjes në këndin e sipërm të djathtë do t'iu hapë një skedë të re me dokumentacion. Mund të bëni gjithashtu navigim në kod duke klikuar në numrin e rreshtit me paralajmërim të analizatorit. Navigimi nga një kompjuter i largët është i mundur me aplikimin e SourceTreeRoot markerit. Ai që është i interesuar për këtë mod të punës së analizatorit mund të njohë me seksionin përkatës dokumentacionin.

Shikimi i rezultateve të punës së analizatorit

Pasi përfunduam me vendosjen dhe konfigurimin e ndërtimit, propozoi të shikojmë disa paralajmërime interesante që u zbuluan në projektin e shqyrtuar.

Kujdesi N1

V773 [CWE-401] Po e përjashtojmë pa çliruar treguesin 'result'. Një rrjedhje memorjeje është e mundshme. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("Objekti ka gabime");
  }
  ....
}

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("Lloji i objektit është i pavlefshëm");
  }
  return result;
}

Analizatori vuri re një gabim që, pas ndarjes dinamike të memorjes në CreateObject, gjatë hedhjes së një përjashtimi, memoria nuk pastrohet, për rrjedhojë ndodhin rrjedhjet e memorjes.

Kujdesi N2

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

Pak një analizë statike do të ishte i vetmi që do të kalonte këtë provim vëmendësie. Ky shembull kopipast është i mirë pikërisht për këtë.

Paralajmërimet N3

V703 ËshtĂ« e çuditshme qĂ« fusha 'flags' nĂ« klasĂ«n e nxjerrĂ« '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ë variabli me të njëjtin emër në klasën bazë dhe në trashëgimtar nuk është gjithmonë një gabim. Megjithatë, teknologjia e trashëgimisë vetë supozon praninë e të gjitha fushave të klasës prind në klasën fëmijë. Duke shpallur fusha me të njëjtin emër në trashëgimtar, krijojmë konfuzion.

Paralajmërimi N4

V793 ËshtĂ« e çuditshme qĂ« rezultati i deklaratĂ«s 'imageDirection / 8' Ă«shtĂ« njĂ« pjesĂ« e kushtit. Ndoshta, kjo deklaratĂ« duhej 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ë shqyrtojmë më në detaje. Shprehja imageDirection / 8 do të jetë false në rastin kur imageDirection ndodhet në intervalin nga -7 në 7. Pjesa e dytë: (imageDirection / 8) != 3 kontrollon imageDirection për të kaluar jashtë intervalit: nga -31 në -24 dhe nga 24 në 31 përkatësisht. Më duket mjaft 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ë fragment kodi nuk ka ndonjë gabim, do të rekomandoja të rishkruhej këto kushte në një format më të qartë. Kjo do t'i thjeshtësonte ndjeshëm jetën atyre që do të lexonin dhe mirëmbanin këtë kod.

Kujdesi N5

V587 Një sekuencë e çuditshme e caktimeve të këtij lloji: 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;                                 // <=
      // 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;
      ....
  }
  ....
}

Ky këtë fragment kodi, me siguri, e morëm përmes dekodimit. Pastaj, sipas komentit të lënë, është hequr një pjesë e kodit që nuk funksiononte. Megjithatë, mbeturinë disa operacione mbi cursorId, të cilat gjithashtu nuk kanë ndonjë kuptim të veçantë.

Alarmi 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)
  {
    // Shto lojtar të ri.
    player = AddPlayer("", "");
    if (player)                                          // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Ky kod është mjaft e thjeshtë për t'u rregulluar, duhet ose një herë të tretë të kontrollohet player për pointerin null, ose të përfshihet brenda trupit të operatorit kusht. 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);
    }
  }
  ....
}

Alarmi N7

V547 [CWE-570] Shprehja 'name == nullptr' është gjithmonë e gabuar. 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ë eliminojmë njëherë e përgjithmonë këtë varg të vështirë për t'u lexuar dhe të zgjidhim problemin me verifikimin e nullptr. Propozoj të ndryshojmë kodin si më poshtë:

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

Alarmi N8

V1048 [CWE-1164] Variabli 'ColumnHeaderPressedCurrentState' iu dha e njëjta vlerë. libopenrct2ui CustomListView.cpp 510

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

Kode duket mjaft e çuditshme. Më duket se ka pasur një gabim ose në kusht, ose gjatë ri-shpërndarjes së variablit ColumnHeaderPressedCurrentState kuptime false.

Përfundimi

Si e shohim, integrohet analizatori statik PVS-Studio në projektin tuaj në TeamCity mjaft lehtë. Mjafton të shkruani një skedar të vogël konfigurimi. Kontrollimi i kodit do të ndihmojë në identifikimin e problemeve menjëherë pas ndërtimit, duke ndihmuar në rregullimin e tyre kur kompleksiteti dhe kostoja e korrigjimeve janë ende të ulëta.

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

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster