PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
En av de mest aktuella anvĂ€ndningsscenarierna för PVS-Studio-analyzatorn Ă€r dess integration med CI-system. Även om analysen av projektet med PVS-Studio kan integreras i nĂ€stan vilket som helst continuous integration-system med bara nĂ„gra fĂ„ kommandon, fortsĂ€tter vi att göra denna process Ă€nnu bekvĂ€mare. PVS-Studio har fĂ„tt stöd för att konvertera analyzatorns output till TeamCity-format – TeamCity Inspections Type. LĂ„t oss titta pĂ„ hur detta fungerar.

Information om den anvÀnda programvaran

PVS-Studio – en statisk analysator för C, C++, C# och Java-kod, avsedd för att underlĂ€tta sökandet och Ă„tgĂ€rdandet av olika typer av fel. Analyzatorn kan anvĂ€ndas i Windows, Linux och macOS. I denna artikel kommer vi att anvĂ€nda inte bara sjĂ€lva analyzatorn utan Ă€ven vissa verktyg frĂ„n dess distribution.

CLMonitor – Ă€r en övervakningsserver som spĂ„rar körningar av kompilatorer. Den mĂ„ste startas direkt innan byggandet av ditt projekt inleds. I övervakningslĂ€ge kommer servern att fĂ„nga upp körningar av alla stödda kompilatorer. Observera att detta verktyg endast kan anvĂ€ndas för analys av C/C++-projekt.

PlogConverter – ett verktyg för att konvertera analyzatorns rapport till olika format.

Information om det undersökta projektet

LĂ„t oss prova denna funktionalitet med ett praktiskt exempel – vi ska analysera projektet OpenRCT2.

OpenRCT2 – en öppen implementation av spelet RollerCoaster Tycoon 2 (RCT2), som utökar det med nya funktioner och Ă„tgĂ€rdar fel. Spelmekaniken kretsar kring byggandet och skötseln av en nöjespark dĂ€r det finns attraktioner, butiker och objekt. Spelaren mĂ„ste strĂ€va efter att göra en vinst och upprĂ€tthĂ„lla ett gott rykte för parken, samtidigt som gĂ€sterna hĂ„lls nöjda. OpenRCT2 tillĂ„ter att spela antingen i scenarielĂ€get eller i sandlĂ„dan. Scenarier krĂ€ver att spelaren fullföljer en viss uppgift inom en angiven tidsram, medan sandlĂ„dan tillĂ„ter spelaren att bygga en mer flexibel park utan nĂ„gra begrĂ€nsningar eller ekonomiska hinder.

InstÀllning

För att spara tid kommer jag att hoppa över installationsprocessen och börja frÄn det ögonblicket dÄ servern TeamCity Àr igÄng pÄ min dator. Vi behöver gÄ till: localhost:{angett under installationsprocessen port}(i mitt fall, localhost:9090) och ange autentiseringsuppgifter. Efter inloggning möts vi av:

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Klicka pÄ knappen Skapa projekt. DÀrefter vÀljer vi Manuellt och fyller i fÀlten.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Efter att ha tryckt pÄ knappen Skapa, möts vi av ett fönster med instÀllningar.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Klicka Skapa byggkonfiguration.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Fyll i fÀlten, klicka pÄ Skapa. Vi ser ett fönster med förslag pÄ versionskontrollsystem. Eftersom kÀllkoden redan finns lokalt, klicka pÄ Hoppa över.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Till sist gÄr vi vidare till projektsinstÀllningarna.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
LÀgg till byggelement, för detta klicka pÄ: Byggelement -> LÀgg till byggelement.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
HÀr vÀljer vi:

  • Körartyp -> Kommando Rad
  • Kör -> Anpassad skript

Eftersom vi kommer att genomföra analysen under projektkompileringen, bör bygg och analys vara ett steg, sÄ vi fyller i fÀltet Anpassad skript:

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Vi kommer att stanna vid separata steg senare. Det Àr viktigt att laddningen av analysatorn, byggandet av projektet, dess analys, rapportgenerering och formatering tar totalt elva rader kod.

Sista steget Àr att stÀlla in miljövariabler, för vilka jag har angett vissa vÀgar för att förbÀttra deras lÀsbarhet. För detta gÄr vi till: Parametrar -> LÀgg till ny parameter och lÀgger till tre variabler:

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Det ÄterstÄr bara att klicka pÄ knappen Run i det övre högra hörnet. Medan byggandet och analysen av projektet pÄgÄr, ska jag berÀtta om skriptet.

SjÀlva skriptet

För att börja mÄste vi ladda ner den senaste versionen av PVS-Studio. För detta anvÀnder vi paketchefen Chocolatey. För dem som vill veta mer finns det motsvarande artikeln:

choco install pvs-studio -y

DÀrefter startar vi verktyget för att spÄra byggprojektet CLMonitor.

%CLmon% monitor –-attach

Sedan kommer vi att bygga projektet, som miljövariabeln MSB representerar vÀgen till rÀtt version av MSBuild som jag behöver för att bygga

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

Vi anger anvÀndarnamn och licensnyckel för PVS-Studio:

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

Efter bygget kör vi CLMonitor igen för att generera preprocessade filer och statisk analys:

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

EfterÄt anvÀnder vi Ànnu ett verktyg frÄn vÄrt distributionspaket. PlogConverter konverterar rapporten frÄn standardformat till TeamCity-specifikt format. Tack vare detta kan vi titta pÄ den direkt i byggfönstret.

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

Som sista ÄtgÀrd skriver vi ut den formaterade rapporten i stdout, dÀr den plockas upp av TeamCity-parsern.

type "C:tempptest.plog_TeamCity.txt"

FullstÀndig kod av skriptet:

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"

Under tiden har byggandet och analysen av projektet slutförts framgÄngsrikt, sÄ vi kan gÄ till fliken Projekt och bekrÀfta detta.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
LÄt oss nu klicka pÄ Inspections Total, för att gÄ till analysrapporten:

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Varningar Àr grupperade efter diagnostiska regelnummer. För att navigera i koden, klicka pÄ radnumret med varningen. Att klicka pÄ frÄgetecknet i det övre högra hörnet öppnar en ny flik med dokumentation. Du kan ocksÄ navigera i koden genom att klicka pÄ radnumret med analysatorns varning. Navigering frÄn en fjÀrrdator Àr möjlig genom att anvÀnda SourceTreeRoot markören. De som Àr intresserade av denna analysatormodus kan lÀsa avsnittet om dokumentationen.

Visa analysresultat

Efter att ha slutfört distributionen och konfigurationen av bygget tÀnker jag att vi kan titta pÄ nÄgra intressanta varningar som upptÀcktes i det granskade projektet.

Varning N1

V773 [CWE-401] Undantaget kastades utan att frigöra 'result'-pekaren. En minneslÀcka kan vara möjlig. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("Objektet har fel");
  }
  ....
}

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("Ogiltig objekttyp");
  }
  return result;
}

Analysatorn noterade ett fel, nÀmligen att efter dynamisk minnesallokering i CreateObject, vid uppkomsten av ett undantag stÀdas inte minnet, vilket resulterar i en minneslÀcka.

Varning N2

V501 Det finns identiska sub-uttryck '(1ULL << WIDX_MONTH_BOX)' till vÀnster och till höger om '|' operatorn. 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),
  ....
};

Det Àr fÄ som, förutom en statisk analysator, skulle klara detta uppmÀrksamhetstest. Detta exempel pÄ kopiering och klisjéer Àr bra just dÀrför.

Varning N3

V703 Det Àr mÀrkligt att fÀltet 'flags' i den hÀrledda klassen 'RCT12BannerElement' överskrider fÀltet i basklassen 'RCT12TileElementBase'. Kontrollera rader: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

SjÀlvklart Àr det inte alltid ett misstag att anvÀnda en variabel med samma namn i basklassen och den hÀrledda klassen. Men arvsteknologi förutsÀtter att alla fÀlt frÄn basklassen ocksÄ finns i den hÀrledda klassen. Genom att deklarera fÀlt med samma namn i den hÀrledda klassen skapar vi förvirring.

Varning N4

V793 Det Àr mÀrkligt att resultatet av uttalandet 'imageDirection / 8' ingÄr som en del av villkoret. Kanske borde detta uttalande ha jÀmförts med nÄgot annat. libopenrct2 ObservationTower.cpp 38

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

LÄt oss gÄ igenom det mer noggrant. Uttrycket imageDirection / 8 kommer att vara false om imageDirection ligger inom intervallet -7 till 7. Andra delen: (imageDirection / 8) != 3 kontrollerar imageDirection om det ligger utanför intervallet: frÄn -31 till -24 och frÄn 24 till 31. Jag tycker att det Àr ganska mÀrkligt att kontrollera tal för att se om de ligger inom ett visst intervall pÄ det hÀr sÀttet och Àven om det inte finns nÄgot fel i denna kodfragment skulle jag rekommendera att omskriva dessa villkor till mer tydliga former. Det skulle avsevÀrt förenkla livet för dem som lÀser och underhÄller denna kod.

Varning N5

V587 En mÀrklig sekvens av tilldelningar av den hÀr typen: A = B; B = A;. Kontrollera rader: 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;
      ....
  }
  ....
}

Denna kodsnutt har troligtvis erhÄllits genom dekompilering. UtifrÄn den kvarlÀmnade kommentaren verkar det som att en del av den icke-fungerande koden har tagits bort. Det finns dock nÄgra operationer kvar som cursorId, vilka inte heller bÀr nÄgon sÀrskild mening.

Varning N6

V1004 [CWE-476] Pekaren 'player' anvÀndes osÀkert efter att den kontrollerades mot nullptr. Kontrollera rader: 2085, 2094. libopenrct2 Network.cpp 2094

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // LĂ€gg till ny spelare.
    player = AddPlayer("", "");
    if (player)                                          // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Denna kod Àr ganska enkel att rÀtta till, antingen genom att kontrollera player mot nullpekare för tredje gÄngen, eller genom att inkludera det i kroppen av villkorssatsen. Jag skulle föreslÄ det andra alternativet:

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // LĂ€gg till ny spelare.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}

Varning N7

V547 [CWE-570] Uttrycket 'name == nullptr' Àr alltid falskt. 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));
    ....
  }
  ....
}

Man kan pÄ en gÄng bli av med den svÄrlÀsta kodraden och lösa problemet med kontrollen pÄ nullptr. Jag föreslÄr att koden Àndras pÄ följande sÀtt:

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

Varning N8

V1048 [CWE-1164] Variabeln 'ColumnHeaderPressedCurrentState' tilldelades samma vÀrde. libopenrct2ui CustomListView.cpp 510

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

Koden verkar ganska konstig. Jag tror att det kan ha varit en skrivfel antingen i villkoret eller vid den upprepade tilldelningen av variabeln ColumnHeaderPressedCurrentState vÀrden false.

Utdata

Som vi ser, Àr det ganska enkelt att integrera PVS-Studio statisk analysator i sitt projekt pÄ TeamCity. För detta rÀcker det att skriva en liten konfigurationsfil. Koden kommer att kontrolleras för att identifiera problem direkt efter byggprocessen, vilket hjÀlper till att ÄtgÀrda dem nÀr komplexiteten och kostnaden för Àndringar fortfarande Àr lÄg.

PVS-Studio och Continuous Integration: TeamCity. Analys av projektet Open RollerCoaster Tycoon 2
Om du vill dela denna artikel med en engelsktalande publik, vÀnligen anvÀnd lÀnken till översÀttningen: Vladislav Stolyarov. PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster