Partager via


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 liens dynamiques (DLL) qui se compose de fonctions qui reçoivent des messages et d’envoyer des messages, au CLR à l’aide de l’API de profilage. La DLL du profileur est chargée par le CLR au moment de l’exécution.

Les outils de profilage traditionnels se concentrent sur la mesure de l’exécution de l’application. Autrement dit, ils mesurent le temps passé dans chaque fonction ou l’utilisation de la mémoire de l’application au fil du temps. L’API de profilage cible une classe plus large d’outils de diagnostic tels que les utilitaires de couverture du code et même les aides de débogage avancées. Ces utilisations sont tous des diagnostics de nature. L’API de profilage mesure non seulement 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 (ou être affectée par) le profileur.

Le profilage d’une application CLR nécessite plus de prise en charge que le profilage du code d’ordinateur compilé conventionnellement. Cela est dû 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) du code (conversion du langage intermédiaire commun ou CIL, code en code natif de l’ordinateur) et des fonctionnalités similaires. Les mécanismes de profilage conventionnels ne peuvent pas identifier ou fournir des informations utiles sur ces fonctionnalités. L’API de profilage fournit ces informations manquantes efficacement, avec un effet minimal sur les performances du CLR et de l’application profilée.

La compilation JIT au moment de l’exécution offre de bonnes opportunités de profilage. L’API de profilage permet à un profileur de modifier le flux de code CIL en mémoire pour une routine avant qu’il soit compilé par JIT. De cette façon, le profileur peut ajouter dynamiquement du code d’instrumentation à des routines particulières nécessitant une investigation plus approfondie. Bien que cette approche soit possible dans les scénarios classiques, il est beaucoup plus facile d’implémenter pour le CLR à l’aide de l’API de profilage.

L’API de profilage

En règle générale, 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 du profileur, qui est chargée dans le même processus que l’application en cours de profilage. La DLL du profileur implémente une interface de rappel (ICorProfilerCallback dans .NET Framework version 1.0 et 1.1, ICorProfilerCallback2 dans la version 2.0 et ultérieure). Le CLR appelle les méthodes de cette interface pour notifier le profileur d’événements dans le processus profilé. Le profileur peut rappeler dans le runtime à l’aide des méthodes des interfaces ICorProfilerInfo et ICorProfilerInfo2 pour obtenir des informations sur l’état de l’application profilée.

Note

Seule la partie de collecte des données de la solution profileur doit s’exécuter dans le même processus que l’application profilée. Toutes les interfaces utilisateur et l’analyse des données doivent être effectuées dans un processus distinct.

L’illustration suivante montre comment la DLL du profileur interagit avec l’application en cours de profilage et le CLR.

Capture d’écran montrant l’architecture de profilage.

Interfaces de notification

ICorProfilerCallback et ICorProfilerCallback2 peuvent être considérés 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 le profileur.

Par exemple, un profileur peut mesurer les performances du code via deux fonctions de notification : FunctionEnter2 et FunctionLeave2. Il horodaille simplement chaque notification, accumule les résultats et génère une liste qui indique quelles fonctions ont consommé le plus de temps processeur ou d’horloge murale pendant l’exécution de l’application.

Interfaces de récupération des informations

Les autres interfaces principales impliquées dans le profilage sont ICorProfilerInfo et ICorProfilerInfo2. Le profileur appelle ces interfaces selon les besoins pour obtenir plus d’informations pour faciliter son analyse. Par exemple, chaque fois que le CLR appelle la fonction FunctionEnter2 , il fournit un identificateur de fonction. Le profileur peut obtenir plus d’informations sur cette fonction en appelant la méthode ICorProfilerInfo2 ::GetFunctionInfo2 pour découvrir la classe parente de la fonction, son nom, etc.

Fonctionnalités prises en charge

L’API de profilage fournit des informations sur divers événements et actions qui se produisent dans le Common Language Runtime. Vous pouvez utiliser ces informations pour surveiller les fonctionnements internes des processus et analyser les performances de votre application .NET Framework.

L’API de profilage récupère des informations sur les actions et événements suivants qui se produisent dans le CLR :

  • Événements de démarrage et d’arrêt CLR.

  • Événements de création et d’arrêt du domaine d’application.

  • Chargement et déchargement d’événements d’assembly.

  • Chargement et déchargement des événements de module.

  • Événements de création et de destruction com vtable.

  • Compilation juste-à-temps (JIT) et événements de pitching de code.

  • Événements de chargement et de déchargement de classes.

  • Événements de création et de destruction de threads.

  • Événements d’entrée et de sortie de fonction.

  • Exceptions.

  • Transitions entre l’exécution du code managé et non managé.

  • Transitions entre différents contextes d’exécution.

  • Informations sur les suspensions d’exécution.

  • Informations sur le tas de mémoire runtime et l’activité de garbage collection.

L’API de profilage peut être appelée à partir de n’importe quel langage compatible COM (non managé).

L’API est efficace en ce qui concerne la consommation du processeur et de la mémoire. Le profilage n’implique pas de modifications apportées à l’application profilée qui sont suffisamment importantes pour provoquer des résultats trompeurs.

L’API de profilage est utile pour les profils d’échantillonnage et de non-échantillonnage. Un profileur d’échantillonnage inspecte le profil à des cycles d’horloge réguliers, par exemple, à 5 millisecondes d’écart. Un profileur sans échantillonnage est informé d’un événement de façon synchrone 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 de méthodes Win32 conventionnelles. Toutefois, le profileur CLR inclut des événements de transition pour déterminer les limites entre le code managé et non managé.

  • Modification automatique d’applications 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 fournit une prise en charge intrinsèque de la vérification des limites de tout le code managé.

  • Le profilage à distance, qui n’est pas pris en charge pour les raisons suivantes :

    • Le profilage à distance é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 affectés de manière indu. Cela est particulièrement vrai lorsque les performances d’exécution sont surveillées. 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 sur les trames de pile, les objets, et ainsi de suite.

    • Le profileur de code CLR doit inscrire une ou plusieurs interfaces de rappel avec le runtime sur l’ordinateur local sur lequel l’application profilée est en cours d’exécution. Cela limite la possibilité de créer un profileur de code distant.

Notification Threads

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 l’élément explicite ThreadID. En outre, le profileur peut décider d’utiliser le stockage local thread pour stocker et mettre à jour ses blocs d’analyse au lieu d’indexer les blocs d’analyse dans le stockage global, en fonction ThreadID du thread concerné.

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 du profileur si nécessaire pour empêcher l’accès parallèle à partir de plusieurs threads. Par conséquent, dans certains cas, vous pouvez recevoir une séquence inhabituelle de rappels. Par exemple, supposons qu’une application managée génère deux threads qui exécutent du code identique. Dans ce cas, il est possible de recevoir un événement ICorProfilerCallback ::JITCompilationStarted pour une fonction d’un thread et un FunctionEnter rappel de l’autre thread avant de recevoir le rappel ICorProfilerCallback ::JITCompilationFinished . Dans ce cas, l’utilisateur reçoit un FunctionEnter rappel pour une fonction qui n’a peut-être pas encore été entièrement compilée juste-à-temps (JIT).

Security

Une DLL de profileur est une DLL non managée qui s’exécute dans le cadre du moteur d’exécution common language runtime. Par conséquent, le code de la DLL du profileur n’est pas soumis aux restrictions de sécurité de l’accès au code managé. Les seules limitations de la DLL du profileur sont celles imposées par le système d’exploitation sur l’utilisateur qui exécute l’application profilée.

Les auteurs de profileurs doivent prendre les précautions appropriées pour éviter les problèmes liés à la sécurité. Par exemple, lors de l’installation, une DLL de profileur doit être ajoutée à une liste de contrôle d’accès (ACL) afin qu’un utilisateur malveillant ne puisse pas le modifier.

Combinaison de code managé et non managé dans un profileur de code

Un profileur écrit incorrectement peut provoquer des références circulaires à lui-même, ce qui entraîne un comportement imprévisible.

Une révision de l’API de profilage CLR peut créer l’impression que vous pouvez écrire un profileur qui contient des composants managés et non managés qui s’appellent mutuellement via l’interopérabilité COM ou les appels indirects.

Bien que cela soit possible du point de vue de la conception, l’API de profilage ne prend pas en charge les composants managés. Un profileur CLR doit être complètement non géré. Les tentatives de combinaison de code managé et non managé dans un profileur CLR peuvent entraîner des violations d’accès, une défaillance du programme ou des blocages. Les composants managés du profileur déclenchent les événements vers leurs composants non managés, qui appellent ensuite à nouveau les composants managés, ce qui entraîne des références circulaires.

Le seul emplacement où un profileur CLR peut appeler du code managé en toute sécurité se trouve dans le corps CIL (Common Intermediate Language) d’une méthode. La pratique recommandée pour modifier le corps CIL consiste à utiliser les méthodes de recompilation JIT dans l’interface ICorProfilerCallback4 .

Il est également possible d’utiliser les anciennes méthodes d’instrumentation pour modifier CIL. Avant la fin de la compilation juste-à-temps (JIT) d’une fonction, le profileur peut insérer des appels managés dans le corps CIL d’une méthode, puis compiler JIT (voir la méthode ICorProfilerInfo ::GetILFunctionBody ). Cette technique peut être utilisée avec succès pour l’instrumentation sélective du code managé, ou pour collecter des statistiques et des données de performances sur le JIT.

Un profileur de code peut également insérer des crochets natifs dans le corps CIL de chaque fonction managée qui appelle du code non managé. Cette technique peut être utilisée pour l’instrumentation et la couverture. Par exemple, un profileur de code peut insérer des crochets d’instrumentation après chaque bloc CIL pour s’assurer que le bloc a été exécuté. La modification du corps CIL d’une méthode est une opération très délicate et il existe de nombreux facteurs à prendre en compte.

Profilage du code non managé

L’API de profilage CLR (Common Language Runtime) fournit une prise en charge minimale du code non managé. Les fonctionnalités suivantes sont fournies :

  • Énumération des chaînes de pile. Cette fonctionnalité permet à un profileur de code de déterminer la limite entre le code managé et le code non managé.

  • Déterminer si une chaîne de pile correspond au code managé ou au code natif.

Dans les versions 1.0 et 1.1 du .NET Framework, ces méthodes sont disponibles via le sous-ensemble in-process de l’API de débogage CLR. Ils sont définis dans le fichier CorDebug.idl.

Dans .NET Framework 2.0 et versions ultérieures, vous pouvez utiliser la méthode ICorProfilerInfo2 ::D oStackSnapshot pour cette fonctionnalité.

Utilisation de COM

Bien que les interfaces de profilage soient définies en tant qu’interfaces COM, le Common Language Runtime (CLR) n’initialise pas réellement COM pour utiliser ces interfaces. La raison est d’éviter de devoir définir le modèle de threading à l’aide de la fonction CoInitialize avant que l’application managée ait eu la possibilité de spécifier son modèle de threading souhaité. De même, le profileur lui-même ne doit pas appeler CoInitialize, car il peut choisir un modèle de thread qui n’est pas compatible avec l’application en cours de profilage et peut entraîner l’échec de l’application.

Piles d’appels

L’API de profilage fournit deux façons d’obtenir des piles d’appels : une méthode d’instantané de pile, qui permet la collecte éparse des piles d’appels et une méthode de pile d’ombres, qui effectue le suivi de la pile des appels à chaque instant.

Instantané de pile

Un instantané de pile est une trace de la pile d’un thread à un instant dans le temps. L’API de profilage prend en charge le suivi des fonctions gérées sur la pile, mais elle laisse le suivi des fonctions non managées au marcheur de pile du profileur.

Pour plus d’informations sur la façon de programmer le profileur pour parcourir les piles managées, consultez la méthode ICorProfilerInfo2 ::D oStackSnapshot dans cet ensemble de documentation et la marche à pied de la pile Profiler dans le .NET Framework 2.0 : Concepts de base et au-delà.

Pile d’ombres

L’utilisation de la méthode d’instantané trop fréquemment peut rapidement créer un problème de performances. Si vous souhaitez fréquemment effectuer des traces de pile, votre profileur doit créer une pile d’ombres à l’aide des rappels d’exception FunctionEnter2, FunctionLeave2, FunctionTailcall2 et ICorProfilerCallback2 . La pile d’ombres est toujours actuelle et peut rapidement être copiée dans le stockage chaque fois qu’un instantané de pile est nécessaire.

Une pile d’ombres peut obtenir des arguments de fonction, des valeurs de retour et des informations sur les instanciations génériques. Ces informations sont disponibles uniquement par le biais de la pile d’ombres et peuvent être obtenues lorsque le contrôle est remis à une fonction. Toutefois, ces informations peuvent ne pas être disponibles ultérieurement pendant l’exécution de la fonction.

Rappels et profondeur de pile

Les rappels de profileur peuvent être émis dans des circonstances très contraintes par pile, et un dépassement de capacité de pile dans un rappel de profileur entraîne une sortie immédiate du processus. Un profileur doit s’assurer d’utiliser aussi peu de pile que possible en réponse aux rappels. Si le profileur est destiné à être utilisé par rapport aux processus robustes par rapport au dépassement de capacité de pile, le profileur lui-même doit également éviter de déclencher un dépassement de capacité de pile.

Titre Descriptif
Configuration d’un environnement de profilage Explique comment initialiser un profileur, définir des notifications d’événements et profiler un service Windows.
Interfaces de profilage Décrit les interfaces non managées que l’API de profilage utilise.
Profilage des fonctions statiques globales Décrit les fonctions statiques globales non managées utilisées par l’API de profilage.
Énumérations de profilage Décrit les énumérations non managées que l’API de profilage utilise.
Structures de profilage Décrit les structures non managées que l’API de profilage utilise.