PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
L'un des scénarios d'utilisation les plus pertinents de l'analyseur PVS-Studio est son intégration avec des systèmes CI. Bien que l'analyse du projet PVS-Studio puisse être intégrée à presque tous les systèmes d'intégration continue en seulement quelques commandes, nous continuons à rendre ce processus encore plus pratique. PVS-Studio a ajouté le support de la conversion des sorties de l'analyseur dans un format pour TeamCity - TeamCity Inspections Type. Voyons comment cela fonctionne.

Informations sur le logiciel utilisé

PVS-Studio — un analyseur statique pour le code C, C++, C# et Java, destiné à faciliter la tâche de recherche et de correction de divers types d'erreurs. L'analyseur peut être utilisé sous Windows, Linux et macOS. Dans cet article, nous utiliserons activement non seulement l'analyseur lui-même, mais également certains outils de sa distribution.

CLMonitor — est un serveur de surveillance qui suit le lancement des compilateurs. Il doit être lancé juste avant le début de la construction de votre projet. En mode de suivi, le serveur interceptera les lancements de tous les compilateurs pris en charge. Il convient de noter que cet utilitaire ne peut être utilisé que pour l'analyse de projets C/C++.

PlogConverter – un utilitaire pour convertir le rapport de l'analyseur en différents formats.

Informations sur le projet examiné

Essayons cette fonctionnalité sur un exemple pratique – analysons le projet OpenRCT2.

OpenRCT2 — une implémentation open source du jeu RollerCoaster Tycoon 2 (RCT2), qui l'enrichit de nouvelles fonctionnalités et corrige des erreurs. Le gameplay tourne autour de la construction et de la gestion d'un parc d'attractions, comprenant des manèges, des magasins et des installations. Le joueur doit essayer de réaliser des bénéfices tout en maintenant une bonne réputation pour le parc, tout en gardant les visiteurs heureux. OpenRCT2 permet de jouer à la fois en mode scénario et en mode bac à sable. Les scénarios exigent que le joueur accomplisse une tâche spécifique dans un temps imparti, tandis que le mode bac à sable permet au joueur de construire un parc plus flexible sans aucune restriction ou contrainte financière.

Configuration

Pour gagner du temps, je vais omettre le processus d'installation et commencer au moment où le serveur TeamCity est déjà lancé sur mon ordinateur. Nous devons aller sur : localhost:{le port spécifié pendant l'installation} (dans mon cas, localhost:9090) et entrer les données d'authentification. Une fois connectés, nous serons accueillis par :

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Cliquez sur le bouton Créer un projet. Ensuite, sélectionnons Manuellement et remplissons les champs.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Après avoir cliqué sur le bouton Créer, une fenêtre avec les paramètres s'affiche.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Cliquez sur Créer une configuration de build.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Remplissons les champs et cliquons sur Créer. Nous voyons une fenêtre proposant de choisir le système de gestion de versions. Comme les sources sont déjà locales, cliquons sur Passer.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Enfin, nous passons aux paramètres du projet.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Ajoutons les étapes de build, pour cela cliquons sur : Étapes de build -> Ajouter une étape de build.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Ici, sélectionnons :

  • Type de Runner -> Ligne de commande
  • Exécuter -> Script personnalisé

Comme nous effectuerons une analyse pendant la compilation du projet, le build et l'analyse doivent constituer une seule étape, donc remplissons le champ Script personnalisé:

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Nous nous attarderons plus tard sur des étapes séparées. Il est important que le chargement de l'analyseur, la compilation du projet, son analyse, la génération du rapport et son formatage ne prennent qu'onze lignes de code.

La dernière chose à faire est d'installer les variables d'environnement, pour lesquelles j'ai désigné certains chemins pour améliorer leur lisibilité. Pour cela, allons sur : Paramètres -> Ajouter un nouveau paramètre et ajoutons trois variables :

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Il ne reste plus qu'à cliquer sur le bouton Exécution en haut à droite. Pendant que le build et l'analyse du projet se déroulent, je vais vous parler du script.

Le script proprement dit

Pour commencer, nous devons télécharger la dernière distribution de PVS-Studio. Pour cela, nous utilisons le gestionnaire de paquets Chocolatey. Pour ceux qui veulent en savoir plus, il y a un lien correspondant. article:

choco install pvs-studio -y

Ensuite, lançons l'outil de suivi du build du projet CLMonitor.

%CLmon% monitor –-attach

Puis nous allons compiler le projet, en tant que variable d'environnement MSB est le chemin vers la version MSBuild que je veux utiliser pour la compilation

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

Entrez le nom d'utilisateur et la clé de licence PVS-Studio :

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

Après avoir terminé la compilation, relançons CLMonitor pour générer des fichiers prétraités et effectuer une analyse statique :

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

Ensuite, nous utiliserons un autre utilitaire de notre distribution. PlogConverter convertit le rapport du format standard à celui spécifique à TeamCity. Cela nous permettra de le visualiser directement dans la fenêtre de compilation.

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

Enfin, nous exporterons le rapport formaté dans stdout, où le parser TeamCity le récupérera.

type "C:tempptest.plog_TeamCity.txt"

Le code complet du script :

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"

Pendant ce temps, la construction et l’analyse du projet se sont terminées avec succès, nous pouvons passer à l’onglet Projets et vérifier cela.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Cliquons maintenant sur Inspections Totales, pour accéder à l'affichage du rapport de l'analyseur :

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Les avertissements sont regroupés par numéros de règles diagnostiques. Pour naviguer dans le code, vous devez cliquer sur le numéro de ligne contenant un avertissement. Cliquer sur le point d'interrogation en haut à droite ouvrira un nouvel onglet avec la documentation. Vous pouvez également naviguer dans le code en cliquant sur le numéro de ligne avec un avertissement de l'analyseur. La navigation à distance est possible en utilisant SourceTreeRoot comme marqueur. Ceux qui s'intéressent à ce mode de fonctionnement de l'analyseur peuvent consulter la section correspondante. documentation.

Voir les résultats du travail de l’analyseur

Après avoir terminé le déploiement et la configuration de la construction, je propose de jeter un œil à certains avertissements intéressants détectés dans le projet examiné.

Avertissement N1

V773 [CWE-401] L'exception a été lancée sans libérer le pointeur 'result'. Une fuite de mémoire est possible. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("L'objet a des erreurs");
  }
  ....
}

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("Type d'objet invalide");
  }
  return result;
}

L'analyseur a remarqué une erreur, à savoir qu'après l'allocation dynamique de mémoire dans CreateObject, en cas d'exception, la mémoire n'est pas libérée, ce qui entraîne une fuite de mémoire.

Avertissement N2

V501 Il y a des sous-expressions identiques '(1ULL << WIDX_MONTH_BOX)' à gauche et à droite de l'opérateur '|'. 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),
  ....
};

Peu de gens, à part un analyseur statique, pourraient passer ce test d'attention. Cet exemple de copier-coller est bon précisément pour cela.

Avertissements N3

V703 Il est étrange que le champ 'flags' dans la classe dérivée 'RCT12BannerElement' écrase le champ de la classe de base 'RCT12TileElementBase'. Vérifiez les lignes : RCT12.h : 570, RCT12.h : 259. libopenrct2 RCT12.h 570

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

Bien sûr, l'utilisation d'une variable avec le même nom dans la classe de base et dans l'héritier n'est pas toujours une erreur. Cependant, la technologie d'héritage implique par nature la présence de tous les champs de la classe parente dans l'enfant. En déclarant dans l'héritier des champs avec le même nom, nous créons de la confusion.

Avertissement N4

V793 Il est étrange que le résultat de l'instruction 'imageDirection / 8' soit une partie de la condition. Peut-être que cette instruction aurait dû être comparée à quelque chose d'autre. libopenrct2 ObservationTower.cpp 38

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

Examinons cela de plus près. L'expression imageDirection / 8 sera false si imageDirection se trouve dans la plage de -7 à 7. La deuxième partie : (imageDirection / 8) != 3 vérifie imageDirection si elle est hors des plages : de -31 à -24 et de 24 à 31 respectivement. Il me semble plutôt étrange de vérifier les nombres pour leur appartenance à une certaine plage de cette manière et, même si ce fragment de code ne contient pas d'erreur, je recommanderais de réécrire ces conditions de manière plus explicite. Cela faciliterait considérablement la vie aux personnes qui liront et maintiendront ce code.

Avertissement N5

V587 Une séquence d'assignations de ce genre curieuse : A = B; B = A;. Vérifiez les lignes : 1115, 1118. libopenrct2ui MouseInput.cpp 1118

void process_mouse_over(....)
{
  ....
  switch (window->widgets[widgetId].type)
  {
    case WWT_VIEWPORT:
      ebx = 0;
      edi = cursorId;                                 // <=
      // L'événement de fenêtre WE_UNKNOWN_0E a été appelé ici,
      // mais aucune fenêtre n'a réellement implémenté de gestionnaire et
      // il n'est pas connu à quoi cela servait
      cursorId = edi;                                 // <=
      if ((ebx & 0xFF) != 0)
      {
        set_cursor(cursorId);
        return;
      }
      break;
      ....
  }
  ....
}

Ce fragment de code a probablement été obtenu par décompilation. Ensuite, compte tenu du commentaire laissé, une partie du code non fonctionnel a été supprimée. Cependant, il reste quelques opérations sur cursorId, qui n'ont également pas de sens particulier.

Avertissement N6

V1004 [CWE-476] Le pointeur 'player' a été utilisé de manière non sécurisée après avoir été vérifié contre nullptr. Vérifiez les lignes : 2085, 2094. libopenrct2 Network.cpp 2094

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Ajouter un nouveau joueur.
    player = AddPlayer("", "");
    if (player)                                          // Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Ce code est assez facile à corriger, il suffit de vérifier une troisième fois player pour le pointeur nul, ou de l'inclure dans le corps de l'instruction conditionnelle. Je proposerais la seconde option :

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Ajouter un nouveau joueur.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}

Avertissement N7

V547 [CWE-570] L'expression 'name == nullptr' est toujours fausse. 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));
    ....
  }
  ....
}

On peut se débarrasser d'un seul coup de la ligne de code difficile à lire et résoudre le problème de la vérification sur nullptr. Je propose de modifier le code comme suit :

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

Avertissement N8

V1048 [CWE-1164] La variable 'ColumnHeaderPressedCurrentState' a été assignée à la même valeur. libopenrct2ui CustomListView.cpp 510

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

Le code semble assez étrange. Je pense qu'il y a eu une faute de frappe soit dans la condition, soit lors de la réaffectation de la variable ColumnHeaderPressedCurrentState les valeurs faux.

Sortie

Comme nous le voyons, intégrer l'analyseur statique PVS-Studio dans votre projet sur TeamCity est assez simple. Il suffit d'écrire un petit fichier de configuration. L'analyse du code permettra d'identifier les problèmes immédiatement après la compilation, ce qui aide à les résoudre lorsqu'ils sont encore peu complexes et peu coûteux à corriger.

PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2
Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction : Vladislav Stolyarov. PVS-Studio et Intégration Continue : TeamCity. Analyse du projet Open RollerCoaster Tycoon 2.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster