PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Et av de mest aktuelle scenariene for bruk av PVS-Studio-analysatoren er dens integrasjon med CI-systemer. Og selv om analysen av et PVS-Studio-prosjekt fra nesten alle kontinuerlige integreringssystem kan bygges inn i bare noen få kommandoer, fortsetter vi å gjøre denne prosessen enda mer praktisk. PVS-Studio har nå støtte for å konvertere analysatorutdata til et format for TeamCity - TeamCity Inspections Type. La oss se hvordan det fungerer.

Informasjon om programvaren som brukes

PVS-Studio — en statisk analysator av C, C++, C# og Java-kode, designet for å lette oppgaven med å finne og rette ulike typer feil. Analysatoren kan brukes på Windows, Linux og macOS. I denne artikkelen vil vi aktivt bruke ikke bare selve analysatoren, men også noen verktøy fra distribusjonen.

CLMonitor — er en overvåkingsserver som overvåker kompilatorstarter. Det må kjøres umiddelbart før du begynner å bygge prosjektet ditt. I snooping-modus vil serveren fange opp kjøringer av alle kompilatorer som støttes. Det er verdt å merke seg at dette verktøyet kun kan brukes til å analysere C/C++-prosjekter.

PlogConverter – et verktøy for å konvertere analysatorrapporter til forskjellige formater.

Informasjon om prosjektet som studeres

La oss prøve denne funksjonaliteten med et praktisk eksempel - la oss analysere OpenRCT2-prosjektet.

ÅpneRCT2 - en åpen implementering av spillet RollerCoaster Tycoon 2 (RCT2), utvide det med nye funksjoner og fikse feil. Spillet dreier seg om å bygge og vedlikeholde en fornøyelsespark som inneholder forlystelser, butikker og fasiliteter. Spilleren må prøve å tjene penger og opprettholde parkens gode rykte samtidig som gjestene er fornøyde. OpenRCT2 lar deg spille i både scenario og sandkasse. Scenarier krever at spilleren fullfører en spesifikk oppgave innen en fastsatt tid, mens Sandbox lar spilleren bygge en mer fleksibel park uten noen restriksjoner eller økonomi.

justering

For å spare tid vil jeg sannsynligvis hoppe over installasjonsprosessen og starte fra det øyeblikket jeg har TeamCity-serveren kjørende på datamaskinen min. Vi må gå til: localhost:{port spesifisert under installasjonsprosessen} (i mitt tilfelle, localhost:9090) og angi autorisasjonsdata. Etter innreise vil vi bli møtt av:

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Klikk på knappen Opprett prosjekt. Deretter velger du Manuelt og fyller ut feltene.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Etter å ha trykket på knappen Opprett, blir vi møtt av et vindu med innstillinger.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
La oss klikke Lag byggekonfigurasjon.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Fyll ut feltene og klikk Opprett. Vi ser et vindu som ber deg velge et versjonskontrollsystem. Siden kildene allerede er lokalisert, klikk Hopp.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Til slutt går vi videre til prosjektinnstillingene.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
La oss legge til monteringstrinn, for å gjøre dette klikk: Byggtrinn -> Legg til byggetrinn.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Her velger vi:

  • Løpertype -> Kommandolinje
  • Kjør -> Egendefinert skript

Siden vi skal utføre analyser under prosjektsammenstilling, bør montering og analyse være ett trinn, så fyll ut feltet Tilpasset skript:

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Vi vil se på de enkelte trinnene senere. Det er viktig at det bare tar elleve linjer med kode å laste analysatoren, sette sammen prosjektet, analysere den, skrive ut rapporten og formatere den.

Det siste vi må gjøre er å angi miljøvariablene, som jeg har skissert noen måter å forbedre lesbarheten på. For å gjøre dette, la oss gå videre: Parametere -> Legg til ny parameter og legg til tre variabler:

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Alt du trenger å gjøre er å trykke på knappen Kjør i øvre høyre hjørne. Mens prosjektet blir satt sammen og analysert, vil jeg fortelle deg om manuset.

Manus direkte

Først må vi laste ned den nyeste PVS-Studio-distribusjonen. Til dette bruker vi Chocolatey package manager. For de som ønsker å vite mer om dette, finnes det tilsvarende artikkel:

choco install pvs-studio -y

La oss deretter lansere CLMonitor-prosjektbyggsporingsverktøyet.

%CLmon% monitor –-attach

Deretter skal vi bygge prosjektet som en miljøvariabel MSB er banen til versjonen av MSBuild jeg må bygge

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

La oss skrive inn påloggings- og lisensnøkkelen for PVS-Studio:

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

Etter at byggingen er fullført, kjør CLMonitor igjen for å generere forhåndsbehandlede filer og statisk analyse:

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

Da vil vi bruke et annet verktøy fra vår distribusjon. PlogConverter konverterer en rapport fra et standardformat til et TeamCity-spesifikt format. Takket være dette vil vi kunne se det direkte i byggevinduet.

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

Det siste trinnet er å vise den formaterte rapporten i stdout, hvor den vil bli plukket opp av TeamCity-parseren.

type "C:tempptest.plog_TeamCity.txt"

Full skriptkode:

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 mellomtiden er monteringen og analysen av prosjektet fullført, vi kan gå til fanen Prosjekter og sørg for det.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
La oss nå klikke videre Inspeksjoner Totaltfor å gå til å vise analysatorrapporten:

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Advarsler er gruppert etter diagnostiske regelnumre. For å navigere gjennom koden, må du klikke på linjenummeret med advarselen. Ved å klikke på spørsmålstegnet i øvre høyre hjørne åpner du en ny fane med dokumentasjon. Du kan også navigere gjennom koden ved å klikke på linjenummeret med analysatoradvarselen. Navigering fra en ekstern datamaskin er mulig når du bruker SourceTreeRoot markør. Alle som er interessert i denne driftsmodusen til analysatoren kan gjøre seg kjent med den tilsvarende delen dokumentasjon.

Viser analysatorens resultater

Nå som vi er ferdige med å distribuere og konfigurere bygget, la oss ta en titt på noen interessante advarsler i prosjektet vi ser på.

Advarsel N1

V773 [CWE-401] Unntaket ble kastet uten å slippe "resultat"-pekeren. En minnelekkasje 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 la merke til en feil som etter dynamisk allokering av minne CreateObject, når et unntak oppstår, slettes ikke minnet, og det oppstår en minnelekkasje.

Advarsel N2

V501 Det er identiske underuttrykk '(1ULL << WIDX_MONTH_BOX)' til venstre og til høyre 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),
  ....
};

Få andre enn en statisk analysator kunne bestå denne oppmerksomhetstesten. Dette copy-paste-eksemplet er bra av nettopp denne grunnen.

Advarsler N3

V703 Det er merkelig at 'flagg'-feltet i den avledede klassen 'RCT12BannerElement' overskriver feltet i basisklassen 'RCT12TileElementBase'. Sjekk linjer: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Å bruke en variabel med samme navn i grunnklassen og i etterkommeren er selvfølgelig ikke alltid en feil. Imidlertid forutsetter arveteknologien selv at alle feltene til foreldreklassen er til stede i barneklassen. Ved å deklarere felt med samme navn i arvingen skaper vi forvirring.

Advarsel N4

V793 Det er merkelig at resultatet av 'imageDirection / 8'-setningen er en del av tilstanden. Kanskje burde denne uttalelsen vært sammenlignet med noe annet. libopenrct2 ObservationTower.cpp 38

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

La oss ta en nærmere titt. Uttrykk imageDirection/8 vil være falsk hvis imageDirection er i området fra -7 til 7. Andre del: (imageDirection / 8) != 3 sjekker imageDirection for å være utenfor området: henholdsvis fra -31 til -24 og fra 24 til 31. Det virker ganske rart for meg å sjekke tall for inkludering i et bestemt område på denne måten, og selv om det ikke er noen feil i denne kodebiten, vil jeg anbefale å omskrive disse betingelsene for å være mer eksplisitte. Dette ville gjøre livet mye enklere for folk som ville lese og vedlikeholde denne koden.

Advarsel N5

V587 En odde rekkefølge av oppgaver av denne typen: A = B; B = A;. Sjekk 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 kodefragmentet ble mest sannsynlig oppnådd ved dekompilering. Deretter ble en del av den ikke-fungerende koden fjernet etter kommentaren som ble lagt igjen. Det gjenstår imidlertid fortsatt et par operasjoner cursorId, som heller ikke gir mye mening.

Advarsel N6

V1004 [CWE-476] 'Player'-pekeren ble brukt på en usikker måte etter at den ble verifisert mot nullptr. Sjekk 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 koden er ganske enkel å rette; du trenger bare å sjekke den en tredje gang spiller til en null-peker, eller legg den til i hoveddelen av den betingede setningen. Jeg vil foreslå det andre 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);
    }
  }
  ....
}

Advarsel N7

V547 [CWE-570] Uttrykket 'navn == nullptr' er alltid usant. 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 kvitte deg med en vanskelig å lese kodelinje med ett slag og løse problemet med å sjekke for nullptr. Jeg foreslår at du endrer 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] Variabelen 'ColumnHeaderPressedCurrentState' ble tildelt samme verdi. libopenrct2ui CustomListView.cpp 510

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

Koden ser ganske merkelig ut. Det ser ut til at det var en skrivefeil enten i tilstanden eller ved omtildeling av variabelen ColumnHeaderPressedCurrentState som betyr falsk.

Utgang

Som vi kan se, er det ganske enkelt å integrere den statiske PVS-Studio-analysatoren i TeamCity-prosjektet ditt. For å gjøre dette er det nok å skrive bare en liten konfigurasjonsfil. Ved å sjekke koden kan du identifisere problemer umiddelbart etter montering, noe som vil bidra til å eliminere dem når kompleksiteten og kostnadene ved endringer fortsatt er lave.

PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet
Hvis du vil dele denne artikkelen med et engelsktalende publikum, vennligst bruk oversettelseslenken: Vladislav Stolyarov. PVS-Studio og kontinuerlig integrasjon: TeamCity. Analyse av Open RollerCoaster Tycoon 2-prosjektet.

Kilde: www.habr.com

Legg til en kommentar