PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Jedním z nejaktuálnějších scénářů použití analyzátoru PVS-Studio je jeho integrace s CI systémy. Ačkoli je analýzu projektu pomocí PVS-Studio možné téměř v jakémkoli systému kontinuální integrace integrovat jen několika příkazy, stále se snažíme tento proces ještě více ulehčit. V PVS-Studio byla přidána podpora pro převod výstupu analyzátoru do formátu pro TeamCity — TeamCity Inspections Type. Podívejme se, jak to funguje.

Informace o používaném softwaru

PVS-Studio — statický analyzátor C, C++, C# a Java kódu, určený k usnadnění úkolu hledání a opravování různých chyb. Analyzátor lze používat na Windows, Linux a macOS. V tomto článku se zaměříme nejen na samotný analyzátor, ale i na některé nástroje z jeho distribuce.

CLMonitor — je server monitorování, který sleduje spuštění překladačů. Je nutné ho spustit přímo před začátkem sestavení vašeho projektu. V režimu sledování server zachytí spuštění všech podporovaných překladačů. Je třeba zmínit, že tento nástroj lze používat pouze pro analýzu C/C++ projektů.

PlogConverter – nástroj pro konverzi zprávy analyzátoru do různých formátů.

Informace o analyzovaném projektu

Pojďme vyzkoušet tuto funkčnost na praktickém příkladu – analyzujeme projekt OpenRCT2.

OpenRCT2 — otevřená implementace hry RollerCoaster Tycoon 2 (RCT2), která ji rozšiřuje o nové funkce a opravuje chyby. Herní proces se točí kolem stavby a údržby zábavního parku, ve kterém se nacházejí atrakce, obchody a objekty. Hráč se musí snažit dosáhnout zisku a udržovat dobrou pověst parku, přičemž se snaží udržet hosty šťastné. OpenRCT2 umožňuje hrát jak scénáře, tak v písečné hře. Scénáře vyžadují, aby hráč splnil určitý úkol v daném čase, zatímco písečná hra umožňuje hráči stavět flexibilnější park bez jakýchkoli omezení nebo financí.

Nastavení

Abych ušetřil čas, vynechám proces instalace a začnu v okamžiku, kdy mám na počítači spuštěný server TeamCity. Musíme přejít na: localhost:{port uvedený v instalačním procesu}(v mém případě localhost:9090) a zadat přihlašovací údaje. Po přihlášení nás přivítá:

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Klikneme na tlačítko Vytvořit projekt. Dále vybereme Ručně, vyplníme pole.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Po stisknutí tlačítka Vytvořit, setkáme se s oknem s nastavením.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Klikněte Vytvořit konfiguraci sestavení.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Vyplníme pole, klikneme na Vytvořit. Vidíme okno s nabídkou výběru systému správy verzí. Protože zdrojové kódy již leží v místním počítači, klikneme na Přeskočit.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Nakonec přejdeme k nastavení projektu.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Přidáme kroky sestavení, k tomu klikneme na: Kroky sestavení -> Přidat krok sestavení.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Zde vybereme:

  • Typ běžce -> Příkazový řádek
  • Spustit -> Vlastní skript

Protože budeme provádět analýzu během kompilace projektu, sestavení a analýza by měly být jedním krokem, vyplníme pole Vlastní skript:

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Na jednotlivých krocích se zastavíme později. Je důležité, aby načítání analyzátoru, sestavení projektu, jeho analýza, generování zprávy a její formátování nezabralo více než jedenáct řádků kódu.

Poslední, co musíme udělat, je nastavit proměnné prostředí, kterými jsem označil některé cesty pro zlepšení jejich přehlednosti. K tomu přejdeme na: Parametry -> Přidat novou proměnnou a přidáme tři proměnné:

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Zbývá kliknout na tlačítko Spustit v pravém horním rohu. Zatímco probíhá sestavení a analýza projektu, povím vám o skriptu.

Přímo skript

Nejprve musíme stáhnout čerstvý distribuční balíček PVS-Studio. K tomu použijeme správce balíčků Chocolatey. Pro ty, kteří se chtějí dozvědět více, je zde odpovídající neaktuální statistika:

choco install pvs-studio -y

Nyní spustíme nástroj pro sledování sestavení projektu CLMonitor.

%CLmon% monitor --attach

Pak provedeme sestavení projektu, jako proměnnou prostředí MSB je cesta k verzi MSBuild, kterou potřebuju pro sestavení

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

Zadáme uživatelské jméno a klíč licence PVS-Studio:

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

Po dokončení sestavení znovu spustíme CLMonitor pro generování předzpracovaných souborů a statické analýzy:

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

Poté použijeme další nástroj z našeho distribučního balíčku. PlogConverter převede zprávu ze standardního do specifického formátu pro TeamCity. Díky tomu se na ní můžeme podívat přímo v okně sestavení.

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

Posledním krokem vyvedeme naformátovanou zprávu do stdout, kde ji zachytí parser TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Celý kód skriptu:

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"

Mezitím byla sestavení a analýza projektu úspěšně dokončena, můžeme přejít na záložku Projekty a ujistit se o tom.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Nyní klikneme na Celkový počet kontrol, abychom přešli na zobrazení zprávy analyzátoru:

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Upozornění jsou seskupena podle čísel diagnostických pravidel. Pro navigaci v kódu je třeba kliknout na číslo řádku s upozorněním. Kliknutím na otazník v pravém horním rohu se otevře nová záložka s dokumentací. Navigaci v kódu lze také procházet kliknutím na číslo řádku s upozorněním analyzátoru. Navigace z dalekého počítače je možná při použití SourceTreeRoot markeru. Ti, kteří mají zájem o tento režim práce analyzátoru, se mohou seznámit s příslušnou sekcí dokumentaci.

Zobrazení výsledků práce analyzátoru

Poté, co jsme dokončili nasazení a nastavení sestavení, navrhuji podívat se na některá zajímavá upozornění nalezená v prozkoumaném projektu.

Upozornění N1

V773 [CWE-401] Výjimka byla vyhozena bez uvolnění ukazatele 'result'. Může dojít k úniku paměti. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("Objekt má chyby");
  }
  ....
}

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("Neplatný typ objektu");
  }
  return result;
}

Analyzátor si všiml chyby, že po dynamickém alokaci paměti v CreateObject, při vyvolání výjimky není paměť vyčištěna, což znamená, že dochází k úniku paměti.

Upozornění N2

V501 Existují identické podvýrazy '(1ULL << WIDX_MONTH_BOX)' vlevo a vpravo od operátoru '|'. 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),
  ....
};

Málokdo kromě statického analyzátoru by dokázal projít tento test pozornosti. Tento příklad kopírování a vkládání je dobrý právě tímto způsobem.

Varování N3

V703 Je podivné, že pole 'flags' ve třídě 'RCT12BannerElement' přepisuje pole v základní třídě 'RCT12TileElementBase'. Zkontrolujte řádky: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Samozřejmě, použití proměnné se stejným názvem v základní třídě a v dědici není vždy chybou. Nicméně technologie dědění předpokládá, že všechna pole základní třídy jsou v dceřiné. Pokud však v dědici deklarujeme pole se stejným názvem, vytváříme zmatení.

Varování N4

V793 Je podivné, že výsledek výrazu 'imageDirection / 8' je součástí podmínky. Možná by tento výraz měl být porovnán s něčím jiným. libopenrct2 ObservationTower.cpp 38

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

Pojďme se podívat podrobněji. Výraz imageDirection / 8 bude false v případě, že imageDirection je v rozmezí od -7 do 7. Druhá část: (imageDirection / 8) != 3 prověřuje imageDirection vzhledem k tomu, že se nachází mimo rozsah: od -31 do -24 a od 24 do 31. Působí mi to docela divně, kontrolovat čísla na vniknutí do určitého rozsahu tímto způsobem, a i když v tomto úryvku kódu není chyba, doporučoval bych přepsat tato podmínky na jasnější. To by značně usnadnilo život lidem, kteří budou číst a udržovat tento kód.

Varování N5

V587 Podivná sekvence přiřazení tohoto druhu: A = B; B = A;. Zkontrolujte řádky: 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;
      ....
  }
  ....
}

Tento fragment kódu byl pravděpodobně získán dekompilací. Poté byla, jak naznačuje ponechaný komentář, odstraněna část nefunkčního kódu. Nicméně zůstala některá operace nad cursorId, která také nemá výrazný význam.

Upozornění N6

V1004 [CWE-476] Ukazatel 'player' byl použit nebezpečně poté, co byl ověřen proti nullptr. Zkontrolujte řádky: 2085, 2094. libopenrct2 Network.cpp 2094

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Přidat nového hráče.
    player = AddPlayer("", "");
    if (player)                                          // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Tento kód lze poměrně jednoduše opravit, je potřeba buď potřetí zkontrolovat player na nullptr, nebo ho zahrnout do těla podmínkového příkazu. Navrhuji druhou variantu:

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Přidat nového hráče.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}

Upozornění N7

V547 [CWE-570] Výraz 'name == nullptr' je vždy nepravdivý. 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));
    ....
  }
  ....
}

Jedním krokem lze odstranit obtížně čitelný řádek kódu a vyřešit problém s kontrolou na nullptr. Navrhuji změnit kód následujícím způsobem:

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

Upozornění N8

V1048 [CWE-1164] Proměnné 'ColumnHeaderPressedCurrentState' byla přiřazena stejná hodnota. libopenrct2ui CustomListView.cpp 510

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

Kód vypadá docela podivně. Myslím, že došlo k překlepu buď v podmínce, nebo při opakovaném přiřazení proměnné ColumnHeaderPressedCurrentState hodnoty false.

Výstup

Jak vidíme, integrace statického analyzátoru PVS-Studio do projektu na TeamCity je docela jednoduchá. Stačí napsat jeden malý konfigurační soubor. Kontrola kódu umožní odhalit problémy ihned po sestavení, což pomůže je odstranit, když jsou obtížnost a náklady na opravy ještě nízké.

PVS-Studio a neustálá integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.
Pokud chcete sdílet tento článek s anglicky mluvícím publikem, prosím použijte odkaz na překlad: Vladislav Stolyarov. PVS-Studio a kontinuitní integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster