PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Ett av de mest aktuella scenarierna för att använda PVS-Studio-analysatorn är dess integration med CI-system. Och även om analysen av ett PVS-Studio-projekt från nästan alla kontinuerliga integrationssystem kan byggas in i bara några få kommandon, fortsätter vi att göra denna process ännu mer bekväm. PVS-Studio har nu stöd för att konvertera analysatorns utdata till ett format för TeamCity - TeamCity Inspections Type. Låt oss se hur det fungerar.

Information om vilken programvara som används

PVS-studio — en statisk analysator av C, C++, C# och Java-kod, utformad för att underlätta uppgiften att hitta och korrigera olika typer av fel. Analysatorn kan användas på Windows, Linux och macOS. I den här artikeln kommer vi aktivt att använda inte bara själva analysatorn utan också några verktyg från dess distribution.

CLMonitor — är en övervakningsserver som övervakar kompilatorstarter. Det måste köras omedelbart innan du börjar bygga ditt projekt. I snoopningsläge kommer servern att fånga upp körningar av alla kompilatorer som stöds. Det är värt att notera att detta verktyg endast kan användas för att analysera C/C++-projekt.

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

Information om projektet som studeras

Låt oss prova den här funktionen med ett praktiskt exempel - låt oss analysera OpenRCT2-projektet.

ÖppnaRCT2 - en öppen implementering av spelet RollerCoaster Tycoon 2 (RCT2), som utökar det med nya funktioner och fixar buggar. Spelet kretsar kring att bygga och underhålla en nöjespark som innehåller åkattraktioner, butiker och faciliteter. Spelaren måste försöka göra en vinst och behålla parkens goda rykte samtidigt som gästerna är nöjda. OpenRCT2 låter dig spela i både scenario och sandlåda. Scenarier kräver att spelaren slutför en specifik uppgift inom en viss tid, medan Sandbox låter spelaren bygga en mer flexibel park utan några begränsningar eller ekonomi.

justering

För att spara tid kommer jag förmodligen att hoppa över installationsprocessen och börja från det ögonblick då jag har TeamCity-servern igång på min dator. Vi måste gå till: localhost:{port specificerad under installationsprocessen} (i mitt fall, localhost:9090) och ange behörighetsdata. Efter inträde möts vi av:

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Klicka på knappen Skapa projekt. Välj sedan Manuellt och fyll i fälten.

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

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Låt oss klicka Skapa byggkonfiguration.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Fyll i fälten och klicka Skapa. Vi ser ett fönster som ber dig välja ett versionskontrollsystem. Eftersom källorna redan finns lokalt, klicka Hoppa.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Slutligen går vi vidare till projektinställningarna.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Låt oss lägga till monteringssteg, för att göra så här klicka: Byggsteg -> Lägg till byggsteg.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Här väljer vi:

  • Löpare typ -> Kommandorad
  • Kör -> Anpassat skript

Eftersom vi kommer att utföra analys under projektsammanställningen bör montering och analys vara ett steg, så fyll i fältet Anpassat skript:

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Vi kommer att titta på enskilda steg senare. Det är viktigt att ladda analysatorn, sätta ihop projektet, analysera det, mata ut rapporten och formatera den bara tar elva rader kod.

Det sista vi behöver göra är att ställa in miljövariablerna, som jag har beskrivit några sätt att förbättra deras läsbarhet. För att göra detta, låt oss gå vidare: Parametrar -> Lägg till ny parameter och lägg till tre variabler:

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Allt du behöver göra är att trycka på knappen Körning i det övre högra hörnet. Medan projektet håller på att monteras och analyseras kommer jag att berätta om manuset.

Manus direkt

Först måste vi ladda ner den senaste PVS-Studio-distributionen. För detta använder vi Chocolatey package manager. För den som vill veta mer om detta finns motsvarande artikel:

choco install pvs-studio -y

Låt oss sedan lansera spårningsverktyget för CLMonitor-projektet.

%CLmon% monitor –-attach

Sedan kommer vi att bygga projektet som en miljövariabel MSB är sökvägen till den version av MSBuild jag behöver bygga

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

Låt oss ange inloggnings- och licensnyckeln för PVS-Studio:

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

När konstruktionen är klar, kör CLMonitor igen för att generera förbearbetade filer och statisk analys:

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

Sedan kommer vi att använda ett annat verktyg från vår distribution. PlogConverter konverterar en rapport från ett standardformat till ett TeamCity-specifikt format. Tack vare detta kommer vi att kunna se det direkt i byggfönstret.

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

Det sista steget är att visa den formaterade rapporten i stdout, där det kommer att hämtas av TeamCity-parsern.

type "C:tempptest.plog_TeamCity.txt"

Fullständig skriptkod:

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 monteringen och analysen av projektet slutförts framgångsrikt, vi kan gå till fliken Projekt och se till det.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Låt oss nu klicka vidare Besiktningar Totaltför att gå till att visa analysrapporten:

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Varningar är grupperade efter diagnostiska regelnummer. För att navigera genom koden måste du klicka på radnumret med varningen. Genom att klicka på frågetecknet i det övre högra hörnet öppnas en ny flik med dokumentation. Du kan också navigera genom koden genom att klicka på radnumret med analysatorvarningen. Navigering från en fjärrdator är möjlig när du använder SourceTreeRoot markör. Alla som är intresserade av detta funktionssätt för analysatorn kan bekanta sig med motsvarande avsnitt dokumentation.

Visa analysatorns resultat

Nu när vi är klara med att distribuera och konfigurera bygget, låt oss ta en titt på några intressanta varningar som finns i projektet vi tittar på.

Varning N1

V773 [CWE-401] Undantaget kastades utan att släppa "resultat"-pekaren. En minnesläcka är möjlig. 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;
}

Analysatorn märkte ett fel som efter att dynamiskt allokerat minne i Skapa objekt, när ett undantag inträffar rensas inte minnet och en minnesläcka inträffar.

Varning N2

V501 Det finns identiska underuttryck '(1ULL << WIDX_MONTH_BOX)' till vänster och till höger om '|' operatör. 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),
  ....
};

Få andra än en statisk analysator kunde klara detta uppmärksamhetstest. Det här copy-paste-exemplet är bra av just detta skäl.

Varningar N3

V703 Det är konstigt att 'flagga'-fältet i den härledda klassen 'RCT12BannerElement' skriver över fältet i basklassen 'RCT12TileElementBase'. Kontrolllinjer: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Att använda en variabel med samma namn i basklassen och i descendent är naturligtvis inte alltid ett fel. Dock förutsätter arvsteknologin i sig att alla fält i föräldraklassen finns i barnklassen. Genom att deklarera fält med samma namn i arvtagaren inför vi förvirring.

Varning N4

V793 Det är konstigt att resultatet av "imageDirection / 8"-satsen är 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 ta en närmare titt. Uttryck imageDirection/8 kommer att vara falskt om bildriktning ligger i intervallet från -7 till 7. Andra delen: (imageDirection / 8) != 3 kontroller bildriktning för att ligga utanför intervallet: från -31 till -24 respektive från 24 till 31. Det förefaller mig ganska konstigt att kontrollera siffror för inkludering i ett visst intervall på det här sättet, och även om det inte finns något fel i denna kod, skulle jag rekommendera att skriva om dessa villkor för att vara mer explicita. Detta skulle göra livet mycket lättare för de människor som skulle läsa och underhålla den här koden.

Varning N5

V587 En udda sekvens av uppdrag av detta slag: 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;
      ....
  }
  ....
}

Detta kodfragment erhölls troligen genom dekompilering. Sedan, att döma av kommentaren som lämnades, togs en del av den icke-fungerande koden bort. Det återstår dock fortfarande ett par operationer cursorId, vilket inte heller är så vettigt.

Varning N6

V1004 [CWE-476] 'Player'-pekaren användes på ett osäkert sätt efter att den verifierats mot nullptr. Kontrollera rader: 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);                    // <=
  }
  ....
}

Den här koden är ganska lätt att korrigera, du behöver bara kontrollera den en tredje gång Spelaren till en nollpekare, eller lägg till den i brödtexten i det villkorliga uttalandet. Jag skulle föreslå det andra alternativet:

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

Varning N7

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

Du kan bli av med en svårläst kodrad i ett svep och lösa problemet med att kontrollera nullptr. Jag föreslår att du ändrar koden enligt följande:

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

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 ser ganska konstig ut. Det verkar för mig att det fanns ett stavfel antingen i villkoret eller när variabeln tilldelades om ColumnHeaderPressedCurrentState betyder falsk.

Utgång

Som vi kan se är det ganska enkelt att integrera den statiska analysatorn PVS-Studio i ditt TeamCity-projekt. För att göra detta räcker det att bara skriva en liten konfigurationsfil. Genom att kontrollera koden kan du identifiera problem omedelbart efter montering, vilket hjälper till att eliminera dem när komplexiteten och kostnaden för ändringar fortfarande är låga.

PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet
Om du vill dela den här artikeln med en engelsktalande publik, använd gärna översättningslänken: Vladislav Stolyarov. PVS-Studio och kontinuerlig integration: TeamCity. Analys av Open RollerCoaster Tycoon 2-projektet.

Källa: will.com

Lägg en kommentar