L'un des scénarios les plus actuels d'utilisation de l'analyseur PVS-Studio est son intégration avec les systèmes CI. Et bien que l'analyse d'un projet PVS-Studio à partir de presque n'importe quel système d'intégration continue puisse être intégrée à quelques commandes seulement, nous continuons à rendre ce processus encore plus pratique. PVS-Studio prend désormais en charge la conversion de la sortie de l'analyseur dans un format pour TeamCity - TeamCity Inspections Type. Voyons voir comment ça fonctionne.
Informations sur le logiciel utilisé
Informations sur le projet à l'étude
Essayons cette fonctionnalité sur un exemple pratique - analysons le projet OpenRCT2.
réglage
Afin de gagner du temps, je vais probablement ignorer le processus d'installation et commencer à partir du moment où le serveur TeamCity est en cours d'exécution sur mon ordinateur. Nous devons accéder à : localhost : {port spécifié lors du processus d'installation} (dans mon cas, localhost : 9090) et saisir les données d'autorisation. Après être entré, nous serons accueillis par :
Cliquez sur le bouton Créer un projet. Ensuite, sélectionnez Manuellement et remplissez les champs.
Après avoir appuyé sur le bouton Création, nous sommes accueillis par une fenêtre avec des paramètres.
Cliquons Créer une configuration de build.
Remplissez les champs et cliquez Création. Nous voyons une fenêtre vous demandant de sélectionner un système de contrôle de version. Puisque les sources sont déjà localisées localement, cliquez sur Skip.
Enfin, nous passons aux paramètres du projet.
Ajoutons des étapes d'assemblage, pour cela cliquez : Étapes de construction -> Ajouter une étape de construction.
Ici, nous choisissons :
- Type de coureur -> Ligne de commande
- Exécuter -> Script personnalisé
Puisque nous effectuerons une analyse lors de la compilation du projet, l'assemblage et l'analyse devraient être une seule étape, alors remplissez le champ Script personnalisé:
Nous examinerons les étapes individuelles plus tard. Il est important que le chargement de l'analyseur, l'assemblage du projet, son analyse, la sortie du rapport et son formatage ne prennent que onze lignes de code.
La dernière chose que nous devons faire est de définir les variables d'environnement, dont j'ai décrit les moyens d'améliorer leur lisibilité. Pour ce faire, passons à autre chose : Paramètres -> Ajouter un nouveau paramètre et ajoutez trois variables :
Il ne vous reste plus qu'à appuyer sur le bouton Courir dans le coin supérieur droit. Pendant que le projet est assemblé et analysé, je vais vous parler du scénario.
Directement script
Tout d’abord, nous devons télécharger la dernière distribution PVS-Studio. Pour cela, nous utilisons le gestionnaire de packages Chocolatey. Pour ceux qui veulent en savoir plus, il existe un
choco install pvs-studio -y
Ensuite, lançons l'utilitaire de suivi de construction de projet CLMonitor.
%CLmon% monitor –-attach
Ensuite, nous construirons le projet en tant que variable d'environnement MSB est le chemin d'accès à la version de MSBuild que je dois construire
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Entrons le login et la clé de licence pour PVS-Studio :
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Une fois la construction terminée, exécutez à nouveau CLMonitor pour générer des fichiers prétraités et une analyse statique :
%CLmon% analyze -l "c:ptest.plog"
Ensuite, nous utiliserons un autre utilitaire de notre distribution. PlogConverter convertit un rapport d'un format standard en un format spécifique à TeamCity. Grâce à cela, nous pourrons le visualiser directement dans la fenêtre de construction.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
La dernière étape consiste à afficher le rapport formaté dans Stdout, où il sera récupéré par l'analyseur TeamCity.
type "C:tempptest.plog_TeamCity.txt"
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"
En attendant, le montage et l'analyse du projet s'est terminé avec succès, on peut aller dans l'onglet Projets et убедиться в этом.
Maintenant cliquons sur Total des inspectionspour accéder à la visualisation du rapport de l'analyseur :
Les avertissements sont regroupés par numéros de règle de diagnostic. Pour naviguer dans le code, vous devez cliquer sur le numéro de ligne avec l'avertissement. En cliquant sur le point d'interrogation dans le coin supérieur droit, vous ouvrirez un nouvel onglet avec de la documentation. Vous pouvez également naviguer dans le code en cliquant sur le numéro de ligne avec l'avertissement de l'analyseur. La navigation depuis un ordinateur distant est possible lors de l'utilisation Racine de l'arbre source marqueur. Toute personne intéressée par ce mode de fonctionnement de l'analyseur peut se familiariser avec la rubrique correspondante
Visualisation des résultats de l'analyseur
Maintenant que nous avons fini de déployer et de configurer la version, examinons quelques avertissements intéressants trouvés dans le projet que nous examinons.
Avertissement N1
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;
}
L'analyseur a remarqué une erreur qui, après l'allocation dynamique de mémoire dans CreateObject, lorsqu'une exception se produit, la mémoire n'est pas effacée et une fuite de mémoire se produit.
Avertissement N2
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 personnes autres qu'un analyseur statique pourraient réussir ce test d'attention. Cet exemple de copier-coller est bon précisément pour cette raison.
Avertissements N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Bien entendu, utiliser une variable du même nom dans la classe de base et dans le descendant n’est pas toujours une erreur. Cependant, la technologie d’héritage elle-même suppose que tous les champs de la classe parent sont présents dans la classe enfant. En déclarant des champs de même nom chez l'héritier, on introduit une confusion.
Avertissement N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Regardons de plus près. Expression imageDirection/8 sera faux si Direction de l'image est compris entre -7 et 7. Deuxième partie : (directionimage / 8) != 3 chèques Direction de l'image pour être en dehors de la plage : de -31 à -24 et de 24 à 31, respectivement. Il me semble assez étrange de vérifier ainsi l'inclusion de nombres dans une certaine plage et, même s'il n'y a pas d'erreur dans ce morceau de code, je recommanderais de réécrire ces conditions pour qu'elles soient plus explicites. Cela rendrait la vie beaucoup plus facile aux personnes qui liraient et maintiendraient ce code.
Avertissement N5
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;
....
}
....
}
Ce fragment de code a très probablement été obtenu par décompilation. Ensuite, à en juger par le commentaire laissé, une partie du code qui ne fonctionnait pas a été supprimée. Cependant, il reste encore quelques opérations ID du curseur, ce qui n’a pas non plus beaucoup de sens.
Avertissement N6
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); // <=
}
....
}
Ce code est assez simple à corriger, il suffit de le vérifier une troisième fois joueur vers un pointeur nul, ou ajoutez-le au corps de l'instruction conditionnelle. Je suggérerais la deuxième option :
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);
}
}
....
}
Avertissement N7
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));
....
}
....
}
Vous pouvez vous débarrasser d'une ligne de code difficile à lire d'un seul coup et résoudre le problème en vérifiant nullptr. Je suggère de changer le code comme suit :
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);
....
}
....
}
Avertissement N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Le code semble assez étrange. Il me semble qu'il y a eu une faute de frappe soit dans la condition, soit lors de la réaffectation de la variable ColumnHeaderPressedCurrentState sens non.
conclusion
Comme nous pouvons le constater, intégrer l'analyseur statique PVS-Studio dans votre projet TeamCity est assez simple. Pour ce faire, il suffit d'écrire un seul petit fichier de configuration. La vérification du code vous permettra d'identifier les problèmes immédiatement après l'assemblage, ce qui contribuera à les éliminer lorsque la complexité et le coût des modifications sont encore faibles.
Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien de traduction : Vladislav Stolyarov.
Source: habr.com