PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Isa sa mga pinakabagong sitwasyon para sa paggamit ng PVS-Studio analyzer ay ang pagsasama nito sa mga CI system. At kahit na ang pagsusuri ng isang proyekto ng PVS-Studio mula sa halos anumang tuluy-tuloy na sistema ng pagsasama ay maaaring itayo sa ilang mga utos, patuloy naming ginagawang mas maginhawa ang prosesong ito. Ang PVS-Studio ay mayroon na ngayong suporta para sa pag-convert ng output ng analyzer sa isang format para sa TeamCity - Uri ng TeamCity Inspections. Tingnan natin kung paano ito gumagana.

Impormasyon tungkol sa software na ginamit

PVS-Studio β€” isang static na analyzer ng C, C++, C# at Java code, na idinisenyo upang mapadali ang gawain ng paghahanap at pagwawasto ng iba't ibang uri ng mga error. Maaaring gamitin ang analyzer sa Windows, Linux at macOS. Sa artikulong ito ay aktibong gagamitin namin hindi lamang ang analyzer mismo, kundi pati na rin ang ilang mga utility mula sa pamamahagi nito.

CLMonitor β€” ay isang monitoring server na sumusubaybay sa mga paglulunsad ng compiler. Dapat itong patakbuhin kaagad bago simulan ang pagbuo ng iyong proyekto. Sa mode ng pagsubaybay, haharangin ng server ang lahat ng sinusuportahang compiler. Kapansin-pansin na ang utility na ito ay magagamit lamang upang pag-aralan ang mga proyekto ng C/C++.

PlogConverter – isang utility para sa pag-convert ng mga ulat ng analyzer sa iba't ibang mga format.

Impormasyon tungkol sa proyektong pinag-aaralan

Subukan natin ang functionality na ito gamit ang isang praktikal na halimbawa - suriin natin ang proyekto ng OpenRCT2.

OpenRCT2 - isang bukas na pagpapatupad ng larong RollerCoaster Tycoon 2 (RCT2), pagpapalawak nito gamit ang mga bagong function at pag-aayos ng mga bug. Ang gameplay ay umiikot sa pagbuo at pagpapanatili ng amusement park na naglalaman ng mga rides, tindahan, at pasilidad. Dapat subukan ng manlalaro na kumita at mapanatili ang magandang reputasyon ng parke habang pinapanatiling masaya ang mga bisita. Binibigyang-daan ka ng OpenRCT2 na maglaro sa parehong senaryo at sandbox. Ang mga sitwasyon ay nangangailangan ng player na kumpletuhin ang isang partikular na gawain sa loob ng isang nakatakdang oras, habang ang Sandbox ay nagpapahintulot sa player na bumuo ng isang mas flexible na parke nang walang anumang mga paghihigpit o pananalapi.

pag-aayos

Upang makatipid ng oras, malamang na laktawan ko ang proseso ng pag-install at magsimula mula sa sandaling mayroon akong server ng TeamCity na tumatakbo sa aking computer. Kailangan nating pumunta sa: localhost:{port na tinukoy sa panahon ng proseso ng pag-install} (sa aking kaso, localhost:9090) at ipasok ang data ng pahintulot. Pagkapasok namin ay sasalubungin kami ni:

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Mag-click sa pindutan ng Lumikha ng Proyekto. Susunod, piliin ang Manu-manong at punan ang mga patlang.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Pagkatapos pindutin ang pindutan Lumikha, binati kami ng isang window na may mga setting.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Pindutin natin Lumikha ng configuration ng build.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Punan ang mga patlang at i-click Lumikha. Nakikita namin ang isang window na humihiling sa iyo na pumili ng isang version control system. Dahil ang mga mapagkukunan ay matatagpuan na sa lokal, i-click Laktawan.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Sa wakas, lumipat kami sa mga setting ng proyekto.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Magdagdag tayo ng mga hakbang sa pagpupulong, para magawa ang pag-click na ito: Mga hakbang sa pagbuo -> Magdagdag ng hakbang sa pagbuo.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Dito kami pumili:

  • Uri ng runner -> Command Line
  • Run -> Custom na Script

Dahil magsasagawa kami ng pagsusuri sa panahon ng pagsasama-sama ng proyekto, ang pagpupulong at pagsusuri ay dapat na isang hakbang, kaya punan ang field Custom Script:

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Titingnan natin ang mga indibidwal na hakbang mamaya. Mahalaga na ang pag-load ng analyzer, pag-assemble ng proyekto, pagsusuri nito, pag-output ng ulat at pag-format nito ay tumatagal lamang ng labing-isang linya ng code.

Ang huling bagay na kailangan nating gawin ay itakda ang mga variable ng kapaligiran, na binalangkas ko ng ilang paraan upang mapabuti ang kanilang pagiging madaling mabasa. Upang gawin ito, magpatuloy tayo: Mga Parameter -> Magdagdag ng bagong parameter at magdagdag ng tatlong variable:

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Ang kailangan mo lang gawin ay pindutin ang pindutan Tumakbo sa kanang sulok sa itaas. Habang binubuo at sinusuri ang proyekto, sasabihin ko sa iyo ang tungkol sa script.

Direktang script

Una, kailangan nating i-download ang pinakabagong pamamahagi ng PVS-Studio. Para dito ginagamit namin ang Chocolatey package manager. Para sa mga nais malaman ang higit pa tungkol dito, mayroong isang katumbas artikulo:

choco install pvs-studio -y

Susunod, ilunsad natin ang CLmonitor project build tracking utility.

%CLmon% monitor –-attach

Pagkatapos ay bubuo kami ng proyekto bilang isang variable ng kapaligiran MSB ay ang landas sa bersyon ng MSBuild na kailangan kong buuin

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

Ilagay natin ang login at license key para sa PVS-Studio:

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

Pagkatapos makumpleto ang build, patakbuhin muli ang CLmonitor upang makabuo ng mga na-preprocess na file at static na pagsusuri:

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

Pagkatapos ay gagamit kami ng isa pang utility mula sa aming pamamahagi. Kino-convert ng PlogConverter ang isang ulat mula sa isang karaniwang format sa isang format na partikular sa TeamCity. Salamat dito, makikita namin ito nang direkta sa window ng build.

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

Ang huling hakbang ay upang ipakita ang na-format na ulat sa stdout, kung saan ito ay kukunin ng TeamCity parser.

type "C:tempptest.plog_TeamCity.txt"

Buong script code:

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"

Samantala, matagumpay na nakumpleto ang pagpupulong at pagsusuri ng proyekto, maaari tayong pumunta sa tab proyekto at siguraduhin mo ito.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Ngayon mag-click tayo sa Kabuuan ng mga Inspeksyonupang pumunta sa pagtingin sa ulat ng analyzer:

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Nakapangkat ang mga babala ayon sa mga numero ng diagnostic na panuntunan. Upang mag-navigate sa code, kailangan mong mag-click sa numero ng linya na may babala. Ang pag-click sa tandang pananong sa kanang sulok sa itaas ay magbubukas sa iyo ng bagong tab na may dokumentasyon. Maaari ka ring mag-navigate sa code sa pamamagitan ng pag-click sa numero ng linya na may babala ng analyzer. Ang pag-navigate mula sa isang malayuang computer ay posible kapag gumagamit SourceTreeRoot pananda. Ang sinumang interesado sa mode na ito ng pagpapatakbo ng analyzer ay maaaring maging pamilyar sa kaukulang seksyon dokumentasyon.

Pagtingin sa mga resulta ng analyzer

Ngayong tapos na tayo sa pag-deploy at pag-configure ng build, tingnan natin ang ilang kawili-wiling babala na makikita sa proyektong tinitingnan natin.

Babala N1

V773 [CWE-401] Ang pagbubukod ay itinapon nang hindi inilabas ang 'result' pointer. Posible ang memory leak. 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;
}

Napansin ng analyzer ang isang error na pagkatapos ng dynamic na paglalaan ng memorya CreateObject, kapag may naganap na pagbubukod, ang memorya ay hindi na-clear, at ang isang memory leak ay nangyayari.

Babala N2

V501 Mayroong magkaparehong mga sub-expression na '(1ULL << WIDX_MONTH_BOX)' sa kaliwa at sa kanan ng '|' operator. 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),
  ....
};

Ilang tao maliban sa isang static na analyzer ang maaaring makapasa sa attentiveness test na ito. Ang halimbawang copy-paste na ito ay mabuti para sa eksaktong kadahilanang ito.

Mga Babala N3

V703 Kakaiba na ang field na 'flag' sa nagmula na klase na 'RCT12BannerElement' ay nag-o-overwrite sa field sa base class na 'RCT12TileElementBase'. Suriin ang mga linya: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Siyempre, ang paggamit ng variable na may parehong pangalan sa base class at sa descendant ay hindi palaging isang error. Gayunpaman, ipinapalagay mismo ng inheritance technology na ang lahat ng field ng parent class ay nasa child class. Sa pamamagitan ng pagdedeklara ng mga field na may parehong pangalan sa tagapagmana, lumilikha kami ng kalituhan.

Babala N4

V793 Ito ay kakaiba na ang resulta ng 'imageDirection / 8' na pahayag ay bahagi ng kundisyon. Marahil, ang pahayag na ito ay dapat na inihambing sa ibang bagay. libopenrct2 ObservationTower.cpp 38

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

Tingnan natin nang maigi. Pagpapahayag Direksyon ng larawan/8 magiging huwad kung Direksyon ng imahe ay nasa hanay mula -7 hanggang 7. Pangalawang bahagi: (Direksiyon ng imahe / 8) != 3 mga tseke Direksyon ng imahe para sa pagiging nasa labas ng saklaw: mula -31 hanggang -24 at mula 24 hanggang 31, ayon sa pagkakabanggit. Mukhang kakaiba sa akin na suriin ang mga numero para sa pagsasama sa isang tiyak na hanay sa ganitong paraan at, kahit na walang error sa piraso ng code na ito, inirerekumenda kong muling isulat ang mga kundisyong ito upang maging mas malinaw. Gagawin nitong mas madali ang buhay para sa mga taong magbabasa at magpanatili ng code na ito.

Babala N5

V587 Isang kakaibang pagkakasunod-sunod ng mga takdang-aralin ng ganitong uri: A = B; B = A;. Suriin ang mga linya: 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;
      ....
  }
  ....
}

Ang fragment ng code na ito ay malamang na nakuha sa pamamagitan ng decompilation. Pagkatapos, sa paghusga sa komentong iniwan, inalis ang bahagi ng hindi gumaganang code. Gayunpaman, mayroon pa ring ilang mga operasyon na natitira cursorId, na hindi rin gaanong makatuwiran.

Babala N6

V1004 [CWE-476] Ang 'manlalaro' na pointer ay ginamit nang hindi ligtas pagkatapos itong ma-verify laban sa nullptr. Suriin ang mga linya: 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);                    // <=
  }
  ....
}

Ang code na ito ay medyo madaling itama; kailangan mo lamang itong suriin sa pangatlong beses manlalaro sa isang null pointer, o idagdag ito sa katawan ng conditional statement. Iminumungkahi ko ang pangalawang pagpipilian:

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

Babala N7

V547 [CWE-570] Ang ekspresyong 'pangalan == nullptr' ay palaging mali. 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));
    ....
  }
  ....
}

Maaalis mo ang isang mahirap basahin na linya ng code sa isang iglap at lutasin ang problema sa pagsuri para sa nullptr. Iminumungkahi kong baguhin ang code tulad ng sumusunod:

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

Babala N8

V1048 [CWE-1164] Ang variable na 'ColumnHeaderPressedCurrentState' ay itinalaga ng parehong halaga. libopenrct2ui CustomListView.cpp 510

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

Ang code ay mukhang medyo kakaiba. Tila sa akin ay nagkaroon ng typo alinman sa kundisyon o kapag muling pagtatalaga ng variable ColumnHeaderPressedCurrentState kahulugan hindi totoo.

Pagbubuhos

Tulad ng nakikita natin, ang pagsasama ng PVS-Studio static analyzer sa iyong proyekto ng TeamCity ay medyo simple. Upang gawin ito, sapat na upang magsulat lamang ng isang maliit na file ng pagsasaayos. Ang pagsuri sa code ay magbibigay-daan sa iyo na matukoy ang mga problema kaagad pagkatapos ng pagpupulong, na makakatulong na maalis ang mga ito kapag ang pagiging kumplikado at gastos ng mga pagbabago ay mababa pa rin.

PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2
Kung nais mong ibahagi ang artikulong ito sa isang madla na nagsasalita ng Ingles, mangyaring gamitin ang link ng pagsasalin: Vladislav Stolyarov. PVS-Studio at Patuloy na Pagsasama: TeamCity. Pagsusuri ng proyekto ng Open RollerCoaster Tycoon 2.

Pinagmulan: www.habr.com

Magdagdag ng komento