PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Et af de mest aktuelle scenarier for brug af PVS-Studio-analysatoren er dens integration med CI-systemer. Og selvom analysen af ​​et PVS-Studio-projekt fra næsten ethvert kontinuerligt integrationssystem kan indbygges i nogle få kommandoer, fortsætter vi med at gøre denne proces endnu mere bekvem. PVS-Studio har nu understøttelse af konvertering af analysatoroutput til et format for TeamCity - TeamCity Inspections Type. Lad os se, hvordan det virker.

Oplysninger om den anvendte software

PVS Studio — en statisk analysator af C, C++, C# og Java-kode, designet til at lette opgaven med at finde og rette forskellige typer fejl. Analysatoren kan bruges på Windows, Linux og macOS. I denne artikel vil vi aktivt bruge ikke kun selve analysatoren, men også nogle værktøjer fra dens distribution.

CLMonitor — er en overvågningsserver, der overvåger kompileringslanceringer. Det skal køres umiddelbart før du begynder at bygge dit projekt. I snooping-tilstand opsnapper serveren kørsler af alle understøttede compilere. Det er værd at bemærke, at dette værktøj kun kan bruges til at analysere C/C++-projekter.

PlogConverter – et værktøj til at konvertere analysatorrapporter til forskellige formater.

Information om det undersøgte projekt

Lad os prøve denne funktionalitet på et praktisk eksempel - lad os analysere OpenRCT2-projektet.

ÅbenRCT2 - en åben implementering af spillet RollerCoaster Tycoon 2 (RCT2), der udvider det med nye funktioner og fikser fejl. Gameplay kredser om at bygge og vedligeholde en forlystelsespark, der indeholder forlystelser, butikker og faciliteter. Spilleren skal forsøge at tjene penge og bevare parkens gode ry og samtidig holde gæsterne glade. OpenRCT2 giver dig mulighed for at spille i både scenarier og sandkasse. Scenarier kræver, at spilleren udfører en bestemt opgave inden for en fastsat tid, mens Sandbox giver spilleren mulighed for at bygge en mere fleksibel park uden nogen begrænsninger eller økonomi.

justering

For at spare tid vil jeg sandsynligvis springe installationsprocessen over og starte fra det øjeblik, hvor jeg har TeamCity-serveren kørende på min computer. Vi skal gå til: localhost:{port specificeret under installationsprocessen} (i mit tilfælde localhost:9090) og indtaste autorisationsdata. Efter indtastning vil vi blive mødt af:

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Klik på knappen Opret projekt. Vælg derefter Manuelt og udfyld felterne.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Efter at have trykket på knappen Opret, bliver vi mødt af et vindue med indstillinger.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Lad os klikke Opret build-konfiguration.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Udfyld felterne og klik Opret. Vi ser et vindue, der beder dig vælge et versionskontrolsystem. Da kilderne allerede er lokaliseret, skal du klikke Spring.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Til sidst går vi videre til projektindstillingerne.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Lad os tilføje monteringstrin, for at gøre dette klik: Byg trin -> Tilføj byggetrin.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Her vælger vi:

  • Løbertype -> Kommandolinje
  • Kør -> Brugerdefineret script

Da vi vil udføre analyser under projektopstilling, bør montering og analyse være ét trin, så udfyld feltet Brugerdefineret script:

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Vi vil se på de enkelte trin senere. Det er vigtigt at indlæse analysatoren, samle projektet, analysere det, udskrive rapporten og formatere det kun tager elleve linjer kode.

Den sidste ting, vi skal gøre, er at indstille miljøvariablerne, som jeg har skitseret nogle måder at forbedre deres læsbarhed på. For at gøre dette, lad os gå videre: Parametre -> Tilføj ny parameter og tilføj tre variable:

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Alt du skal gøre er at trykke på knappen Kør i øverste højre hjørne. Mens projektet bliver samlet og analyseret, vil jeg fortælle dig om manuskriptet.

Direkte script

Først skal vi downloade den seneste PVS-Studio-distribution. Til dette bruger vi Chocolatey Package Manager. For dem, der vil vide mere om dette, er der en tilsvarende artiklen:

choco install pvs-studio -y

Lad os derefter lancere CLMonitor-projektbyggesporingsværktøjet.

%CLmon% monitor –-attach

Derefter vil vi bygge projektet som en miljøvariabel MSB er stien til den version af MSBuild jeg skal bygge

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

Lad os indtaste login og licensnøglen til PVS-Studio:

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

Når opbygningen er færdig, skal du køre CLMonitor igen for at generere forbehandlede filer og statisk analyse:

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

Så vil vi bruge et andet hjælpeprogram fra vores distribution. PlogConverter konverterer en rapport fra et standardformat til et TeamCity-specifikt format. Takket være dette vil vi være i stand til at se det direkte i byggevinduet.

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

Det sidste trin er at vise den formaterede rapport i stdout, hvor det vil blive samlet op af TeamCity-parseren.

type "C:tempptest.plog_TeamCity.txt"

Fuld script kode:

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"

I mellemtiden er montagen og analysen af ​​projektet gennemført med succes, vi kan gå til fanen Projekter og sørg for det.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Lad os nu klikke videre Eftersyn i altfor at gå til visning af analysatorrapporten:

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Advarsler er grupperet efter diagnostiske regelnumre. For at navigere gennem koden skal du klikke på linjenummeret med advarslen. Ved at klikke på spørgsmålstegnet i øverste højre hjørne åbner du en ny fane med dokumentation. Du kan også navigere gennem koden ved at klikke på linjenummeret med analysatoradvarslen. Navigation fra en fjerncomputer er mulig ved brug SourceTreeRoot markør. Enhver, der er interesseret i denne funktionsmåde for analysatoren, kan gøre sig bekendt med det tilsvarende afsnit dokumentation.

Visning af analysatorens resultater

Nu hvor vi er færdige med at implementere og konfigurere bygningen, lad os tage et kig på nogle interessante advarsler, der findes i det projekt, vi kigger på.

Advarsel N1

V773 [CWE-401] Undtagelsen blev kastet uden at frigive 'resultat'-markøren. En hukommelseslæk er mulig. 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;
}

Analysatoren bemærkede en fejl, der efter dynamisk allokering af hukommelse i Opret objekt, når der opstår en undtagelse, ryddes hukommelsen ikke, og der opstår et hukommelseslæk.

Advarsel N2

V501 Der er identiske underudtryk '(1ULL << WIDX_MONTH_BOX)' til venstre og til højre for '|' 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),
  ....
};

Kun få andre end en statisk analysator kunne bestå denne opmærksomhedstest. Dette copy-paste eksempel er godt af netop denne grund.

Advarsler N3

V703 Det er mærkeligt, at 'flag'-feltet i den afledte klasse 'RCT12BannerElement' overskriver feltet i basisklassen 'RCT12TileElementBase'. Tjek linjer: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Det er selvfølgelig ikke altid en fejl at bruge en variabel med samme navn i basisklassen og i efterkommeren. Dog forudsætter arveteknologien selv, at alle forældreklassens felter er til stede i børneklassen. Ved at angive felter med samme navn i arvingen introducerer vi forvirring.

Advarsel N4

V793 Det er mærkeligt, at resultatet af 'imageDirection / 8'-erklæringen er en del af betingelsen. Måske skulle dette udsagn have været sammenlignet med noget andet. libopenrct2 ObservationTower.cpp 38

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

Lad os se nærmere. Udtryk imageDirection/8 vil være falsk hvis imageDirection er i området fra -7 til 7. Anden del: (imageDirection / 8) != 3 checks imageDirection for at være uden for området: henholdsvis fra -31 til -24 og fra 24 til 31. Det forekommer mig ret mærkeligt at kontrollere tal for inklusion i et bestemt område på denne måde, og selvom der ikke er nogen fejl i dette stykke kode, vil jeg anbefale at omskrive disse betingelser for at være mere eksplicitte. Dette ville gøre livet meget lettere for de mennesker, der ville læse og vedligeholde denne kode.

Advarsel N5

V587 En ulige række af opgaver af denne art: A = B; B = A;. Tjek linjer: 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;
      ....
  }
  ....
}

Dette kodefragment blev højst sandsynligt opnået ved dekompilering. Derefter, at dømme efter kommentaren, blev en del af den ikke-fungerende kode fjernet. Der er dog stadig et par operationer tilbage cursorId, hvilket heller ikke giver meget mening.

Advarsel N6

V1004 [CWE-476] 'Player'-markøren blev brugt usikkert, efter at den blev verificeret mod nullptr. Tjek linjer: 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);                    // <=
  }
  ....
}

Denne kode er ret nem at rette; du skal bare tjekke den en tredje gang spiller til en nul-markør, eller føj den til brødteksten i den betingede sætning. Jeg vil foreslå den anden mulighed:

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

Advarsel N7

V547 [CWE-570] Udtrykket 'navn == nullptr' er altid falsk. 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 slippe af med en svær-læselig kodelinje i ét hug og løse problemet ved at tjekke efter nullptr. Jeg foreslår at ændre koden som følger:

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

Advarsel N8

V1048 [CWE-1164] Variablen 'ColumnHeaderPressedCurrentState' blev tildelt den samme værdi. libopenrct2ui CustomListView.cpp 510

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

Koden ser ret mærkelig ud. Det forekommer mig, at der var en tastefejl enten i betingelsen eller ved gentildeling af variablen ColumnHeaderPressedCurrentState betydning falsk.

Output

Som vi kan se, er det ret simpelt at integrere den statiske PVS-Studio-analysator i dit TeamCity-projekt. For at gøre dette er det nok kun at skrive en lille konfigurationsfil. Kontrol af koden giver dig mulighed for at identificere problemer umiddelbart efter montering, hvilket vil hjælpe med at eliminere dem, når kompleksiteten og omkostningerne ved ændringer stadig er lave.

PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet
Hvis du vil dele denne artikel med et engelsktalende publikum, så brug venligst oversættelseslinket: Vladislav Stolyarov. PVS-Studio og kontinuerlig integration: TeamCity. Analyse af Open RollerCoaster Tycoon 2-projektet.

Kilde: www.habr.com

Tilføj en kommentar