Les systèmes Linux enfouis suivis visuellement à la
Transcription
Les systèmes Linux enfouis suivis visuellement à la
A P P L I C A T I O N Débogage logiciel Les systèmes Linux enfouis suivis visuellement à la trace… logicielle Dans le cadre du développement de systèmes enfouis, le débogage logiciel s’avère parfois une étape laborieuse, imprévisible et source de nombreux défis. Une fois l’exécution d’un programme en errance détectée, plusieurs questions se posent. Comment l’application a-t-elle pu se retrouver dans cet état ? Par quelle combinaison d’entrées à un instant donné l’erreur a-t-elle vu le jour, et pourquoi ? A ce niveau, une trace logicielle peut fournir une réponse adaptée. L a technologie de traçage d’une application embarquée implique d’enregistrer le comportement du logiciel lors de son exécution, ce qui permet d’analyser ensuite les données de suivi recueillies. Le traçage est le plus souvent utilisé lors de la phase de développement, mais il peut également être activé pour une utilisation en production, de manière à être continuellement actif avec enregistrement du comportement de l’application et capture des erreurs en phase de post-déploiement. Le traçage en production peut s’avérer aussi une technique efficace pour détecter des erreurs qui se manifestent rarement et qui sont donc difficiles à reproduire avec un débogueur classique. Il peut s’agir de situations où le système répond par exemple plus lentement que prévu, donne une sortie incorrecte, se fige ou se bloque complètement. Le traçage peut être réalisé soit au niveau matériel (dans le processeur), soit au niveau logiciel. Le traçage au niveau matériel génère un historique détaillé d’exécution des instructions, sans perturber le système analysé. L’inconvénient est que le traçage matériel nécessite un processeur, une carte et un débogueur prenant explicitement en charge cette fonctionnalité. Ainsi, la prise en charge du traçage matériel doit être envisagée très tôt, dès la sélection de la plate-forme matérielle du projet. En outre, la plupart des solutions de traçage matériel ne permettent pas l’enregistrement de données, mais font uniquement 34 / L’EMBARQUÉ / N°6 / 2014 AUTEUR Dr. Johan Kraft, PDG et fondateur de Percepio, société suédoise créée en 2009 (les outils de Percepio sont distribués en France par Antycip). du contrôle de flux, autrement dit du code exécuté. Le traçage par logiciel, de son côté, se concentre sur des événements sélectionnés, tels que les appels du système d’exploitation, les routines d’interruption ou les mises à jour de variables importantes. Cela ne nécessite pas de matériel particulier et peut même se déployer sur des produits embarqués, un peu comme un enregistreur de vol de type « boîte noire » utilisé dans l’aviation. En outre, le traçage logiciel permet de stocker les données pertinentes avec les événements, tels que les paramètres des appels système. Revers de la médaille, le traçage logiciel impose l’utilisation du processeur et de la mémoire RAM du système cible pour stocker les événements. Cependant, étant donné que le stockage de chaque événement ne prend généralement que quelques microsecondes, un système de traçage logiciel s›exécute généralement à environ 99 % de la vitesse normale. En outre, le temps de processeur supplémentaire utilisé par le traçage est souvent compensé par une meilleure optimisation du logiciel. Un autre problème lié au traçage logiciel est ce que l’on appelle « l’effet de sonde », i.e. l’impact théorique sur le comportement du logiciel en raison de l’impact de synchronisation introduit par le traçage logiciel. Cependant, ces effets de synchronisation sont limités et d’un impact comparable à celui des modifications de base couramment apportées à un logiciel. Il est cependant possible d’éliminer complètement cet effet intrusif en maintenant l’enregistrement actif en permanence, c’est-à-dire, même dans le code de production. De cette façon, l’enregistrement de la trace devient partie intégrante du système intégré testé. Cette approche présente l’avantage que les traces peuvent être automatiquement sauvegardées par le code de gestion d’erreurs, ce qui peut grandement faciliter l’analyse post-mortem. Le traçage est particulièrement important pour les systèmes qui VOIR LES TRACES… n Tracealyzer est une famille d’outils de visualisation de traces développée par Percepio qui fournit un large éventail d’outils graphiques pour faciliter l’analyse de traces logicielles. Rappelons que le traçage constitue un outil puissant pour l’analyse des systèmes logiciels multithreads. Sous Linux, il est activé par LTTng, une solution open source. Conçu pour visualiser les données de trace LTTng au moyen de nombreuses vues graphiques interconnectées, Tracealyzer pour Linux est disponible pour plusieurs systèmes d’exploitation enfouis, y compris Linux, VxWorks, FreeRTOS, SafeRTOS, Micrium µC/OS-III et RTXC Quadros. Tracealyzer pour Linux est conçu pour visualiser les données de trace LTTng et prend en charge la v2.x LTTng actuelle et l’ancienne version de LTTng (par exemple, celle qui figure dans Wind River Linux 5). La visualisation Tracealyzer est développée sous Microsoft. NET. La version Tracealyzer v2.7 fonctionne également sous Linux via Mono (http://mono-project.org), une mise en œuvre alternative en open source de .NET. Débogage logiciel intègrent un système d’exploitation. Un élément central des systèmes d’exploitation est en effet le multithreading qui rend possible l’exécution de plusieurs programmes (threads) sur un seul cœur de processeur en basculant rapidement d’un contexte d’exécution à l’autre. Or, le multithreading est très pratique pour les logiciels enfouis où plusieurs activités périodiques doivent fonctionner à des vitesses différentes, dans un système de contrôle par exemple, ou lorsque des fonctions critiques temps réel doivent être activées sur certains événements, prenant le pas sur d’autres activités moins urgentes. Mais cette technologie de multithreading rend le comportement des logiciels plus complexe, et fait que le développeur contrôle moins bien le comportement lors de l’exécution de l’application, cette dernière étant préemptée par le système d’exploitation. Traçage des systèmes Linux : l’aide de LTTng LTTng (Linux Trace Toolkit Next Generation, http://lttng.org) est actuellement la solution de pointe pour le traçage logiciel sous Linux. LTTng est un programme open source pris en charge par la plupart des distributions Linux et par Yocto, le système de mise au point (build) très couramment employé pour Linux embarqué. Très efficace, LTTng a fait ses preuves à l’utilisation et est compatible avec les noyaux Linux dès la version 2.6.32. Les noyaux à partir de la v2.6.38 sont en outre pris en charge sans aucune modification du noyau. LTTng est basé sur les « tracepoints », espaces réservés aux appels de fonction qui sont inactifs par défaut. Un tracepoint inactif a un impact minime sur les performances, de quelques cycles d’horloge seulement. Lorsque LTTng est activé, il relie les tracepoints à une fonction LTTng interne qui stocke l’événement dans une mémoire Ram tampon. Les données de trace dans la mémoire Ram peuvent être envoyées en permanence vers le disque ou déchargées vers un autre système via une connexion réseau. Le déchargement est géré par des threads qui s’exécutent dans l’espace utilisateur. Une autre possibilité est de conserver les données de trace dans la A P P L I C A T I O N mémoire Ram en utilisant une mémoire tampon circulaire, à savoir, en écrasant les événements antérieurs lorsque la mémoire tampon est pleine. Dans ce mode, un instantané contenant les derniers événements est enregistré à la demande. LTTng fournit deux enregistreurs de trace. Le traceur de noyau enregistre la planification des threads, les appels système, les IRQ, la gestion de la mémoire, ainsi que d’autres activités au niveau du noyau, en utilisant les tracepoints existants dans le noyau Linux. Le traceur de l’espace utilisateur (LTTng-UST) permet de générer des événements personnalisés à partir du code de l’espace utilisateur, c’est-à-dire par l’ajout de nouveaux tracepoints. Bien que LTTng soit basé sur la notion d’instrumentation logicielle, conséquences sur les liaisons dynamiques et impose que l’application appelle la fonction de wrapper au lieu de la fonction d’origine. La fonction de wrapper enregistre l’événement en utilisant le ou les tracepoint(s), puis appelle la fonction d’origine. Ainsi, le wrapping de fonction est complètement transparent pour le code d’application, sans aucun besoin de recompilation. Lors du premier appel d’une fonction wrapper, celle-ci recherche l’adresse de la fonction d’origine et la stocke dans un pointeur de fonction pour utilisation dans les appels ultérieurs. Un outil pour visualiser et analyser les traces LTTng LTTng renvoie les enregistrements de traces dans un format ouvert appelé Common Trace Format. S’agissant 1 TRACE DES APPELS DE FONCTIONS On voit ici la trace des appels de fonctions dans un Linux embarqué à l'aide des fonctions wrapper et LD_PRELOAD. il ne nécessite pas de recompilation du code source pour la cible. Le noyau contient déjà des tracepoints aux endroits stratégiques, et en utilisant une autre caractéristique de Linux, il est possible de tracer les appels de fonctions de l’espace utilisateur sélectionné sans modifier le code source. Cela se fait par la création d’un fichier d’objet partagé avec fonctions d’encapsulation (figure 1) contenant des tracepoints. Le fichier d’objet partagé est ensuite spécifié dans LD_PRELOAD lors du lancement de l’application. Cela a des d’un format binaire, un outil est nécessaire pour l’analyse. L’outil Babeltrace de LTTng peut convertir les données de trace vers des fichiers texte, mais il est difficile d’avoir une vision d’ensemble à partir de grandes quantités de données de trace au format texte. Un outil de visualisation facilite grandement l’analyse puisque le cerveau humain est beaucoup plus doué pour repérer les tendances dans des images que dans des données au format texte. C’est l’objet de l’outil Tracealyzer, développé par Percepio. La vue de trace principale L’EMBARQUÉ / N°6 / 2014 / 35 A P P L I C A T I O N 2 VUE PRINCIPALE DE TRACEALYZER Dans cet écran, on visualise l’exécution de threads et les appels système. dans Tracealyzer montre l’exécution des threads le long d’une ligne de temps verticale, avec différents événements (par exemple, des appels système) présentés au moyen d’étiquettes avec des codes de couleur (figure 2). Les étiquettes peuvent être filtrées de plusieurs manières et leur placement est automatiquement ajusté pour éviter les chevauchements. La couleur de fond des étiquettes indique l’état et le type d’opération. Les étiquettes rouges par exemple montrent les appels système qui bloquent le thread appelant, tandis que les étiquettes vertes indiquent où les appels système bloquants retournent à l’appelant. Des événements d’application personnalisés du traceur de l’espace utilisateur (LTTngUST) peuvent être configurés pour apparaître soit comme des appels de service (par exemple, malloc), soit comme des « événements utilisateur », c’est-à-dire des messages de débogage génériques (étiquettes jaunes). Tracealyzer va dans le même temps au-delà d’un afficheur de base. Il comprend et met en évidence les dépendances entre des événements 36 / L’EMBARQUÉ / N°6 / 2014 connexes dans les données de trace, par exemple en envoyant et en recevant un signal de sémaphore. Ce qui facilite la compréhension du comportement du système d’exploitation, Débogage logiciel et explicite notamment pourquoi certains threads sont bloqués et d’autres déclenchés. Un exemple est montré dans la figure 2, où un appel « en écriture » bloquant est mis en évidence. Cet appel génère deux événements LTTng, lorsque l’appel commence (événement d’entrée) et lorsque l’appel retourne (événement de sortie). Puisque l’appel a bloqué le thread, les deux événements sont séparés par les commutateurs de contexte et d’autres événements. Tracealyzer comprend que ces événements sont liés et met en évidence les deux événements (contour bleu) lorsque l’un est sélectionné. L’événement d’entrée (« write(FD-1) blocks ») indique que le blocage du thread appelant « demo. out: 5133 » a été causé par une opération d’écriture sur FD-1, c’est-à-dire le Descripteur de Fichier 1, qui est la Sortie Standard. Le thread est devenu prêt à s’exécuter presque immédiatement (« Actor Ready: demo.out: 5133 »), mais l’exécution n’a pas repris jusqu’à 69 µs plus tard (« write(FD-1) returns after 69 µs »). La vue principale est prise en charge par plus de 20 autres vues graphiques avec d’autres perspectives de la trace, comme par exemple un graphique d’utilisation du CPU montrant la charge système totale et chaque thread au fil du temps. D’autres vues affichent aussi les sta- 3 DÉPENDANCES DE COMMUNICATION ENTRE LES THREADS Ce flot montre les dépendances de communication entre les threads à travers les objets du noyau. A P P L I C A T I O N Débogage logiciel tistiques de synchronisation des threads, le blocage du noyau, l’intensité de la planification, la communication interprocessus et les dépendances de communication entre les threads (figure 3). Etant donné qu’une trace contient souvent des quantités importantes de scénarios répétitifs présentant un intérêt moindre, les nombreuses vues fournies par Tracealyzer offrent différentes perspectives, différents points de vue, ce qui facilite le repérage de parties intéressantes, par exemple lorsqu’un appel système échoue ou lorsqu’un thread prend plus de temps que la normale pour se terminer. Les événements de l’application sont présentés sous forme d’étiquettes jaunes dans la vue principale (événements utilisateur), mais peuvent également s’afficher dans une fenêtre de journal distincte qui donne un aperçu du comportement général de l’application comme, par exemple, les mises à jour des variables d’état importantes. Si des données numériques sont incluses dans l’enregistrement de l’application (utilisation de la mémoire tampon, signaux de commande ou entrées de capteur, tout élément qui peut être tracé…), Tracealyzer peut être considéré comme un analyseur logique logiciel, utile dans de nombreux types de développement. En outre, les points de données dans les traces sont liés à la vue principale ; par conséquent, un double clic sur n’importe quel point de données permet de synchroniser la vue principale et de lui faire afficher l’événement correspondant. réactif. Si un thread de priorité élevée utilise trop de temps CPU, cela est illustré par le graphique de la charge CPU et par le tracé des temps de réponse (figure 4). En outre, le rapport de statistiques fournit également un aperçu des priorités des threads, de l’utilisation du CPU et des statistiques de synchronisation, qui permet d’étudier et de revoir les priorités des threads en général. Tracealyzer propose aussi plusieurs vues montrant les propriétés de synchronisation des threads dans des tracés chronologiques, où chaque point de données représente une instance (exécution) du thread. L’axe des Y indique une propriété de syn- risquent d’entrer en concurrence fréquemment par rapport à la synchronisation, autrement dit, de commencer en même temps et de se disputer l’utilisation du temps CPU, même si le système dispose par ailleurs de beaucoup de temps processeur inutilisé. Cela provoque des basculements de contexte inutiles et retarde l’achèvement de l’ensemble des threads en concurrence. Ces cas sont des « fruits mûrs » pour l’optimisation, pour lesquels de petits changements de synchronisation peuvent permettre d’améliorer considérablement les performances. Tracealyzer facilite le repérage de ces opportunités, par exemple en inspectant le 4 INDICATION D’UNE CHARGE CPU SUR UNE FRISE CHRONOLOGIQUE Sur cette vue, charge CPU et temps de réponse sont indiqués sur la même frise chronologique. Une approche multivue La plupart des vues sont reliées entre elles d’une manière similaire. La vue principale est liée aux vues qui la prennent en charge, et celles-ci sont liées aux autres vues qui les prennent en charge, ainsi qu’à la vue principale. Une approche qui facilite le basculement entre les différentes perspectives des données de trace lors de l’analyse d’une situation particulière. Certains systèmes Linux enfouis utilisent des priorités de planification fixes (en temps réel) pour les threads critiques temps réel, afin d’éviter les interférences avec des threads moins critiques. La bonne définition des priorités est cependant cruciale pour garantir un fonctionnement fiable et chronisation spécifique telle que le temps d’exécution, le temps de réponse ou la périodicité (temps entre deux activations). Cette dernière est particulièrement utile pour analyser les activités périodiques. Si l’exécution de thread périodique est retardée à un moment donné, cela est révélé par le tracé de la périodicité. Et, tout comme dans d’autres vues similaires, le fait de double-cliquer sur le point de données dans le tracé de périodicité permet de synchroniser la vue de trace principale afin de permettre l’analyse de la cause du retard. Les threads périodiques qui s’exécutent à des fréquences semblables graphique des « interférences des réponses ». Ce dernier montre le temps de réponse normalisé par rapport au temps d’exécution. Par exemple, si un thread prend 300 µs à s’exécuter, mais n’utilise que 100 µs de temps CPU, l’interférence des réponses est de 200 %. Si plusieurs threads ont souvent des pics d’interférence des réponses aux mêmes moments, cela mérite probablement qu’on s’y intéresse d’un peu plus près. Si l’exécution de threads périodiques entrant en collision peut être déplacée, le nombre de collisions peut ainsi être réduit et les performances peuvent s›en trouver augmentées. n L’EMBARQUÉ / N°6 / 2014 / 37