PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Eines der aktuellsten Einsatzszenarien des PVS-Studio-Analysators ist seine Integration in CI-Systeme. Und obwohl die Analyse eines PVS-Studio-Projekts aus nahezu jedem Continuous-Integration-System in nur wenigen Befehlen integriert werden kann, machen wir diesen Prozess weiterhin noch komfortabler. PVS-Studio unterstützt jetzt die Konvertierung der Analyseausgabe in ein Format für TeamCity – TeamCity Inspections Type. Mal sehen, wie es funktioniert.

Informationen zur verwendeten Software

PVS Studio – ein statischer Analysator für C-, C++-, C#- und Java-Code, der das Auffinden und Korrigieren verschiedener Arten von Fehlern erleichtern soll. Der Analysator kann unter Windows, Linux und macOS verwendet werden. In diesem Artikel werden wir nicht nur den Analysator selbst, sondern auch einige Dienstprogramme aus seiner Distribution aktiv nutzen.

CLMonitor – ist ein Überwachungsserver, der Compiler-Starts überwacht. Es muss unmittelbar vor Beginn der Erstellung Ihres Projekts ausgeführt werden. Im Snooping-Modus fängt der Server Ausführungen aller unterstützten Compiler ab. Es ist zu beachten, dass dieses Dienstprogramm nur zur Analyse von C/C++-Projekten verwendet werden kann.

PlogConverter – ein Dienstprogramm zum Konvertieren von Analyseberichten in verschiedene Formate.

Informationen zum untersuchten Projekt

Probieren wir diese Funktionalität an einem praktischen Beispiel aus – analysieren wir das OpenRCT2-Projekt.

OpenRCT2 - eine offene Implementierung des Spiels RollerCoaster Tycoon 2 (RCT2), die es um neue Funktionen erweitert und Fehler behebt. Das Gameplay dreht sich um den Bau und die Instandhaltung eines Vergnügungsparks mit Fahrgeschäften, Geschäften und Einrichtungen. Der Spieler muss versuchen, Gewinn zu machen, den guten Ruf des Parks aufrechtzuerhalten und gleichzeitig die Gäste zufrieden zu stellen. Mit OpenRCT2 können Sie sowohl im Szenario als auch in der Sandbox spielen. In Szenarien muss der Spieler eine bestimmte Aufgabe innerhalb einer festgelegten Zeit erledigen, während Sandbox es dem Spieler ermöglicht, einen flexibleren Park ohne Einschränkungen oder finanzielle Mittel zu errichten.

Einstellung

Um Zeit zu sparen, werde ich den Installationsprozess wahrscheinlich überspringen und von dem Moment an beginnen, in dem der TeamCity-Server auf meinem Computer läuft. Wir müssen zu: localhost:{Port, der während des Installationsprozesses angegeben wurde} (in meinem Fall localhost:9090) gehen und Autorisierungsdaten eingeben. Nach dem Betreten werden wir begrüßt von:

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Klicken Sie auf die Schaltfläche Projekt erstellen. Wählen Sie als Nächstes „Manuell“ aus und füllen Sie die Felder aus.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Nach dem Drücken der Taste Erstellen, wir werden von einem Fenster mit Einstellungen begrüßt.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Lasst uns klicken Erstellen Sie eine Build-Konfiguration.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Füllen Sie die Felder aus und klicken Sie Erstellen. Wir sehen ein Fenster, in dem Sie aufgefordert werden, ein Versionskontrollsystem auszuwählen. Da sich die Quellen bereits lokal befinden, klicken Sie auf überspringen.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Abschließend gehen wir zu den Projekteinstellungen über.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Fügen wir Montageschritte hinzu. Klicken Sie dazu auf: Build-Schritte -> Build-Schritt hinzufügen.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Hier wählen wir:

  • Läufertyp -> Befehlszeile
  • Ausführen -> Benutzerdefiniertes Skript

Da wir die Analyse während der Projektkompilierung durchführen, sollten Montage und Analyse ein Schritt sein. Füllen Sie daher das Feld aus Benutzerdefiniertes Skript:

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Auf einzelne Schritte gehen wir später noch ein. Wichtig ist, dass das Laden des Analysators, das Zusammenstellen des Projekts, die Analyse, die Ausgabe des Berichts und die Formatierung nur elf Zeilen Code erfordern.

Als letztes müssen wir die Umgebungsvariablen festlegen. Ich habe einige Möglichkeiten beschrieben, um ihre Lesbarkeit zu verbessern. Gehen wir dazu weiter: Parameter -> Neuen Parameter hinzufügen und füge drei Variablen hinzu:

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Alles was Sie tun müssen, ist den Knopf zu drücken Führen Sie in der oberen rechten Ecke. Während das Projekt zusammengestellt und analysiert wird, erzähle ich Ihnen etwas über das Drehbuch.

Direkt Skript

Zuerst müssen wir die neueste PVS-Studio-Distribution herunterladen. Hierzu nutzen wir den Paketmanager Chocolatey. Für diejenigen, die mehr darüber erfahren möchten, gibt es eine entsprechende Beitrag:

choco install pvs-studio -y

Als Nächstes starten wir das CLMonitor-Dienstprogramm zur Projekterstellungsverfolgung.

%CLmon% monitor –-attach

Dann erstellen wir das Projekt als Umgebungsvariable MSB ist der Pfad zu der Version von MSBuild, die ich erstellen muss

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

Geben wir den Login und den Lizenzschlüssel für PVS-Studio ein:

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

Nachdem der Build abgeschlossen ist, führen Sie CLMonitor erneut aus, um vorverarbeitete Dateien und eine statische Analyse zu generieren:

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

Dann werden wir ein anderes Dienstprogramm aus unserer Distribution verwenden. PlogConverter konvertiert einen Bericht von einem Standardformat in ein TeamCity-spezifisches Format. Dadurch können wir es direkt im Build-Fenster anzeigen.

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

Der letzte Schritt besteht darin, den formatierten Bericht anzuzeigen stdout, wo es vom TeamCity-Parser erfasst wird.

type "C:tempptest.plog_TeamCity.txt"

Vollständiger Skriptcode:

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"

In der Zwischenzeit ist die Montage und Analyse des Projekts erfolgreich abgeschlossen, wir können auf die Registerkarte gehen Projekte und stellen Sie sicher, dass es so ist.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Jetzt lasst uns auf klicken Inspektionen insgesamtSo gelangen Sie zur Anzeige des Analyseberichts:

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Warnungen werden nach Diagnoseregelnummern gruppiert. Um durch den Code zu navigieren, müssen Sie auf die Zeilennummer mit der Warnung klicken. Durch Klicken auf das Fragezeichen in der oberen rechten Ecke öffnet sich ein neuer Tab mit Dokumentation. Sie können auch durch den Code navigieren, indem Sie auf die Zeilennummer mit der Analysewarnung klicken. Bei Verwendung ist die Navigation von einem entfernten Computer aus möglich SourceTreeRoot Marker. Wer sich für diese Funktionsweise des Analysators interessiert, kann sich mit dem entsprechenden Abschnitt vertraut machen Dokumentation.

Anzeigen der Ergebnisse des Analysegeräts

Nachdem wir nun mit der Bereitstellung und Konfiguration des Builds fertig sind, werfen wir einen Blick auf einige interessante Warnungen in dem Projekt, das wir betrachten.

Warnung N1

V773 [CWE-401] Die Ausnahme wurde ausgelöst, ohne den „Ergebnis“-Zeiger freizugeben. Ein Speicherverlust ist möglich. 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;
}

Der Analysator hat einen Fehler festgestellt, der nach der dynamischen Speicherzuweisung in CreateObjectWenn eine Ausnahme auftritt, wird der Speicher nicht gelöscht und es tritt ein Speicherverlust auf.

Warnung N2

V501 Es gibt identische Unterausdrücke „(1ULL << WIDX_MONTH_BOX)“ links und rechts vom „|“ Operator. 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),
  ....
};

Nur wenige Menschen außer einem statischen Analysator könnten diesen Aufmerksamkeitstest bestehen. Dieses Copy-Paste-Beispiel ist genau aus diesem Grund gut.

Warnungen N3

V703 Es ist seltsam, dass das Feld „flags“ in der abgeleiteten Klasse „RCT12BannerElement“ das Feld in der Basisklasse „RCT12TileElementBase“ überschreibt. Überprüfen Sie die Zeilen: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Natürlich ist die Verwendung einer Variablen mit demselben Namen in der Basisklasse und im Nachkommen nicht immer ein Fehler. Die Vererbungstechnologie selbst geht jedoch davon aus, dass alle Felder der übergeordneten Klasse in der untergeordneten Klasse vorhanden sind. Indem wir Felder mit demselben Namen im Erben deklarieren, führen wir zu Verwirrung.

Warnung N4

V793 Es ist seltsam, dass das Ergebnis der Anweisung „imageDirection / 8“ Teil der Bedingung ist. Vielleicht hätte diese Aussage mit etwas anderem verglichen werden sollen. libopenrct2 ObservationTower.cpp 38

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

Lass uns genauer hinschauen. Ausdruck imageDirection/8 wird falsch sein, wenn Bildrichtung liegt im Bereich von -7 bis 7. Zweiter Teil: (Bildrichtung / 8) != 3 Schecks Bildrichtung für außerhalb des Bereichs: von -31 bis -24 bzw. von 24 bis 31. Es erscheint mir ziemlich seltsam, auf diese Weise zu prüfen, ob Zahlen in einem bestimmten Bereich enthalten sind, und selbst wenn dieser Code keinen Fehler enthält, würde ich empfehlen, diese Bedingungen expliziter umzuschreiben. Dies würde den Menschen, die diesen Code lesen und pflegen würden, das Leben erheblich erleichtern.

Warnung N5

V587 Eine ungerade Folge von Aufgaben dieser Art: A = B; B = A;. Überprüfen Sie die Zeilen: 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;
      ....
  }
  ....
}

Dieses Codefragment wurde höchstwahrscheinlich durch Dekompilierung erhalten. Dem hinterlassenen Kommentar nach zu urteilen, wurde dann ein Teil des nicht funktionierenden Codes entfernt. Es sind jedoch noch einige Operationen übrig Cursor-ID, was auch wenig Sinn ergibt.

Warnung N6

V1004 [CWE-476] Der „player“-Zeiger wurde unsicher verwendet, nachdem er gegen nullptr überprüft wurde. Überprüfen Sie die Zeilen: 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);                    // <=
  }
  ....
}

Dieser Code ist ganz einfach zu korrigieren; Sie müssen ihn nur ein drittes Mal überprüfen Spieler auf einen Nullzeiger umstellen oder ihn dem Hauptteil der bedingten Anweisung hinzufügen. Ich würde die zweite Option vorschlagen:

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

Warnung N7

V547 [CWE-570] Der Ausdruck „name == nullptr“ ist immer falsch. 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));
    ....
  }
  ....
}

Sie können eine schwer lesbare Codezeile auf einen Schlag loswerden und das Problem mit der Suche lösen nullptr. Ich schlage vor, den Code wie folgt zu ändern:

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

Warnung N8

V1048 [CWE-1164] Der Variablen „ColumnHeaderPressedCurrentState“ wurde derselbe Wert zugewiesen. libopenrct2ui CustomListView.cpp 510

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

Der Code sieht ziemlich seltsam aus. Es scheint mir, dass entweder in der Bedingung oder bei der Neuzuweisung der Variablen ein Tippfehler vorliegt ColumnHeaderPressedCurrentState Bedeutung falsch.

Abschluss

Wie wir sehen, ist die Integration des statischen Analysators PVS-Studio in Ihr TeamCity-Projekt recht einfach. Dazu reicht es aus, nur eine kleine Konfigurationsdatei zu schreiben. Durch die Überprüfung des Codes können Sie Probleme sofort nach der Montage erkennen und so beheben, wenn die Komplexität und die Kosten der Änderungen noch gering sind.

PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Vladislav Stolyarov. PVS-Studio und kontinuierliche Integration: TeamCity. Analyse des Open RollerCoaster Tycoon 2-Projekts.

Source: habr.com

Kommentar hinzufügen