.NET est un runtime géré . Cela signifie qu'il contient des fonctions de haut niveau qui contrôlent votre programme pour vous (de l' introduction au Common Language Runtime (CLR), 2007 ):
Le runtime fournit de nombreuses fonctions, il est donc pratique de les diviser dans les catégories suivantes:
- Les principales fonctions qui affectent l'appareil des autres. Cela comprend:
- collecte des ordures;
- sécuriser l'accès à la mémoire et saisir la sécurité du système;
- prise en charge de haut niveau des langages de programmation.
- Fonctions supplémentaires - fonctionnent sur la base des principales. De nombreux programmes utiles s'en passent. Ces fonctions comprennent:
- Isoler des applications à l'aide d'AppDomains
- protection de l'application et isolation du bac à sable.
- D'autres fonctions sont nécessaires à tous les runtimes, mais elles n'utilisent pas les fonctions CLR de base. Ces fonctionnalités reflètent le désir de créer un environnement de programmation complet. Cela comprend:
- contrôle de version;
- débogage / profilage;
- assurer l'interaction.
On peut voir que bien que le débogage et le profilage ne soient pas les fonctions principales ou supplémentaires, ils sont sur la liste en raison du « désir de créer un environnement de programmation à part entière ».

Le reste de l'article décrit les fonctions de surveillance , d' observabilité et d' introspection qui existent dans le Core CLR, pourquoi elles sont utiles et comment l'environnement les fournit.
Diagnostics
Tout d'abord, jetez un œil aux informations de diagnostic que le CLR nous fournit. Traditionnellement, le suivi des événements pour Windows (ETW) a été utilisé pour cela.
Il existe de nombreux événements sur lesquels le CLR fournit des informations. Ils sont associés à:
- collecte des ordures (GC);
- Compilation JIT;
- modules et domaines d'application;
- travailler avec les threads et les conflits lors du blocage;
- ainsi que de nombreux autres.
Par exemple, ici un événement se produit pendant le chargement dans AppDomain , ici l' événement est associé au lancement d'une exception , et ici au cycle d'allocation de mémoire par le garbage collector .
Vue Perf
Si vous souhaitez voir les événements dans le système de trace (ETW) liés à vos applications .NET, je vous recommande d'utiliser l'excellent outil PerfView et de commencer par ces vidéos de formation ou cette présentation de PerfView: The Ultimate .NET Performance Tool . PerfView a été largement reconnu pour fournir des informations précieuses. Par exemple, les ingénieurs de Microsoft l'utilisent régulièrement pour analyser les performances .
Infrastructure commune
Si soudain le nom n'est pas clair, le suivi des événements dans ETW n'est disponible que sous Windows, ce qui ne correspond pas très bien au monde multiplateforme de .NET Core. Vous pouvez utiliser PerfView pour analyser les performances sous Linux (en utilisant LTTng). Cependant, cet outil de ligne de commande, appelé PerfCollect, collecte uniquement des données. Les capacités d'analyse et une interface utilisateur riche (y compris les graphiques à flamme ) ne sont actuellement disponibles que dans les solutions Windows.
Mais si vous souhaitez toujours analyser les performances .NET sous Linux, il existe d'autres approches:
Le deuxième lien ci-dessus mène à une discussion sur la nouvelle infrastructure EventPipe , qui est en cours d' élaboration dans .NET Core (en plus d'EventSources & EventListeners). Ses objectifs de développement se trouvent dans le document Conception de la surveillance des performances multiplateforme . À un niveau élevé, cette infrastructure créera un endroit unique où le CLR enverra des événements liés aux diagnostics et aux performances. Ces événements seront ensuite redirigés vers un ou plusieurs enregistreurs, qui peuvent, par exemple, inclure ETW, LTTng et BPF. L'enregistreur nécessaire sera déterminé en fonction du système d'exploitation ou de la plate-forme sur lequel le CLR est exécuté. Pour une explication détaillée des avantages et des inconvénients des différentes technologies de journalisation, consultez la conception des performances et des événements .NET Cross-Plat .
La progression d'EventPipes est surveillée via le projet de surveillance des performances et les problèmes liés à «EventPipe» .
Plans futurs
Enfin, il est prévu de créer un contrôleur de profilage des performances , qui aura les tâches suivantes:
Le contrôleur doit gérer l'infrastructure de profilage et présenter les données de performances générées par les composants .NET responsables des diagnostics de performances de manière simple et multiplateforme.
Selon le plan, le contrôleur doit fournir les fonctionnalités suivantes via le serveur HTTP , en recevant toutes les données nécessaires de l'infrastructure EventPipes:
API REST
- Principe 1: profilage simple: profilez le runtime sur la période X et renvoyez la trace.
- Principe 1: profilage avancé: démarrage du suivi (avec configuration)
- Principe 1: profilage avancé: suivi complet (la réponse à cet appel sera la trace elle-même).
- Principe 2: Obtenez des statistiques associées à tous les EventCounters ou à un EventCounter spécifique.
Pages HTML navigables
- Principe 1: Une représentation textuelle de toutes les piles de code managé dans un processus.
- Crée des instantanés des processus en cours d'exécution à utiliser comme rapport de diagnostic simple.
- Principe 2: affichage de l'état actuel (éventuellement avec un historique) des compteurs EventCounters.
- Fournit une vue d'ensemble des compteurs existants et de leurs valeurs.
- PROBLÈME NON RÉSOLU: je ne pense pas qu'il existe des API publiques nécessaires pour compter les EventCounters.
Je veux vraiment voir ce qui se passe avec le contrôleur de profilage des performances (PPC?). Je pense que s'il est intégré au CLR, il apportera de nombreux avantages à .NET. Cette fonctionnalité existe dans d'autres runtimes .
Profilage
Un autre outil efficace dont dispose le CLR est une API de profilage. Il est (principalement) utilisé par des outils tiers pour se connecter au runtime à un niveau bas. Vous pouvez en savoir plus sur l'API à partir de cette revue , mais à un niveau élevé, vous pouvez l'utiliser pour effectuer des rappels qui sont activés si:
- les événements liés au ramasse-miettes;
- des exceptions sont levées;
- les assemblages sont chargés / déchargés;
- et bien plus .
Image de la page de l'API de profilage BOTR - Présentation
De plus, il possède d'autres fonctionnalités efficaces. Tout d'abord, vous pouvez configurer des gestionnaires qui sont appelés à chaque exécution de la méthode .NET, que ce soit dans l'environnement lui-même ou à partir du code utilisateur. Ces rappels sont appelés gestionnaires d'entrée / sortie. Voici un bon exemple de la façon de les utiliser. Cependant, pour cela, vous devez comprendre les conventions d'appel pour différentes architectures de système d'exploitation et de processeur , ce qui n'est pas toujours facile . N'oubliez pas non plus que l'API de profilage est un composant COM accessible uniquement à partir du code C / C ++, mais pas à partir de C # / F # / VB.NET.
Deuxièmement, le profileur peut réécrire le code IL de n'importe quelle méthode .NET avant la compilation JIT à l'aide de l' API SetILFunctionBody () . Cette API est vraiment efficace. Il sous-tend de nombreux outils APM .NET . Vous pouvez en savoir plus sur son utilisation dans mon article Comment se moquer des classes scellées et des méthodes statiques et du code associé.
API ICorProfiler
Il s'avère que l'API de profilage a fonctionné, il devrait y avoir toutes sortes de trucs dans l'environnement d'exécution. Il suffit de regarder la discussion sur la page Autoriser le rejet sur pièce jointe (voir ReJIT: Un guide pratique pour plus d'informations sur ReJIT).
Une définition complète de toutes les interfaces et rappels de l'API de profilage se trouve dans \ vm \ inc \ corprof.idl (voir Langage de description d'interface ). Il est divisé en 2 parties logiques. Une partie est l'interface Profiler -> Runtime Environment (EE) , connue sous le nom de ICorProfilerInfo
:
// , ICorProfilerInfo*, // . , DLL // , // .
Ceci est implémenté dans les fichiers suivants:
L'autre partie principale est les callbacks Runtime -> Profiler, qui sont regroupés sous l'interface ICorProfilerCallback
:
// // ICorProfilerCallaback* . // , EEToProfInterfaceImpl.
Ces rappels sont implémentés dans les fichiers suivants:
Enfin, il convient de noter que les API de profilage peuvent ne pas fonctionner sur tous les systèmes d'exploitation et architectures exécutant .NET Core. Voici un exemple: problèmes de talon d'appels ELT sous Linux . Voir le statut des API CoreCLR Profiler pour plus d'informations.
Profilage v. Débogage
Comme petite digression, je dois dire que le profilage et le débogage se chevauchent encore un peu. Par conséquent, il est utile de comprendre ce que différentes API fournissent dans le contexte du runtime .NET (extrait du débogage CLR par rapport au profilage CLR ).
La différence entre le débogage et le profilage dans le CLR
Débogage
Les développeurs comprennent différemment ce qu'est le débogage. Par exemple, j'ai demandé sur Twitter «comment déboguer des programmes .NET» et j'ai obtenu beaucoup de réponses différentes . En même temps, les réponses contenaient une bonne liste d'outils et de méthodes, je recommande donc de les consulter. Merci #LazyWeb
Je pense que le meilleur de l'essence du débogage reflète ce message:
Le CLR fournit une liste complète des fonctionnalités liées au débogage. Mais pourquoi ces fonds sont-ils nécessaires? Au moins trois raisons sont mentionnées dans cet excellent article Pourquoi le débogage géré est-il différent du débogage natif? :
- Le débogage du code non managé peut être abstrait au niveau matériel, mais le débogage du code managé doit être abstrait au niveau du code IL.
- Le débogage du code managé nécessite un grand nombre d'informations qui ne sont pas disponibles avant l'exécution.
- Le débogueur de code managé doit se coordonner avec le garbage collector (GC)
Par conséquent, pour plus de facilité d'utilisation, le CLR doit fournir une API de débogage de haut niveau appelée ICorDebug
. Il est illustré dans la figure ci-dessous montrant un scénario de débogage général (source: BOTR):
API ICorDebug
Le principe d'implémentation et la description des différents composants sont extraits du débogage CLR, une brève introduction :
Toute la prise en charge du débogage dans .Net est implémentée au-dessus de la bibliothèque de DLL, que nous appelons The Dac. Ce fichier (généralement appelé mscordacwks.dll
) est un élément structurel à la fois pour notre API de débogage publique ( ICorDebug
) et deux API de débogage privées: l'API SOS-Dac et IXCLR.
Dans un monde idéal, tout le monde utiliserait ICorDebug
, notre API publique. Cependant, ICorDebug
manque de nombreuses fonctionnalités dont les développeurs d'outils ont besoin. C'est le problème que nous essayons de résoudre là où nous le pouvons. Toutefois, ces améliorations sont présentes uniquement dans le CL.v.next, mais pas dans les versions antérieures du CLR. En fait, la prise en charge du débogage de vidage sur ICorDebug
est apparue dans l'API ICorDebug
uniquement avec la sortie de CLR v4. Quiconque utilise des ICorDebug
sur ICorDebug
pour le débogage dans CLR v2 ne pourra pas du tout appliquer ICorDebug
.
(Voir SOS et ICorDebug pour plus d'informations)
En fait, l'API ICorDebug
est divisée en plus de 70 interfaces. Je ne les donnerai pas tous, mais je montrerai par quelles catégories ils peuvent être divisés. Pour plus d'informations, consultez la partition de ICorDebug, où cette liste a été publiée.
- Niveau supérieur : ICorDebug + ICorDebug2 - interfaces de niveau supérieur qui servent parfaitement de collection d'objets ICorDebugProcess.
- Rappels : les événements de débogage de code managé sont envoyés via des méthodes à l'objet de rappel implémenté par le débogueur.
- Processus : cet ensemble d'interfaces représente le code de travail et inclut des API liées aux événements.
- Inspection de code / type : fonctionne principalement avec des images PE statiques, mais il existe des méthodes pratiques pour les données réelles.
- Contrôle d'exécution : possibilité de suivre la progression du thread. En pratique, cela signifie la possibilité de définir des points d'arrêt (F9) et de parcourir le code (entrée de code F11, contournement de code F10, sortie de code S + F11). La fonction de contrôle d'exécution ICorDebug ne fonctionne que dans le code managé.
- Threads + piles d'appels : les piles d'appels sont la base des fonctions d'inspection implémentées par le débogueur. Le travail avec la pile d'appels s'effectue à l'aide des interfaces suivantes. ICorDebug prend uniquement en charge le débogage du code managé et, par conséquent, vous pouvez suivre la pile de code managé uniquement.
- Inspection d'objets : l' inspection d'objets fait partie de l'API qui vous permet de voir les valeurs des variables dans le code débogué. Pour chaque interface, je donne la méthode MVP, qui, il me semble, devrait décrire brièvement le but de cette interface.
Comme pour les API de profilage, les niveaux de prise en charge des API de débogage varient selon le système d'exploitation et l'architecture du processeur. Par exemple, en août 2018, il n'y avait toujours pas de solution Linux ARM pour diagnostiquer et déboguer le code managé. Pour plus d'informations sur la prise en charge de Linux, consultez Débogage de .NET Core sous Linux avec LLDB et le référentiel de diagnostics de Microsoft, qui vise à faciliter le débogage des programmes .NET sous Linux.
Enfin, si vous voulez voir à quoi ressemble l'API ICorDebug
en C #, jetez un œil aux wrappers de la bibliothèque CLRMD, y compris tous les rappels disponibles (plus d'informations sur CLRMD seront discutées plus loin dans cet article).
SOS et DAC
Le composant d'accès aux données (DAC) est décrit en détail sur la page BOTR . Essentiellement, il fournit un accès hors processus aux structures de données CLR afin que les informations qu'elles contiennent puissent être lues à partir d'un autre processus. Ainsi, le débogueur (via ICorDebug
) ou l' extension 'Son of Strike' (SOS) peut accéder à l'instance CLR ou au vidage de mémoire en cours d'exécution et trouver, par exemple:
- tous les threads en cours d'exécution;
- objets de tas gérés
- des informations complètes sur la méthode, y compris le code machine;
- trace de pile actuelle.
Une petite digression : si vous voulez savoir d'où viennent ces noms étranges et obtenir une petite leçon sur l'histoire .NET, consultez cette réponse sur Stack Overflow .
La liste complète des commandes SOS est impressionnante . Si vous l'utilisez avec WinDBG, vous pouvez découvrir ce qui se passe à l'intérieur de votre programme et du CLR à un niveau très bas. Pour voir comment tout est implémenté, regardons la !HeapStat
, qui affiche une description des tailles des différents tas que le GC .NET utilise:
(Image extraite de SOS: la prochaine version contient quelques nouvelles commandes - HeapStat)
Voici un flux de code qui montre comment SOS et DAC fonctionnent ensemble:
- Équipe complète SOS
!HeapStat
( lien ) - Code SOS dans la
!HeapStat
qui fonctionne avec Workstation GC (lien) - Fonction SOS
GCHeapUsageStats(..)
, qui effectue la partie la plus difficile du travail ( lien ) DacpGcHeapDetails
données DacpGcHeapDetails
partagée qui contient des pointeurs vers les données principales du tas GC, tels que des segments, des masques de bits et des générations individuelles ( référence ).GetGCHeapStaticData
DAC GetGCHeapStaticData
qui remplit la structure DacpGcHeapDetails
( lien )DacpHeapSegmentData
données DacpHeapSegmentData
partagée qui contient des informations sur un segment de segment GC individuel ( lien )- DAC
GetHeapSegmentData(..)
, qui remplit la structure DacpHeapSegmentData
( lien )
Débogueurs tiers
Depuis que Microsoft a publié l'API de débogage, les développeurs tiers ont pu utiliser les interfaces ICorDebug
. Voici une liste de ceux que j'ai réussi à trouver:
Vidages de mémoire
La dernière chose dont nous parlerons est les vidages de mémoire, qui peuvent être obtenus à partir d'un système de travail et analysés à l'extérieur. Le runtime .NET a toujours pris en charge le vidage de la mémoire sous Windows . Et maintenant que .NET Core est devenu multiplateforme, des outils sont apparus qui effectuent la même tâche sur d'autres systèmes d'exploitation.
Lors de l'utilisation de vidages de mémoire, il est parfois difficile d'obtenir les versions correctes et correspondantes des fichiers SOS et DAC. Heureusement, Microsoft a récemment publié l'outil CLI dotnet symbol
, qui:
peut télécharger tous les fichiers nécessaires au débogage (jeux de caractères, modules, fichiers SOS et DAC pour un module de coreclr spécifique) pour tout vidage de mémoire spécifique, minidump ou fichiers de toute plate-forme prise en charge, y compris ELF, MachO, DLL Windows, PDB et portable PDB
Enfin, si vous faites un peu d'analyse des vidages de mémoire, je vous recommande de jeter un œil à l'excellente bibliothèque CLR MD que Microsoft a publiée il y a plusieurs années. J'ai déjà écrit sur ses fonctions. En bref, en utilisant la bibliothèque, vous pouvez travailler avec des vidages de mémoire via une API C # intuitive contenant des classes qui donnent accès à ClrHeap, GC Roots, CLR Threads, Stack Frames et bien plus encore. En fait, le CLR MD peut implémenter la plupart (sinon la totalité) des commandes SOS.
Vous pouvez découvrir comment cela fonctionne à partir de ce post :
La bibliothèque gérée ClrMD est un wrapper autour des API de débogage destinées à un usage interne uniquement dans le CLR. Malgré le fait que ces API soient très efficaces pour les diagnostics, nous ne les prenons pas en charge sous la forme de versions publiques et documentées, car leur utilisation est complexe et étroitement liée à d'autres fonctionnalités de l'implémentation CLR. ClrMD résout ce problème en fournissant un wrapper facile à utiliser et gérable autour de ces API de débogage de bas niveau.
