PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Jedným z najaktuálnejších scenárov používania analyzátora PVS-Studio je jeho integrácia so systémami CI. A hoci analýza projektu PVS-Studio z takmer akéhokoľvek systému kontinuálnej integrácie môže byť zabudovaná len do niekoľkých príkazov, naďalej robíme tento proces ešte pohodlnejším. PVS-Studio teraz podporuje konverziu výstupu analyzátora do formátu pre TeamCity - TeamCity Inspections Type. Pozrime sa, ako to funguje.

Informácie o použitom softvéri

Štúdio PVS — statický analyzátor kódu C, C++, C# a Java, určený na uľahčenie úlohy hľadania a opravy rôznych typov chýb. Analyzátor je možné použiť na Windows, Linux a macOS. V tomto článku budeme aktívne používať nielen samotný analyzátor, ale aj niektoré nástroje z jeho distribúcie.

CLMonitor — je monitorovací server, ktorý monitoruje spúšťanie kompilátora. Musí sa spustiť bezprostredne pred začatím vytvárania vášho projektu. V monitorovacom režime bude server zachytávať behy všetkých podporovaných kompilátorov. Stojí za zmienku, že tento nástroj možno použiť iba na analýzu projektov C/C++.

PlogConverter – pomôcka na konverziu správ analyzátora do rôznych formátov.

Informácie o skúmanom projekte

Skúsme si túto funkcionalitu vyskúšať na praktickom príklade – analyzujme projekt OpenRCT2.

OpenRCT2 - otvorená implementácia hry RollerCoaster Tycoon 2 (RCT2), ktorá ju rozširuje o nové funkcie a opravuje chyby. Hra sa točí okolo budovania a udržiavania zábavného parku obsahujúceho jazdy, obchody a zariadenia. Hráč sa musí snažiť zarobiť a udržať si dobrú povesť parku a zároveň udržať hostí spokojných. OpenRCT2 vám umožňuje hrať v scenári aj v karanténe. Scenáre vyžadujú, aby hráč dokončil konkrétnu úlohu v stanovenom čase, zatiaľ čo Sandbox umožňuje hráčovi vybudovať flexibilnejší park bez akýchkoľvek obmedzení alebo financií.

nastavenie

Aby som ušetril čas, pravdepodobne preskočím inštalačný proces a začnem od chvíle, keď mám na počítači spustený server TeamCity. Musíme prejsť na: localhost:{port zadaný počas procesu inštalácie} (v mojom prípade localhost:9090) a zadať autorizačné údaje. Po vstupe nás privíta:

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Kliknite na tlačidlo Vytvoriť projekt. Ďalej vyberte možnosť Manuálne a vyplňte polia.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Po stlačení tlačidla vytvoriť, privíta nás okno s nastaveniami.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Poďme kliknúť Vytvorte konfiguráciu zostavy.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Vyplňte polia a kliknite vytvoriť. Vidíme okno s výzvou na výber systému správy verzií. Keďže zdroje sú už umiestnené lokálne, kliknite Preskočiť.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Nakoniec prejdeme k nastaveniam projektu.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Pridajme montážne kroky, ak to chcete urobiť, kliknite na: Kroky zostavenia -> Pridať krok zostavenia.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Tu vyberáme:

  • Typ bežca -> Príkazový riadok
  • Spustiť -> Vlastný skript

Keďže analýzu vykonáme pri zostavovaní projektu, montáž a analýza by mali byť jedným krokom, preto pole vyplňte Vlastný skript:

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Na jednotlivé kroky sa pozrieme neskôr. Je dôležité, aby načítanie analyzátora, zostavenie projektu, jeho analýza, výstup správy a jej formátovanie zabralo iba jedenásť riadkov kódu.

Posledná vec, ktorú musíme urobiť, je nastaviť premenné prostredia, ktorým som načrtol niekoľko spôsobov, ako zlepšiť ich čitateľnosť. Ak to chcete urobiť, poďme ďalej: Parametre -> Pridať nový parameter a pridajte tri premenné:

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Jediné, čo musíte urobiť, je stlačiť tlačidlo beh v pravom hornom rohu. Zatiaľ čo sa projekt zostavuje a analyzuje, poviem vám o scenári.

Priamo skript

Najprv si musíme stiahnuť najnovšiu distribúciu PVS-Studio. Na tento účel používame správcu balíkov Chocolatey. Pre tých, ktorí sa chcú o tom dozvedieť viac, je tu zodpovedajúci článok:

choco install pvs-studio -y

Ďalej spustíme nástroj na sledovanie zostavenia projektu CLMonitor.

%CLmon% monitor –-attach

Potom vytvoríme projekt ako premennú prostredia MSB je cesta k verzii MSBuild, ktorú potrebujem zostaviť

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

Zadáme prihlasovacie meno a licenčný kľúč pre PVS-Studio:

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

Po dokončení zostavovania znova spustite CLMonitor, aby sa vygenerovali predspracované súbory a statická analýza:

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

Potom použijeme inú utilitu z našej distribúcie. PlogConverter konvertuje správu zo štandardného formátu do formátu špecifického pre TeamCity. Vďaka tomu si ho budeme môcť pozrieť priamo v okne zostavenia.

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

Posledným krokom je zobrazenie naformátovanej zostavy stdout, kde ho prevezme 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"

Medzitým je montáž a analýza projektu úspešne dokončená, môžeme prejsť na kartu projekty a presvedčte sa o tom.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Teraz kliknime ďalej Kontroly Celkomprejdite na zobrazenie správy analyzátora:

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Varovania sú zoskupené podľa čísel diagnostických pravidiel. Ak chcete prechádzať kódom, musíte kliknúť na číslo riadku s upozornením. Kliknutím na otáznik v pravom hornom rohu sa vám otvorí nová karta s dokumentáciou. V kóde sa môžete pohybovať aj kliknutím na číslo riadku s upozornením analyzátora. Pri používaní je možná navigácia zo vzdialeného počítača SourceTreeRoot marker. Každý, kto má záujem o tento režim činnosti analyzátora, sa môže zoznámiť s príslušnou časťou dokumentáciu.

Zobrazenie výsledkov analyzátora

Teraz, keď sme dokončili nasadenie a konfiguráciu zostavy, pozrime sa na niekoľko zaujímavých upozornení, ktoré sa nachádzajú v projekte, na ktorý sa pozeráme.

Upozornenie N1

V773 [CWE-401] Výnimka bola vyvolaná bez uvoľnenia ukazovateľa „výsledku“. Je možný únik pamäte. 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 si všimol chybu, že po dynamickom pridelení pamäte v CreateObject, keď sa vyskytne výnimka, pamäť sa nevymaže a dôjde k úniku pamäte.

Upozornenie N2

V501 Naľavo a napravo od výrazu „|“ sú rovnaké 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),
  ....
};

Len málo ľudí okrem statického analyzátora by mohlo prejsť týmto testom pozornosti. Tento príklad kopírovania a vkladania je dobrý práve z tohto dôvodu.

Varovania N3

V703 Je zvláštne, že pole 'flags' v odvodenej triede 'RCT12BannerElement' prepisuje pole v základnej triede 'RCT12TileElementBase'. Kontrolné riadky: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Samozrejme, použitie premennej s rovnakým názvom v základnej triede a v potomkovi nie je vždy chyba. Samotná technológia dedenia však predpokladá, že všetky polia nadradenej triedy sú prítomné v podradenej triede. Deklarovaním polí s rovnakým názvom v dedičovi vytvárame zmätok.

Upozornenie N4

V793 Je zvláštne, že výsledok príkazu 'imageDirection / 8' je súčasťou podmienky. Možno by sa toto tvrdenie malo porovnať s niečím iným. libopenrct2 ObservationTower.cpp 38

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

Poďme sa na to pozrieť bližšie. Výraz imageDirection/8 bude falošné, ak imageDirection je v rozsahu od -7 do 7. Druhá časť: (imageDirection / 8) != 3 šeky imageDirection pre mimo rozsahu: od -31 do -24 a od 24 do 31, v tomto poradí. Zdá sa mi dosť zvláštne kontrolovať čísla na zahrnutie do určitého rozsahu týmto spôsobom a aj keď v tomto kúsku kódu nie je žiadna chyba, odporučil by som prepísať tieto podmienky, aby boli jasnejšie. To by značne uľahčilo život ľuďom, ktorí by tento kód čítali a udržiavali.

Upozornenie N5

V587 Nepárna postupnosť priradení tohto druhu: A = B; B = A;. Kontrolné riadky: 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 bol s najväčšou pravdepodobnosťou získaný dekompiláciou. Potom, súdiac podľa zanechaného komentára, bola časť nefunkčného kódu odstránená. Zostáva však ešte niekoľko operácií kurzorId, čo tiež nedáva veľký zmysel.

Upozornenie N6

V1004 [CWE-476] Ukazovateľ 'player' bol použitý nebezpečne po tom, čo bol overený proti nullptr. Kontrolné riadky: 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 sa dá pomerne ľahko opraviť, stačí ho skontrolovať tretíkrát hráč na nulový ukazovateľ alebo ho pridajte do tela podmieneného príkazu. Navrhoval by som druhú možnosť:

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

Upozornenie 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 ťahom sa môžete zbaviť ťažko čitateľného riadku kódu a vyriešiť problém s kontrolou nullptr. Navrhujem zmeniť kód takto:

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

Upozornenie N8

V1048 [CWE-1164] Premennej 'ColumnHeaderPressedCurrentState' bola priradená rovnaká hodnota. libopenrct2ui CustomListView.cpp 510

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

Kód vyzerá dosť zvláštne. Zdá sa mi, že došlo k preklepu buď v podmienke alebo pri opätovnom priraďovaní premennej ColumnHeaderPressedCurrentState význam nepravdivý.

Výkon

Ako vidíme, integrácia statického analyzátora PVS-Studio do vášho projektu TeamCity je pomerne jednoduchá. Na to stačí napísať len jeden malý konfiguračný súbor. Kontrola kódu vám umožní identifikovať problémy ihneď po zostavení, čo ich pomôže odstrániť, keď sú zložitosť a náklady na zmeny stále nízke.

PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2
Ak chcete zdieľať tento článok s anglicky hovoriacim publikom, použite odkaz na preklad: Vladislav Stolyarov. PVS-Studio a nepretržitá integrácia: TeamCity. Analýza projektu Open RollerCoaster Tycoon 2.

Zdroj: hab.com

Pridať komentár