Vue d'ensemble du profilage
Un profileur est un outil qui surveille l'exécution d'une autre application. Un profileur CLR (Common Language Runtime) est une bibliothèque de liaisons dynamiques (DLL) qui se compose des fonctions qui reçoivent des messages du CLR et envoient des messages à celui-ci en utilisant l'API de profilage. La DLL de profileur est chargée par le CLR au moment de l'exécution.
Les outils de profilage traditionnels se contentent de mesurer l'exécution de l'application. Autrement dit, ils mesurent le temps consacré à chaque fonction ou l'utilisation de la mémoire de l'application à la longue. L'API de profilage cible une classe plus générale d'outils de diagnostics tels que les utilitaires de couverture du code et même des outils d'aide au débogage avancés. Ces utilisations relèvent du diagnostic par nature. L'API de profilage non seulement mesure mais surveille également l'exécution d'une application. Pour cette raison, l'API de profilage ne doit jamais être utilisée par l'application elle-même, et l'exécution de l'application ne doit pas dépendre du profileur (ni être affecté par celui-ci).
Le profilage d'une application CLR requiert plus d'assistance que le profilage de code machine compilé de façon classique. Cela tient au fait que le CLR introduit des concepts tels que les domaines d'application, le garbage collection, la gestion des exceptions managées, la compilation juste-à-temps (JIT) de code (la conversion de code MSIL en code machine natif), et autres fonctionnalités similaires. Les mécanismes de profilage classiques ne peuvent pas identifier ou fournir des informations utiles à propos de ces fonctionnalités. L'API de profilage fournit efficacement ces informations manquantes, avec un effet minime sur les performances du CLR et l'application profilée.
La compilation JIT au moment de l'exécution fournit de bonnes possibilités pour le profilage. L'API de profilage permet à un profileur de modifier le flux du code MSIL en mémoire pour une routine avant sa compilation par JIT. De cette manière, le profileur peut ajouter dynamiquement le code d'instrumentation à des routines particulières qui demandent un examen plus approfondi. Bien que cette approche soit possible dans les scénarios classiques, il est beaucoup plus facile à implémenter pour le CLR en utilisant l'API de profilage.
Cette vue d'ensemble se compose des sections suivantes :
L'API de profilage
Fonctionnalités prises en charge
Threads de notification
Sécurité
Combinaison de code managé et non managé dans un profileur de code
Profilage de code non managé
Utilisation de COM
Piles des appels
Rappels et profondeur de la pile
Rubriques connexes
L'API de profilage
En général, l'API de profilage est utilisée pour écrire un profileur de code, qui est un programme qui surveille l'exécution d'une application managée.
L'API de profilage est utilisée par une DLL de profileur qui est chargée dans le même processus que l'application qui est profilée. La DLL de profileur implémente une interface de rappel (ICorProfilerCallback dans le .NET Framework version 1.0 et 1.1, ICorProfilerCallback2 dans la version 2.0 et ultérieures). Le CLR appelle les méthodes dans cette interface pour informer le profileur des événements dans le processus profilé. Le profileur peut rappeler dans le runtime en utilisant les méthodes dans les interfaces ICorProfilerInfo et ICorProfilerInfo2 afin d'obtenir des informations à propos de l'état de l'application profilée.
Remarque |
---|
Seule la partie collecte de données de la solution de profileur doit s'exécuter dans le même processus que l'application profilée.L'interface utilisateur et l'analyse de données doivent être exécutées dans un processus séparé. |
L'illustration suivante montre comment la DLL de profileur interagit avec l'application qui est profilée et le CLR.
Profilage de l'architecture
Interfaces de notification
ICorProfilerCallback et ICorProfilerCallback2 peuvent être considérées comme des interfaces de notification. Ces interfaces se composent de méthodes telles que ClassLoadStarted, ClassLoadFinished et JITCompilationStarted. Chaque fois que le CLR charge ou décharge une classe, compile une fonction, et ainsi de suite, il appelle la méthode correspondante dans l'interface ICorProfilerCallback ou ICorProfilerCallback2 du profileur.
Par exemple, un profileur pourrait mesurer la performance du code via deux fonctions de notification : FunctionEnter2 et FunctionLeave2. Il horodate tout simplement chaque notification, cumule les résultats et émet en sortie une liste qui indique quelles fonctions ont consommé le plus de ressources de l'UC ou l'heure (d'après l'horloge murale) pendant l'exécution de l'application.
Interfaces de récupération d'informations
Les autres interfaces principales impliquées dans le profilage sont ICorProfilerInfo et ICorProfilerInfo2. Le profileur appelle ces interfaces selon le cas pour obtenir plus d'informations pouvant faciliter son analyse. Par exemple, toutes les fois que le CLR appelle la fonction FunctionEnter2, il fournit un identificateur de fonction. Le profileur peut obtenir plus d'informations à propos de cette fonction en appelant la méthode ICorProfilerInfo2::GetFunctionInfo2 pour découvrir la classe parente de la fonction, son nom, et ainsi de suite.
Retour au début
Fonctionnalités prises en charge
L'API de profilage fournit des informations relatives à divers événements et actions qui se produisent dans le Common Language Runtime. Vous pouvez utiliser ces informations pour surveiller les mécanismes internes des processus et analyser les performances de votre application .NET Framework.
L'API de profilage récupère des informations à propos des actions et événements suivants qui se produisent dans le CLR :
Événements liés au démarrage et à l'arrêt du CLR.
Événements liés à la création et à l'arrêt de domaines d'application.
Événements liés au chargement et au déchargement des assemblys.
Événements liés au chargement et au déchargement des modules.
Événements liés à la création et la destruction de vtable COM.
Événements liés à la compilation juste-à-temps (JIT) et à la suspension du code (code-pitching).
Événements liés au chargement et au déchargement des classes.
Événements liés à la création et la destruction de threads.
Événements liés à l'entrée et à la sortie de fonctions.
Exceptions.
Transitions entre exécutions de code managé et de code non managé.
Transitions entre contextes d'exécution différents.
Informations à propos des arrêts du runtime.
Informations à propos de l'activité du tas de mémoire d'exécution (runtime) et de garbage collection.
L'API de profilage peut être appelée à partir de tout langage (non managé) compatible COM.
L'API est performante par rapport au processeur et à la consommation de mémoire. Le profilage n'implique pas les modifications apportées à l'application profilée qui sont assez significatives pour générer des résultats trompeurs.
L'API de profilage est utile à la fois pour les profils d'échantillonnage et les profileurs de non-échantillonnage. Un profileur d'échantillonnage inspecte le profil à des battements d'horloge réguliers, par exemple, toutes les cinq millisecondes. Un profileur de non-échantillonnage est informé de façon synchrone d'un événement avec le thread qui provoque l'événement.
Fonctionnalité non prise en charge
L'API de profilage ne prend pas en charge les fonctionnalités suivantes :
Code non managé, qui doit être profilé à l'aide des méthodes Win32 classiques. Toutefois, le profileur CLR inclut des événements de transition pour déterminer les limites entre code managé et code non managé.
Applications auto-modifiables qui modifient leur propre code à des fins telles que la programmation orientée aspect.
Vérification des limites, car l'API de profilage ne fournit pas ces informations. Le CLR prend en charge de façon intrinsèque la vérification des limites de tout le code managé.
Profilage distant, qui n'est pas pris en charge pour les raisons suivantes :
Le profilage distant étend le temps d'exécution. Lorsque vous utilisez les interfaces de profilage, vous devez réduire le temps d'exécution afin que les résultats du profilage ne soient pas excessivement affectés. Ceci est particulièrement vrai lorsque la performance d'exécution est surveillée. Toutefois, le profilage distant n'est pas une limitation lorsque les interfaces de profilage sont utilisées pour surveiller l'utilisation de la mémoire ou pour obtenir des informations d'exécution à propos des frames de pile, objets, etc.
Le profileur de code CLR doit enregistrer une ou plusieurs interfaces de rappel auprès du runtime sur l'ordinateur local sur lequel l'application profilée s'exécute. Cela limite la capacité de créer un profileur de code distant.
Le profilage dans des environnements de production exigeant une haute disponibilité. L'API de profilage a été créée pour prendre en charge les diagnostics de développement. Il n'a pas subi les tests rigoureux requis pour la prise en charge des environnements de production.
Retour au début
Threads de notification
Dans la plupart des cas, le thread qui génère un événement exécute également des notifications. Ces notifications (par exemple, FunctionEnter et FunctionLeave) n'ont pas besoin de fournir le ThreadID explicite. Le profileur peut, également, décider d'utiliser le stockage local des threads (TLS, Thread-Local Storage) pour stocker et mettre à jour ses blocs d'analyse au lieu d'indexer les blocs d'analyse dans le stockage global, selon le ThreadID du thread affecté.
Notez que ces rappels ne sont pas sérialisés. Les utilisateurs doivent protéger leur code en créant des structures de données thread-safe et en verrouillant le code de profileur quand c'est nécessaire afin d'empêcher l'accès parallèle par plusieurs threads. Par conséquent, vous pouvez obtenir une séquence exceptionnelle de rappels dans certains cas. Par exemple, supposons qu'une application managée engendre deux threads qui exécutent un code identique. Dans ce cas, il est possible de recevoir un événement ICorProfilerCallback::JITCompilationStarted pour une fonction d'un thread et un rappel FunctionEnter de l'autre thread avant de recevoir le rappel ICorProfilerCallback::JITCompilationFinished. Dans ce cas, l'utilisateur recevra un rappel FunctionEnter pour une fonction qui n'a pas été peut-être encore complètement compilée juste-à-temps (JIT).
Retour au début
Sécurité
Une DLL de profileur est une DLL non managée qui s'exécute dans le cadre du moteur d'exécution du Common Language Runtime. En conséquence, le code dans la DLL de profileur n'est pas soumis aux restrictions de sécurité d'accès du code managé. Les seules limitations sur la DLL de profileur sont celles imposées par le système d'exploitation sur l'utilisateur qui exécute l'application profilée.
Les auteurs de profileur doivent prendre les précautions appropriées pour éviter des problèmes relatifs à la sécurité. Par exemple, une DLL de profileur doit être ajoutée à une liste de contrôle d'accès (ACL) pendant l'installation, afin qu'un utilisateur malveillant ne puisse pas la modifier.
Retour au début
Combinaison de code managé et non managé dans un profileur de code
Un profileur incorrectement écrit peut générer des références circulaires vers lui-même, provoquant ainsi un comportement imprévisible.
Une révision de l'API de profilage du CLR peut donner l'impression que vous pouvez écrire un profileur contenant des composants managés et non managés qui s'appellent les uns les autres via COM Interop ou des appels indirects.
Bien que ce soit possible sous un angle de conception, l'API de profilage ne prend pas en charge de composants managés. Un profileur de CLR doit être complètement non managé. Les tentatives visant à combiner code managé et code non managé dans un profileur de CLR peuvent provoquer des violations d'accès, des échecs de programmes ou des interblocages. Les composants managés du profileur redéclencheront des événements vers leurs composants non managés, qui appelleraient par la suite les composants managés de nouveau, provoquant ainsi des références circulaires.
Le seul emplacement où un profileur de CLR peut appeler le code managé de manière sécurisée est dans le corps d'une méthode MSIL (Microsoft Intermediate Language). Avant l'achèvement de la compilation juste-à-temps (JIT) d'une fonction, le profileur peut insérer des appels managés dans le corps d'une méthode MSIL, puis la compiler juste-à-temps (consultez la méthode ICorProfilerInfo::GetILFunctionBody). Cette technique peut être utilisée à bon escient pour l'instrumentation sélective de code managé, ou pour collecter des statistiques et des données de performances à propos du JIT.
Un profileur de code peut également insérer des raccordements natifs dans le corps MSIL de chaque fonction managée qui fait appel à un code non managé. Cette technique peut être utilisée pour l'instrumentation et la couverture. Par exemple, un profileur de code pourrait insérer des raccordements d'instrumentation après chaque bloc MSIL afin de garantir l'exécution du bloc. Le changement du corps MSIL d'une méthode est une opération très délicate, et il existe de nombreux facteurs à prendre en considération.
Retour au début
Profilage de code non managé
L'API de profilage du Common Language Runtime (CLR) fournit une prise en charge minimale pour le profilage du code non managé. Les fonctionnalités suivantes sont fournies :
Énumération de chaînes de pile. Cette fonctionnalité permet à un profileur de code de déterminer la limite entre code managé et code non managé.
Détermination si une chaîne de pile correspond au code managé ou au code natif.
Dans le .NET Framework versions 1.0 et 1.1, ces méthodes sont disponibles vis le sous-ensemble in-process de l'API de débogage CLR. Elles sont définies dans le fichier CorDebug.idl et expliquées dans Vue d'ensemble du débogage CLR.
Dans le .NET Framework 2.0 et versions ultérieures, vous pouvez utiliser la méthode ICorProfilerInfo2::DoStackSnapshot pour ces fonctionnalités.
Retour au début
Utilisation de COM
Même si les interfaces de profilage sont définies en tant qu'interfaces COM, le Common Language Runtime (CLR) n'initialise en fait pas COM pour utiliser ces interfaces. Cela permet d'éviter d'avoir à définir le modèle de thread à l'aide de la fonction CoInitialize avant que l'application managée n'ait pu spécifier son modèle de thread. De même, le profileur ne doit lui-même pas appeler CoInitialize, car il risque de choisir un modèle de thread incompatible avec l'application en cours de profilage et d'entraîner l'échec de l'application.
Retour au début
Piles des appels
L'API de profilage propose deux méthodes d'obtention des piles des appels : un instantané de la pile qui active la collecte fragmentée des piles des appels et une pile cachée qui suit la pile des appels à chaque instant.
Instantané de la pile
Un instantané de la pile correspond à une trace de la pile d'un thread à un instant T. L'API de profilage prend en charge le traçage des fonctions managées sur la pile, mais il laisse le traçage des fonctions non managées à l'explorateur de piles du profileur.
Pour plus d'informations sur la programmation du profileur pour explorer des piles managées, consultez la méthode ICorProfilerInfo2::DoStackSnapshot dans cette documentation et Profiler Stack Walking in the .NET Framework 2.0: Basics and Beyond dans MSDN Library.
Pile cachée
En cas d'utilisation trop fréquente de la méthode instantanée, des problèmes de performances risquent rapidement d'apparaître. Pour suivre fréquemment les traces de la pile, utilisez plutôt le profileur pour générer une pile cachée à l'aide de rappels d'exception FunctionEnter2, FunctionLeave2, FunctionTailcall2 et ICorProfilerCallback2. La pile cachée est toujours à jour et peut être rapidement copiée dans le support de stockage chaque fois qu'un instantané de la pile s'avère nécessaire.
Une pile cachée peut obtenir des arguments de fonction, des valeurs de retour et des informations sur les instanciations génériques. Ces informations sont uniquement disponibles via la pile cachée et peuvent être obtenues lorsque le contrôle est transmis à une fonction. Ces informations risquent toutefois de ne plus être disponibles par la suite, lors de l'exécution de la fonction.
Retour au début
Rappels et profondeur de la pile
Les rappels de profileur peuvent être émis dans des circonstances soumises à de fortes contraintes de la pile, et un dépassement de capacité de la pile dans un rappel de profileur conduira à une sortie de processus immédiate. Un profileur doit veiller à utiliser aussi peu de pile que possible à la suite des rappels. Si le profileur est prévu pour une utilisation en présence de processus qui sont robustes face au dépassement de capacité de la pile, le profileur lui-même doit également éviter de déclencher le dépassement de capacité de la pile.
Retour au début
Rubriques connexes
Titre |
Description |
---|---|
Décrit les modifications et améliorations apportées au profilage dans le .NET Framework 2.0 et versions ultérieures. |
|
Explique comment initialiser un profileur, définir des notifications d'événements et profiler un service Windows. |
|
Décrit les rappels qui sont publiés pour charger des domaines d'application, assemblys, modules et classes. |
|
Explique comment le garbage collection est déclenché, détecté et bloqué. |
|
Explique comment les objets qui ont été déplacés pendant le garbage collection peuvent être suivis. |
|
Explique comment un profileur peut utiliser les métadonnées pour obtenir des informations à propos des objets. |
|
Décrit comment un profileur peut surveiller des événements d'exception. |
|
Décrit comment un profileur peut contrôler la génération du code automatique et manuelle. |
|
Présente les ID de classe, de thread et de domaine d'application qui sont passés aux profileurs par le Common Language Runtime. |
|
Décrit les valeurs de retour HRESULT, la façon d'allouer des mémoires tampons de retour pour une utilisation par les API de profilage et l'utilisation des paramètres de sortie optionnels. |
|
Décrit les interfaces non managées que l'API de profilage utilise. |
|
Décrit les fonctions statiques globales non managées qui sont utilisées par l'API de profilage. |
|
Décrit les énumérations non managées que l'API de profilage utilise. |
|
Décrit les structures non managées que l'API de profilage utilise. |
Retour au début