PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Unu el la plej aktualaj scenaroj por uzi la analizilon PVS-Studio estas ĝia integriĝo kun CI-sistemoj. Kaj kvankam la analizo de projekto de PVS-Studio el preskaŭ ajna kontinua integriga sistemo povas esti konstruita en nur kelkajn komandojn, ni daŭre faras ĉi tiun procezon eĉ pli oportuna. PVS-Studio nun havas subtenon por konverti analizilproduktaĵon en formaton por TeamCity - TeamCity Inspections Type. Ni vidu kiel ĝi funkcias.

Informoj pri la uzata programaro

PVS Studio — senmova analizilo de C, C++, C# kaj Java kodo, dizajnita por faciligi la taskon trovi kaj korekti diversajn specojn de eraroj. La analizilo povas esti uzata en Vindozo, Linukso kaj macOS. En ĉi tiu artikolo ni aktive uzos ne nur la analizilon mem, sed ankaŭ iujn utilecojn de ĝia distribuo.

CLMonitor — estas monitora servilo, kiu kontrolas lanĉojn de kompililo. Ĝi devas esti rulita tuj antaŭ ol komenci konstrui vian projekton. En monitora reĝimo, la servilo kaptos kurojn de ĉiuj subtenataj kompililoj. Indas noti, ke ĉi tiu utileco nur povas esti uzata por analizi projektojn C/C++.

PlogConverter - ilo por konverti analizilajn raportojn al malsamaj formatoj.

Informoj pri la studata projekto

Ni provu ĉi tiun funkcion sur praktika ekzemplo - ni analizu la projekton OpenRCT2.

OpenRCT2 - malferma efektivigo de la ludo RollerCoaster Tycoon 2 (RCT2), vastigante ĝin per novaj funkcioj kaj riparante cimojn. Ludado rondiras ĉirkaŭ konstruado kaj prizorgado de amuzparko enhavanta veturojn, butikojn kaj instalaĵojn. La ludanto devas provi fari profiton kaj konservi la bonan reputacion de la parko konservante gastojn feliĉaj. OpenRCT2 permesas vin ludi en kaj scenaro kaj sablokesto. Scenaroj devigas la ludanton plenumi specifan taskon ene de fiksita tempo, dum Sandbox permesas al la ludanto konstrui pli flekseblan parkon sen iuj restriktoj aŭ ekonomio.

alĝustigo

Por ŝpari tempon, mi verŝajne preterlasos la instalan procezon kaj komencos de la momento, kiam mi havas la TeamCity-servilon funkciantan en mia komputilo. Ni devas iri al: localhost:{porto specifita dum la instala procezo} (en mia kazo, localhost:9090) kaj enigi rajtigajn datumojn. Post la eniro ni estos salutitaj de:

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Alklaku la butonon Krei Projekton. Poste, elektu Mane kaj plenigu la kampojn.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Post premado de la butono krei, nin salutas fenestro kun agordoj.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Ni klaku Krei konstruan agordon.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Plenigu la kampojn kaj alklaku krei. Ni vidas fenestron, kiu petas vin elekti version-kontrolsistemon. Ĉar la fontoj jam troviĝas loke, alklaku Preterpasi.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Fine, ni transiru al la projektaj agordoj.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Ni aldonu asembleajn paŝojn, por fari ĉi tion klaku: Konstrupaŝoj -> Aldonu konstrupaŝon.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Ĉi tie ni elektas:

  • Kura tipo -> Komandlinio
  • Run -> Propra Skripto

Ĉar ni faros analizon dum projekta kompilo, muntado kaj analizo devus esti unu paŝo, do plenigu la kampon Propra Skribo:

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Ni rigardos individuajn paŝojn poste. Gravas, ke ŝarĝi la analizilon, kunmeti la projekton, analizi ĝin, eligi la raporton kaj formati ĝin bezonas nur dek unu liniojn de kodo.

La lasta afero, kiun ni devas fari, estas agordi la mediajn variablojn, kiujn mi skizis kelkajn manierojn plibonigi ilian legeblecon. Por fari tion, ni pluiru: Parametroj -> Aldonu novan parametron kaj aldonu tri variablojn:

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Ĉio, kion vi devas fari, estas premi la butonon Kuri en la supra dekstra angulo. Dum la projekto estas kunvenita kaj analizata, mi rakontos al vi pri la skripto.

Rekte skripto

Unue, ni devas elŝuti la plej novan distribuadon de PVS-Studio. Por tio ni uzas la pakaĵadministrilon Chocolatey. Por tiuj, kiuj volas scii pli pri tio, ekzistas koresponda artikolo:

choco install pvs-studio -y

Poste, ni lanĉu la konstruan spuran ilon de la projekto CLMonitor.

%CLmon% monitor –-attach

Tiam ni konstruos la projekton kiel mediovariablo MSB estas la vojo al la versio de MSBuild, kiun mi bezonas konstrui

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

Ni enigu la ensaluton kaj permesilon ŝlosilon por PVS-Studio:

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

Post kiam la konstruo finiĝos, rulu CLMonitor denove por generi antaŭprilaboritajn dosierojn kaj statikan analizon:

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

Tiam ni uzos alian ilon de nia distribuo. PlogConverter konvertas raporton de norma formato al TeamCity-specifa formato. Dank' al ĉi tio, ni povos rigardi ĝin rekte en la konstrua fenestro.

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

La lasta paŝo estas montri la formatitan raporton en stdout, kie ĝi estos prenita de la analizanto de TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Plena skriptokodo:

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"

Intertempe, la asembleo kaj analizo de la projekto finiĝis sukcese, ni povas iri al la langeto projektoj kaj certigu pri tio.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Nun ni alklaku Inspektadoj Sumopor iri al rigardado de la analizilo-raporto:

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Avertoj estas grupigitaj per diagnozaj regulnombroj. Por navigi tra la kodo, vi devas alklaki la linionumeron kun la averto. Alklakante la demandosignon en la supra dekstra angulo malfermos al vi novan langeton kun dokumentado. Vi ankaŭ povas navigi tra la kodo alklakante la linionumeron kun la analizilo averto. Navigado de fora komputilo eblas dum uzado FontoTreeRoot markilo. Ĉiu, kiu interesiĝas pri ĉi tiu operacia maniero de la analizilo, povas konatiĝi kun la responda sekcio dokumentado.

Vidante la rezultojn de la analizilo

Nun kiam ni finis deploji kaj agordi la konstruon, ni rigardu kelkajn interesajn avertojn trovitajn en la projekto, kiun ni rigardas.

Averto N1

V773 [CWE-401] La escepto estis ĵetita sen liberigado de la "rezulta" montrilo. Memorfuĝo eblas. 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;
}

La analizilo rimarkis eraron, kiu post dinamike asignado de memoro en KreiObjekton, kiam okazas escepto, la memoro ne estas malbarita, kaj memorfuĝo okazas.

Averto N2

V501 Estas identaj subesprimoj '(1ULL << WIDX_MONTH_BOX)' maldekstre kaj dekstre de la '|' operatoro. 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),
  ....
};

Malmultaj homoj krom senmova analizilo povus trapasi ĉi tiun atentecteston. Ĉi tiu kopi-alglua ekzemplo estas bona ĝuste pro tio.

Avertoj N3

V703 Estas strange, ke la kampo 'flags' en derivita klaso 'RCT12BannerElement' anstataŭigas kampon en baza klaso 'RCT12TileElementBase'. Kontrolu liniojn: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Kompreneble, uzi variablon kun la sama nomo en la baza klaso kaj en la posteulo ne ĉiam estas eraro. Tamen, hereda teknologio mem supozas ke ĉiuj kampoj de la gepatra klaso ĉeestas en la infanklaso. Deklarante kampojn kun la sama nomo en la kronprinco, ni enkondukas konfuzon.

Averto N4

V793 Estas strange, ke la rezulto de la deklaro 'imageDirection / 8' estas parto de la kondiĉo. Eble, ĉi tiu deklaro devus esti komparita kun io alia. libopenrct2 ObservationTower.cpp 38

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

Ni rigardu pli detale. Esprimo bildoDirekto/8 estos malvera se bildoDirekto estas en la intervalo de -7 ĝis 7. Dua parto: (bildoDirekto / 8) != 3 ĉekoj bildoDirekto por esti ekster la intervalo: de -31 ĝis -24 kaj de 24 ĝis 31, respektive. Ŝajnas al mi sufiĉe strange kontroli nombrojn por inkluziviĝo en certa intervalo tiamaniere kaj, eĉ se ne estas eraro en ĉi tiu kodo, mi rekomendus reverki ĉi tiujn kondiĉojn por esti pli eksplicita. Ĉi tio multe plifaciligus la vivon por la homoj, kiuj legus kaj konservus ĉi tiun kodon.

Averto N5

V587 Nepara sinsekvo de ĉi tiu speco: A = B; B = A;. Kontrolu liniojn: 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;
      ....
  }
  ....
}

Tiu ĉi kodfragmento estis plej verŝajne akirita per malkompilo. Tiam, juĝante laŭ la lasita komento, parto de la nefunkcianta kodo estis forigita. Tamen restas ankoraŭ kelkaj operacioj cursorId, kiuj ankaŭ ne havas multe da senco.

Averto N6

V1004 [CWE-476] La "ludanto" montrilo estis uzita nesekure post kiam ĝi estis kontrolita kontraŭ nullptr. Kontrolu liniojn: 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);                    // <=
  }
  ....
}

Ĉi tiu kodo estas sufiĉe facile korektebla; vi nur bezonas kontroli ĝin trian fojon ludanton al nula montrilo, aŭ aldonu ĝin al la korpo de la kondiĉa deklaro. Mi sugestus la duan eblon:

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

Averto N7

V547 [CWE-570] Esprimo 'name == nullptr' ĉiam estas malvera. 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));
    ....
  }
  ....
}

Vi povas forigi malfacile legeblan linion de kodo per unu paŝo kaj solvi la problemon per kontrolo de nullptr. Mi proponas ŝanĝi la kodon jene:

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

Averto N8

V1048 [CWE-1164] La variablo 'ColumnHeaderPressedCurrentState' ricevis la saman valoron. libopenrct2ui CustomListView.cpp 510

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

La kodo aspektas sufiĉe strange. Ŝajnas al mi, ke estis mistajpo aŭ en la kondiĉo aŭ dum reasignado de la variablo ColumnHeaderPressedCurrentState valoroj falsa.

konkludo

Kiel ni povas vidi, integri la statikan analizilon PVS-Studio en vian TeamCity-projekton estas sufiĉe simpla. Por fari tion, sufiĉas skribi nur unu malgrandan agordan dosieron. Kontroli la kodon permesos vin identigi problemojn tuj post asembleo, kio helpos forigi ilin kiam la komplekseco kaj kosto de ŝanĝoj ankoraŭ estas malaltaj.

PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto
Se vi volas dividi ĉi tiun artikolon kun anglalingva publiko, bonvolu uzi la tradukan ligilon: Vladislav Stolyarov. PVS-Studio kaj Kontinua Integriĝo: TeamCity. Analizo de la Open RollerCoaster Tycoon 2 projekto.

fonto: www.habr.com

Aldoni komenton