PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2

PVS-Studio a průběžná 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 se systémy CI. A přestože analýzu projektu PVS-Studio z téměř jakéhokoli systému kontinuální integrace lze zabudovat do několika příkazů, i nadále je tento proces ještě pohodlnější. PVS-Studio nyní podporuje převod výstupu analyzátoru do formátu pro TeamCity – TeamCity Inspections Type. Pojďme se podívat, jak to funguje.

Informace o použitém softwaru

Studio PVS — statický analyzátor kódu C, C++, C# a Java, navržený tak, aby usnadnil hledání a opravu různých typů chyb. Analyzátor lze použít na Windows, Linux a macOS. V tomto článku budeme aktivně používat nejen samotný analyzátor, ale také některé nástroje z jeho distribuce.

CLMonitor — je monitorovací server, který monitoruje spouštění kompilátoru. Musí být spuštěn bezprostředně před zahájením budování vašeho projektu. V režimu snooping bude server zachycovat běhy všech podporovaných kompilátorů. Stojí za zmínku, že tento nástroj lze použít pouze k analýze projektů C/C++.

PlogConverter – nástroj pro převod zpráv analyzátoru do různých formátů.

Informace o studovaném projektu

Pojďme si tuto funkcionalitu vyzkoušet na praktickém příkladu – pojďme si rozebrat projekt OpenRCT2.

OpenRCT2 - otevřená implementace hry RollerCoaster Tycoon 2 (RCT2), která ji rozšiřuje o nové funkce a opravuje chyby. Hra se točí kolem budování a údržby zábavního parku obsahujícího atrakce, obchody a zařízení. Hráč se musí snažit vydělat a udržet dobrou pověst parku a zároveň udržet hosty spokojené. OpenRCT2 vám umožňuje hrát ve scénáři i v sandboxu. Scénáře vyžadují, aby hráč dokončil konkrétní úkol ve stanoveném čase, zatímco Sandbox umožňuje hráči vybudovat flexibilnější park bez jakýchkoli omezení nebo financí.

Nastavení

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

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Klikněte na tlačítko Vytvořit projekt. Dále vyberte Ručně a vyplňte pole.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Po stisknutí tlačítka Vytvořit, přivítá nás okno s nastavením.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Pojďme kliknout Vytvořte konfiguraci sestavení.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Vyplňte pole a klikněte Vytvořit. Vidíme okno s výzvou k výběru systému správy verzí. Protože jsou zdroje již umístěny lokálně, klikněte na Přeskočit.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Nakonec přejdeme k nastavení projektu.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Přidáme kroky sestavení, provedete to kliknutím: Kroky sestavení -> Přidat krok sestavení.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Zde vybíráme:

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

Vzhledem k tomu, že analýzu budeme provádět při sestavování projektu, montáž a analýza by měly být jedním krokem, proto pole vyplňte Vlastní skript:

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Na jednotlivé kroky se podíváme později. Je důležité, že načtení analyzátoru, sestavení projektu, jeho analýza, výstup zprávy a její formátování zabere pouze jedenáct řádků kódu.

Poslední věc, kterou musíme udělat, je nastavit proměnné prostředí, kterým jsem nastínil několik způsobů, jak zlepšit jejich čitelnost. Chcete-li to provést, pojďme dále: Parametry -> Přidat nový parametr a přidejte tři proměnné:

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Jediné, co musíte udělat, je stisknout tlačítko Běh v pravém horním rohu. Zatímco se projekt skládá a analyzuje, povím vám o scénáři.

Přímo skript

Nejprve si musíme stáhnout nejnovější distribuci PVS-Studio. K tomu používáme správce balíčků Chocolatey. Pro ty, kteří se o tom chtějí dozvědět více, existuje odpovídající článek:

choco install pvs-studio -y

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

%CLmon% monitor –-attach

Poté projekt postavíme jako proměnnou prostředí MSB je cesta k verzi MSBuild, kterou potřebuji sestavit

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

Zadáme přihlašovací a licenční klíč pro PVS-Studio:

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

Po dokončení sestavení spusťte znovu CLMonitor a vygenerujte předzpracované soubory a statickou analýzu:

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

Pak použijeme jinou utilitu z naší distribuce. PlogConverter převede zprávu ze standardního formátu do formátu specifického pro TeamCity. Díky tomu si jej budeme moci prohlédnout přímo v okně sestavení.

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

Posledním krokem je zobrazení naformátované sestavy stdout, kde jej vyzvedne analyzátor TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Úplný 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 úspěšně dokončena montáž a analýza projektu, můžeme přejít na záložku Projekty и убедиться v эtom.

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Nyní klikneme na Kontroly celkempřejděte k zobrazení zprávy analyzátoru:

PVS-Studio a průběžná integrace: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Varování jsou seskupena podle čísel diagnostických pravidel. Chcete-li procházet kódem, musíte kliknout na číslo řádku s upozorněním. Kliknutím na otazník v pravém horním rohu se vám otevře nová karta s dokumentací. Kód můžete také procházet kliknutím na číslo řádku s varováním analyzátoru. Při použití je možná navigace ze vzdáleného počítače SourceTreeRoot popisovač. Každý, kdo má zájem o tento způsob provozu analyzátoru, se může seznámit s odpovídající částí dokumentace.

Zobrazení výsledků analyzátoru

Nyní, když jsme dokončili nasazení a konfiguraci sestavení, pojďme se podívat na některá zajímavá varování nalezená v projektu, na který se díváme.

Varování N1

V773 [CWE-401] Výjimka byla vyvolána bez uvolnění ukazatele 'výsledku'. Je možný únik paměti. 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;
}

Analyzátor zaznamenal chybu, která po dynamickém přidělování paměti v CreateObject, když dojde k výjimce, paměť se nevymaže a dojde k nevracení paměti.

Varování N2

V501 Nalevo a napravo od '|' jsou identické podvýrazy '(1ULL << WIDX_MONTH_BOX)' operátor. libopenrct2ui Cheaty.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),
  ....
};

Jen málo lidí kromě statického analyzátoru by mohlo projít tímto testem pozornosti. Tento příklad kopírování a vkládání je dobrý právě z tohoto důvodu.

Varování N3

V703 Je zvláštní, že pole 'flags' v odvozené třídě 'RCT12BannerElement' přepisuje pole v základní třídě 'RCT12TileElementBase'. Kontrolní řá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ě, že použití proměnné se stejným názvem v základní třídě a v potomkovi není vždy chyba. Samotná technologie dědičnosti však předpokládá, že všechna pole nadřazené třídy jsou přítomna v podřízené třídě. Prohlášením polí se stejným názvem v dědici vnášíme zmatek.

Varování N4

V793 Je zvláštní, že výsledek příkazu 'imageDirection / 8' je součástí podmínky. Možná měl být tento výrok přirovnán k něčemu jinému. libopenrct2 ObservationTower.cpp 38

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

Pojďme se na to blíže podívat. Výraz imageDirection/8 bude nepravdivé, pokud imageDirection je v rozsahu od -7 do 7. Druhá část: (imageDirection / 8) != 3 kontroly imageDirection mimo rozsah: od -31 do -24 a od 24 do 31, v tomto pořadí. Zdá se mi docela zvláštní kontrolovat čísla pro zahrnutí v určitém rozsahu tímto způsobem, a i když v tomto kusu kódu není žádná chyba, doporučil bych přepsat tyto podmínky, aby byly jasnější. To by výrazně usnadnilo život lidem, kteří by tento kód četli a udržovali ho.

Varování N5

V587 Lichá posloupnost přiřazení tohoto druhu: A = B; B = A;. Kontrolní řá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 s největší pravděpodobností získán dekompilací. Poté, soudě podle zanechaného komentáře, byla část nefunkčního kódu odstraněna. Zbývá však ještě několik operací id kurzoru, což také nedává moc smysl.

Varování N6

V1004 [CWE-476] Ukazatel 'player' byl použit nebezpečně poté, co byl ověřen proti nullptr. Kontrolní řádky: 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);                    // <=
  }
  ....
}

Tento kód je poměrně snadné opravit, stačí jej zkontrolovat potřetí hráč na nulový ukazatel nebo jej přidejte do těla podmíněného příkazu. Navrhoval bych druhou možnost:

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

Varování N7

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

Jedním tahem se můžete zbavit těžko čitelného řádku kódu a vyřešit problém s kontrolou nullptr. Navrhuji změnit kód následovně:

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

Varová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á dost divně. Zdá se mi, že došlo k překlepu buď v podmínce, nebo při opětovném přiřazení proměnné ColumnHeaderPressedCurrentState hodnoty nepravdivý.

Výkon

Jak vidíme, integrace statického analyzátoru PVS-Studio do vašeho projektu TeamCity je poměrně jednoduchá. K tomu stačí napsat pouze jeden malý konfigurační soubor. Kontrola kódu vám umožní identifikovat problémy ihned po sestavení, což je pomůže odstranit, když jsou složitost a náklady na změny stále nízké.

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

Zdroj: www.habr.com

Přidat komentář