Instruments personnalisés: quand le panneau ne suffit pas

Instruments for Apple's Xcode est un outil pour analyser les performances d'une application iOS. Ils sont utilisés pour collecter et afficher les données nécessaires au débogage du code. L'année dernière, Apple a présenté des instruments personnalisés. C'est l'occasion d'élargir l'ensemble standard d'outils pour le profilage des applications. Lorsque les outils existants ne suffisent pas, vous pouvez en créer vous-même - ils collecteront, analyseront et afficheront les données selon vos besoins.

Une année s'est écoulée et il n'y a presque pas de nouveaux outils publics et d'informations sur leur création sur le réseau. Nous avons donc décidé de rectifier la situation et de partager comment nous avons créé notre propre instrument personnalisé, qui détermine la raison de la faible isolation des tests unitaires. Il est basé sur la technologie des panneaux (nous l'avons écrit dans l'article précédent ) et vous permet de déterminer rapidement et précisément où le test clignote.



Minimum théorique


Pour créer un nouvel outil pour Xcode, vous devez comprendre deux blocs théoriques. Pour ceux qui veulent le découvrir par eux-mêmes, nous donnerons immédiatement les liens nécessaires:


Pour le reste - ci-dessous est un bref résumé des sujets nécessaires.

Sélectionnez d'abord Fichier -> Nouveau -> Projet -> Catégorie macOS -> Package d'instruments. Le projet créé comprend un fichier avec l'extension .instrpkg, dans lequel un nouvel outil est déclaré de manière déclarative au format xml. Familiarisons-nous avec les éléments de balisage:

QuoiAttributsLa description
Schémas de données
schéma d'intervalle, schéma de point, etc.
Décrit la structure de données sous forme de tableau comme les schémas SQL. Les schémas sont utilisés dans d'autres éléments de balisage pour déterminer le type de données à l'entrée et à la sortie du modèle, par exemple, lors de la description d'un mappage (UI).
Importer des schémas de données
schéma d'importation
Importez des schémas prêts à l'emploi. Il vous permet d'utiliser des structures de données définies par Apple.
Modèle d'outil
modeleur
Associe l'outil à un fichier .clp, dans lequel la logique de l'outil est définie, et annonce le schéma de données attendu à l'entrée et à la sortie du modèle.
Description de l'outil
instrument
Décrit le modèle de données et détermine comment les événements seront affichés dans l'interface utilisateur. Le modèle de données est décrit à l'aide des attributs create-table, create-parameter, etc. Les diagrammes d'outils sont définis par des attributs de graphique et le tableau des pièces est défini par une liste, un récit, etc.

Si nous voulons compléter la logique du nouvel outil, alors créez un fichier .clp avec le code CLIPS. Entités linguistiques de base:

  • «Fact» est un certain événement enregistré dans le système à l'aide de la commande assert;
  • «Règle» est un bloc if avec une syntaxe spécifique qui contient une condition sous laquelle un ensemble d'actions est effectué.

Les règles et la séquence qui seront activées sont déterminées par le CLIPS lui-même en fonction des faits entrants, des priorités des règles et du mécanisme de résolution des conflits.

Le langage prend en charge la création de types de données basés sur des primitives, l'utilisation de fonctions et d'opérations arithmétiques et logiques. Et aussi une programmation orientée objet (OOP) à part entière avec la définition de classes, l'envoi de messages, l'héritage multiple.

Considérez la syntaxe de base d'un langage qui vous permet de créer une logique pour des outils personnalisés.

1. Pour créer un fact , utilisez la construction assert :

 CLIPS> (assert (duck)) 

Ainsi, nous obtenons l'entrée duck dans la table de faits, qui peut être consultée à l'aide de la commande facts :

 CLIPS> (facts) 

Pour supprimer le fait, utilisez la commande retract : (retract duck)

2. Pour créer une rule , utilisez la construction defrule :

 CLIPS> (defrule duck) —     duck (animal-is duck)</i> —  animal-is duck     => (assert (sound-is quack))) —     sound-is quack 

3. Pour créer et utiliser des variables, la syntaxe suivante est utilisée (avant le nom de la variable, il y a un signe obligatoire "?"):

 ?<variable-name> 

4. Vous pouvez créer de nouveaux types de données en utilisant:

 CLIPS> (deftemplate prospect (slot name (type STRING) (default ?DERIVE)) (slot assets (type SYMBOL) (default rich)) (slot age (type NUMBER) (default 80))) 

Ainsi, nous avons défini une structure avec le nom du prospect et trois attributs nom, actifs et âge du type correspondant et une valeur par défaut.

5. Les opérations arithmétiques et logiques ont une syntaxe de préfixe. Autrement dit, pour ajouter 2 et 3, vous devez utiliser la construction suivante:

 CLIPS> (+ 2 3) 

Ou pour comparer deux variables x et y:

 CLIPS> (> ?x ?y) 

Exemple pratique


Dans notre projet, nous utilisons la bibliothèque OCMock pour créer des objets stub. Cependant, il existe des situations où un mok vit plus longtemps que le test pour lequel il a été créé et affecte l'isolement des autres tests. En conséquence, cela conduit au «clignotement» (instabilité) des tests unitaires. Afin de suivre la durée de vie des tests et des simulations, nous allons créer notre propre outil. Ce qui suit est un algorithme d'actions.

Étape 1. Création d'un balisage pour les événements de signalisation


Pour détecter les mox problématiques, deux catégories d'événements d'intervalle sont nécessaires: l'heure de création et de destruction du moxa, l'heure de début et l'heure de fin du test. Pour obtenir ces événements, accédez à la bibliothèque OCMock et OCMock les avec signpost dans les méthodes init et stopMocking de la classe stopMocking .





Ensuite, allez au projet à l'étude, faites le balisage dans les tests unitaires, les tearDown setUp et tearDown :



Étape 2. Créez un nouvel outil à partir du modèle de package d'instruments




Tout d'abord, nous déterminons le type de données de l'entrée. Pour ce faire, .instrpkg importons le schéma de signpost dans le signpost . Maintenant, les événements créés par signpost tomberont dans l'outil:



Ensuite, nous déterminons le type de données dans la sortie. Dans cet exemple, nous afficherons des événements simultanés. Chaque événement aura une heure et une description. Pour ce faire, déclarez le schéma:



Étape 3. Nous décrivons la logique de l'outil


Nous créons un fichier séparé avec l'extension .clp , dans lequel nous définissons les règles en utilisant le langage CLIPS. Pour indiquer au nouvel outil dans quel fichier la logique est définie, ajoutez le bloc de modeler :



Dans ce bloc, à l'aide de l'attribut production-system , spécifiez le chemin d'accès relatif au fichier avec la logique. Dans les attributs output et required-input définissons les schémas de données pour l'entrée et la sortie, respectivement.



Étape 4. Nous décrivons les spécificités de la présentation de l'outil (UI)


Dans le fichier .instrpkg , .instrpkg reste à décrire l'outil lui-même, c'est-à-dire à afficher les résultats. Créez une table pour les données dans l'attribut create-table utilisant le schema-ref detected-mocks-narrative précédemment déclaré dans l'attribut schema-ref . Et définissez le type de sortie d'information - narrative (descriptive):



Étape 5. Nous écrivons le code logique


Passons au fichier .clp , dans lequel la logique du système expert est définie. La logique sera la suivante: si l'heure de début du test chevauche l'intervalle de vie du moka, alors nous pensons que ce mok «provient» d'un autre test - qui viole l'isolement du test unitaire actuel. Afin de créer éventuellement un événement avec des informations d'intérêt, vous devez effectuer les étapes suivantes:

1. Définissez les structures mock et unitTest avec des champs - l'heure de l'événement, l'identifiant de l'événement, le nom du test et la classe du mok.



2. Nous définissons les règles qui créeront des faits avec les types mock et unitTest fonction des événements entrants du signpost :



Ces règles peuvent être lues comme suit: si à l'entrée, nous obtenons un fait de type os-signpost avec le subsystem - subsystem , la category , le name et le event-type souhaités, puis créez un nouveau fait avec le type défini ci-dessus (unitTest ou mock) et remplissez-le de valeurs. Il est important de se souvenir ici - CLIPS est un langage sensible à la casse et les valeurs de sous-système, catégorie, nom et type d'événement doivent correspondre à ce qui a été utilisé dans le code du projet à l'étude.



Les variables des événements d'orientation sont transmises comme suit:



3. Nous définissons les règles qui libèrent les événements terminés (ils sont redondants, car ils n'affectent pas le résultat).



Étape 6. Définissez la règle qui générera les résultats.


Vous pouvez lire la règle comme ceci.

Si

1) il y a unitTest et mock;

2) dans ce cas, le début du test se produit plus tard que le moka existant;

3) il existe un tableau pour stocker les résultats avec le schéma narratif simulé détecté;

alors

4) créer un nouvel enregistrement;

5) remplissez avec le temps;

6) ... et une description.



Par conséquent, nous voyons l'image suivante lors de l'utilisation du nouvel outil:



Le code source de l'instrument personnalisé et un exemple de projet d'utilisation de l'instrument peuvent être consultés sur GitHub .

Débogage d'outils


Le débogueur est utilisé pour déboguer des outils personnalisés.



Il permet

1. Voir le code compilé basé sur la description dans instrpkg.
2. Consultez les informations détaillées sur ce qui arrive à l'outil lors de l'exécution.



3. Affichez une liste complète et une description des schémas de données système qui peuvent être utilisés comme entrée dans de nouveaux outils.



4. Exécutez des commandes arbitraires dans la console. Par exemple, affichez une liste de règles avec la commande list-defrules ou des faits avec la commande facts



Configuration sur le serveur CI


Vous pouvez exécuter des outils à partir de la ligne de commande - pour profiler l'application pendant l'exécution de tests unitaires ou d'interface utilisateur sur le serveur CI. Cela permettra, par exemple, de détecter une fuite de mémoire le plus tôt possible. Pour profiler les tests dans le pipeline, utilisez les commandes suivantes:

1. Exécution d'outils avec des attributs:

 xcrun instruments -t <template_name> -l <average_duration_ms> -w <device_udid> 

  • template_name est le chemin d'accès au modèle avec des outils ou le nom du modèle. Vous pouvez obtenir la commande xcrun instruments -s ;
  • average_duration_ms - le temps d'enregistrement en millisecondes, doit être supérieur ou égal au temps d'exécution du test;
  • device_udid - identifiant du simulateur. Vous pouvez obtenir la commande xcrun instruments -s. Doit correspondre à l'identifiant du simulateur sur lequel les tests seront exécutés.

2. Exécution de tests sur le même simulateur avec la commande:

 xcodebuild -workspace <path_to_workspace>-scheme <scheme_with_tests> -destination <device> test-without-building 

  • path_to_workspace est le chemin d'accès à l'espace de travail Xcode;
  • scheme_with_tests - schéma avec tests;
  • device - identifiant du simulateur.

En conséquence, un rapport avec l'extension .trace sera créé dans le répertoire de travail, qui peut être ouvert par l'application Instruments ou en cliquant avec le bouton droit sur le fichier et en sélectionnant Afficher le contenu du package.

Conclusions


Nous avons examiné un exemple de mise à niveau de poteau indicateur vers un outil à part entière et expliqué comment l'appliquer automatiquement sur les «exécutions» du serveur CI et l'utiliser pour résoudre le problème des tests de «clignotement» (instable).

En plongeant dans les possibilités des instruments personnalisés, vous comprendrez mieux dans quels autres cas vous pouvez utiliser les instruments. Par exemple, ils nous aident également à comprendre les problèmes du multithreading - où et quand utiliser l'accès aux données thread-safe.
La création d'un nouvel outil était assez simple. Mais l'essentiel est qu'après avoir passé plusieurs jours à étudier la mécanique et la documentation pour le créer aujourd'hui, vous pourrez éviter plusieurs nuits blanches dans les tentatives de correction de bugs.

Les sources



L'article a été écrit avec @regno , Anton Vlasov, développeur iOS.

Source: https://habr.com/ru/post/fr447594/


All Articles