PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Een van de meest actuele scenario's voor het gebruik van de PVS-Studio-analysator is de integratie met CI-systemen. En hoewel de analyse van een PVS-Studio-project vanuit vrijwel elk continu integratiesysteem in slechts een paar commando's kan worden ingebouwd, blijven we dit proces nog handiger maken. PVS-Studio biedt nu ondersteuning voor het converteren van de uitvoer van de analyser naar een formaat voor TeamCity - TeamCity Inspections Type. Laten we kijken hoe het werkt.

Informatie over de gebruikte software

PVS Studio — een statische analysator van C-, C++-, C#- en Java-code, ontworpen om de taak van het vinden en corrigeren van verschillende soorten fouten te vergemakkelijken. De analyser kan worden gebruikt op Windows, Linux en macOS. In dit artikel zullen we niet alleen de analysator zelf actief gebruiken, maar ook enkele hulpprogramma's uit de distributie ervan.

CLMonitor — is een monitoringserver die het starten van compilers controleert. Het moet onmiddellijk worden uitgevoerd voordat u met de bouw van uw project begint. In de snooping-modus onderschept de server runs van alle ondersteunde compilers. Het is vermeldenswaard dat dit hulpprogramma alleen kan worden gebruikt om C/C++-projecten te analyseren.

PlogConverter – een hulpprogramma voor het converteren van analyserapporten naar verschillende formaten.

Informatie over het onderzochte project

Laten we deze functionaliteit proberen met een praktisch voorbeeld - laten we het OpenRCT2-project analyseren.

RCT2 openen - een open implementatie van het spel RollerCoaster Tycoon 2 (RCT2), dat het uitbreidt met nieuwe functies en bugs oplost. De gameplay draait om het bouwen en onderhouden van een pretpark met attracties, winkels en faciliteiten. De speler moet proberen winst te maken en de goede reputatie van het park in stand te houden en tegelijkertijd de gasten tevreden te houden. Met OpenRCT2 kun je zowel in een scenario als in een sandbox spelen. Scenario's vereisen dat de speler een specifieke taak binnen een bepaalde tijd voltooit, terwijl Sandbox de speler in staat stelt een flexibeler park te bouwen zonder enige beperkingen of financiën.

afstelling

Om tijd te besparen sla ik waarschijnlijk het installatieproces over en begin ik vanaf het moment dat ik de TeamCity-server op mijn computer heb draaien. We moeten naar: localhost:{poort gespecificeerd tijdens het installatieproces} (in mijn geval localhost:9090) en autorisatiegegevens invoeren. Na binnenkomst worden wij begroet door:

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Klik op de knop Project aanmaken. Selecteer vervolgens Handmatig en vul de velden in.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Na het indrukken van de knop creëren, worden we begroet door een venster met instellingen.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Laten we klikken Maak een buildconfiguratie.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Vul de velden in en klik creëren. We zien een venster waarin u wordt gevraagd een versiebeheersysteem te selecteren. Omdat de bronnen zich al lokaal bevinden, klikt u op overslaan.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Ten slotte gaan we verder met de projectinstellingen.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Laten we montagestappen toevoegen. Klik hiervoor op: Bouwstappen -> Bouwstap toevoegen.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Hier kiezen wij:

  • Runner-type -> Commandoregel
  • Uitvoeren -> Aangepast script

Omdat we analyses zullen uitvoeren tijdens het samenstellen van het project, moeten assemblage en analyse één stap zijn, dus vul het veld in Aangepast script:

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
We zullen later naar de afzonderlijke stappen kijken. Het is belangrijk dat het laden van de analyser, het samenstellen van het project, het analyseren ervan, het uitvoeren van het rapport en het formatteren ervan slechts elf regels code vergt.

Het laatste dat we moeten doen is de omgevingsvariabelen instellen, waarvan ik enkele manieren heb geschetst om hun leesbaarheid te verbeteren. Om dit te doen, gaan we verder: Parameters -> Nieuwe parameter toevoegen en voeg drie variabelen toe:

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Het enige dat u hoeft te doen, is op de knop drukken lopen in de rechterbovenhoek. Terwijl het project wordt samengesteld en geanalyseerd, zal ik je over het script vertellen.

Direct script

Eerst moeten we de nieuwste PVS-Studio-distributie downloaden. Hiervoor gebruiken we de Chocolatey pakketbeheerder. Voor degenen die hier meer over willen weten, is er een overeenkomstige artikel:

choco install pvs-studio -y

Laten we vervolgens het CLMonitor-hulpprogramma voor het volgen van projectbuilds starten.

%CLmon% monitor –-attach

Vervolgens bouwen we het project als een omgevingsvariabele MSB is het pad naar de versie van MSBuild die ik moet bouwen

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

Laten we de login- en licentiesleutel voor PVS-Studio invoeren:

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

Nadat de build is voltooid, voert u CLMonitor opnieuw uit om voorbewerkte bestanden en statische analyses te genereren:

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

Dan zullen we een ander hulpprogramma uit onze distributie gebruiken. PlogConverter converteert een rapport van een standaardformaat naar een TeamCity-specifiek formaat. Hierdoor kunnen we het rechtstreeks in het buildvenster bekijken.

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

De laatste stap is het weergeven van het opgemaakte rapport stdout, waar het zal worden opgepikt door de TeamCity-parser.

type "C:tempptest.plog_TeamCity.txt"

Volledige scriptcode:

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"

Inmiddels is de montage en analyse van het project succesvol afgerond, we kunnen naar het tabblad gaan Projecten en едиться в ом.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Laten we nu verder klikken Inspecties Totaalom naar het analyserrapport te gaan:

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Waarschuwingen zijn gegroepeerd op diagnostische regelnummers. Om door de code te navigeren, klikt u op het regelnummer met de waarschuwing. Als u op het vraagteken in de rechterbovenhoek klikt, wordt een nieuw tabblad met documentatie geopend. U kunt ook door de code navigeren door op het regelnummer met de analysatorwaarschuwing te klikken. Navigatie vanaf een externe computer is mogelijk bij gebruik BronBoomwortel markeerstift. Iedereen die geïnteresseerd is in deze werkingsmodus van de analysator, kan zich vertrouwd maken met de overeenkomstige sectie documentatie.

De resultaten van de analysator bekijken

Nu we klaar zijn met het implementeren en configureren van de build, gaan we eens kijken naar enkele interessante waarschuwingen in het project waar we naar kijken.

Waarschuwing N1

V773 [CWE-401] De uitzondering is gegenereerd zonder dat de 'resultaat'-aanwijzer werd vrijgegeven. Een geheugenlek is mogelijk. 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;
}

De analysator heeft een fout opgemerkt die na het dynamisch toewijzen van geheugen in Object makenWanneer er een uitzondering optreedt, wordt het geheugen niet gewist en treedt er een geheugenlek op.

Waarschuwing N2

V501 Er zijn identieke subexpressies '(1ULL << WIDX_MONTH_BOX)' links en rechts van de '|' exploitant. 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),
  ....
};

Er zijn maar weinig andere mensen dan een statische analysator die deze aandachtstest kunnen doorstaan. Dit copy-paste-voorbeeld is juist om deze reden goed.

Waarschuwingen N3

V703 Het is vreemd dat het veld 'vlaggen' in de afgeleide klasse 'RCT12BannerElement' het veld in de basisklasse 'RCT12TileElementBase' overschrijft. Controleer lijnen: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Natuurlijk is het gebruik van een variabele met dezelfde naam in de basisklasse en in de afstammeling niet altijd een fout. De overervingstechnologie zelf gaat er echter van uit dat alle velden van de ouderklasse aanwezig zijn in de kindklasse. Door velden met dezelfde naam bij de erfgenaam aan te geven, introduceren we verwarring.

Waarschuwing N4

V793 Het is vreemd dat het resultaat van de instructie 'imageDirection / 8' deel uitmaakt van de voorwaarde. Misschien had deze verklaring met iets anders moeten worden vergeleken. libopenrct2 ObservationTower.cpp 38

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

Laten we dat eens van dichterbij bekijken. Uitdrukking afbeeldingRichting/8 zal vals zijn als afbeeldingRichting ligt in het bereik van -7 tot 7. Tweede deel: (imageDirection / 8) != 3 cheques afbeeldingRichting omdat ze buiten het bereik vallen: respectievelijk van -31 tot -24 en van 24 tot 31. Het lijkt mij nogal vreemd om op deze manier getallen te controleren op opname in een bepaald bereik en zelfs als er geen fout in dit stukje code zit, zou ik aanraden deze voorwaarden te herschrijven om explicieter te zijn. Dit zou het leven veel gemakkelijker maken voor de mensen die deze code zouden lezen en onderhouden.

Waarschuwing N5

V587 Een vreemde reeks van dit soort opdrachten: A = B; B = EEN;. Controleer lijnen: 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;
      ....
  }
  ....
}

Dit codefragment is hoogstwaarschijnlijk verkregen door decompilatie. Vervolgens werd, afgaande op de achtergelaten opmerking, een deel van de niet-werkende code verwijderd. Er resten echter nog een aantal operaties cursorId, wat ook niet veel zin heeft.

Waarschuwing N6

V1004 [CWE-476] De 'speler'-aanwijzer werd op onveilige wijze gebruikt nadat deze was geverifieerd tegen nullptr. Controleer lijnen: 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);                    // <=
  }
  ....
}

Deze code is vrij eenvoudig te corrigeren; u hoeft hem alleen maar een derde keer te controleren speler naar een null-aanwijzer, of voeg deze toe aan de hoofdtekst van de voorwaardelijke instructie. Ik zou de tweede optie voorstellen:

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

Waarschuwing N7

V547 [CWE-570] Expressie 'name == nullptr' is altijd onwaar. 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));
    ....
  }
  ....
}

U kunt in één klap een moeilijk leesbare regel code verwijderen en het probleem oplossen met het controleren op nulptr. Ik stel voor om de code als volgt te wijzigen:

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

Waarschuwing N8

V1048 [CWE-1164] Aan de variabele 'ColumnHeaderPressedCurrentState' is dezelfde waarde toegewezen. libopenrct2ui CustomListView.cpp 510

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

De code ziet er nogal vreemd uit. Het lijkt mij dat er een typefout is gemaakt in de voorwaarde of bij het opnieuw toewijzen van de variabele ColumnHeaderPressedCurrentState betekenis vals.

Uitgang

Zoals we kunnen zien, is het vrij eenvoudig om de statische analysator van PVS-Studio in uw TeamCity-project te integreren. Om dit te doen, volstaat het om slechts één klein configuratiebestand te schrijven. Door de code te controleren, kunt u problemen onmiddellijk na de montage identificeren, waardoor u ze kunt elimineren wanneer de complexiteit en de kosten van wijzigingen nog laag zijn.

PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project
Als u dit artikel met een Engelssprekend publiek wilt delen, gebruik dan de vertaallink: Vladislav Stolyarov. PVS-Studio en continue integratie: TeamCity. Analyse van het Open RollerCoaster Tycoon 2-project.

Bron: www.habr.com

Voeg een reactie